| Name |
| |
| EXT_platform_xcb |
| |
| Name Strings |
| |
| EGL_EXT_platform_xcb |
| |
| Contributors |
| |
| Yuxuan Shui <yshuiv7@gmail.com> |
| |
| Contacts |
| |
| Yuxuan Shui <yshuiv7@gmail.com> |
| |
| Status |
| |
| Complete |
| |
| Version |
| |
| Version 1, 2020-08-28 |
| |
| Number |
| |
| EGL Extension #141 |
| |
| Extension Type |
| |
| EGL client extension |
| |
| Dependencies |
| |
| Requires EGL_EXT_client_extensions to query its existence without |
| a display. |
| |
| Requires EGL_EXT_platform_base. |
| |
| This extension is written against the wording of version 9 of the |
| EGL_EXT_platform_base specification. |
| |
| Overview |
| |
| This extension defines how to create EGL resources from native X11 |
| resources using the functions defined by EGL_EXT_platform_base. |
| |
| The native X11 resources required by this extension are xcb resources. |
| All X11 types discussed here are defined by the header `xcb.h`. |
| |
| New Types |
| |
| None |
| |
| New Procedures and Functions |
| |
| None |
| |
| New Tokens |
| |
| Accepted as the <platform> argument of eglGetPlatformDisplayEXT: |
| |
| EGL_PLATFORM_XCB_EXT 0x31DC |
| |
| Accepted as an attribute name in the <attrib_list> argument of |
| eglGetPlatformDisplayEXT: |
| |
| EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE |
| |
| Additions to the EGL Specification |
| |
| None. |
| |
| New Behavior |
| |
| To determine if the EGL implementation supports this extension, clients |
| should query the EGL_EXTENSIONS string of EGL_NO_DISPLAY. |
| |
| This extension defines the same set of behaviors as EGL_EXT_platform_x11, |
| except Xlib types are replaced with xcb types. |
| |
| To obtain an EGLDisplay backed by an X11 screen, call |
| eglGetPlatformDisplayEXT with <platform> set to EGL_PLATFORM_XCB_EXT. The |
| <native_display> parameter specifies the X11 display connection to use, and |
| must point to a valid xcb `xcb_connection_t` or be EGL_DEFAULT_DISPLAY. If |
| <native_display> is EGL_DEFAULT_DISPLAY, then EGL will create [1] a |
| connection to the default X11 display. The environment variable DISPLAY |
| determines the default X11 display, and, unless overridden by the |
| EGL_PLATFORM_XCB_SCREEN_EXT attribute, the default X11 screen - as |
| described in the documentation of `xcb_connect`. If the environment |
| variable DISPLAY is not present in this case, the result is undefined. The |
| value of attribute EGL_PLATFORM_XCB_SCREEN_EXT specifies the X11 screen to |
| use. If the attribute is omitted from <attrib_list>, and <native_display> |
| is not EGL_DEFAULT_DISPLAY, then screen 0 will be used. Otherwise, the |
| attribute's value must be a valid screen on the display connection. If the |
| attribute's value is not a valid screen, then an EGL_BAD_ATTRIBUTE error is |
| generated. |
| |
| [fn1] The method by which EGL creates a connection to the default X11 |
| display is an internal implementation detail. The implementation may use |
| xcb_connect, or any other method. |
| |
| To obtain an on-screen rendering surface from an X11 Window, call |
| eglCreatePlatformWindowSurfaceEXT with a <dpy> that belongs to X11 and |
| a <native_window> that points to an xcb_window_t. |
| |
| To obtain an offscreen rendering surface from an X11 Pixmap, call |
| eglCreatePlatformPixmapSurfaceEXT with a <dpy> that belongs to X11 and |
| a <native_pixmap> that points to an xcb_pixmap_t. |
| |
| Issues |
| |
| 1. As xcb_connection_t doesn't carry a screen number, how should a screen be |
| selected in eglGetPlatformDisplayEXT()? |
| |
| RESOLVED. The screen will be chosen with the following logic: |
| |
| * If EGL_PLATFORM_XCB_SCREEN_EXT is specified, it will always take |
| precedence. Whether <native_display> is EGL_DEFAULT_DISPLAY or not. |
| |
| * Otherwise, if <native_display> is not EGL_DEFAULT_DISPLAY, then |
| screen 0 will be used. |
| |
| * Otherwise, which is to say <native_display> is EGL_DEFAULT_DISPLAY. |
| Then the DISPLAY environment variable will be used to determine the |
| screen number. If DISPLAY contains a screen number, that will be |
| used; if not, then 0 will be used. |
| |
| * If the DISPLAY environment variable is not present when |
| <native_display> is EGL_DEFAULT_DISPLAY, the result will be undefined. |
| |
| Example Code |
| |
| // This example program creates two EGL surfaces: one from an X11 Window |
| // and the other from an X11 Pixmap. |
| // |
| // Compile with `cc example.c -lxcb -lEGL`. |
| |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <xcb/xcb.h> |
| |
| struct my_display { |
| xcb_connection_t *x11; |
| int screen; |
| int root_of_screen; |
| EGLDisplay egl; |
| }; |
| |
| struct my_config { |
| struct my_display dpy; |
| xcb_colormap_t colormap; |
| xcb_visualid_t visualid; |
| int depth; |
| EGLConfig egl; |
| }; |
| |
| struct my_window { |
| struct my_config config; |
| xcb_window_t x11; |
| EGLSurface egl; |
| }; |
| |
| struct my_pixmap { |
| struct my_config config; |
| xcb_pixmap_t x11; |
| EGLSurface egl; |
| }; |
| |
| static void check_extensions(void) { |
| const char *client_extensions = |
| eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); |
| |
| if (!client_extensions) { |
| // EGL_EXT_client_extensions is unsupported. |
| abort(); |
| } |
| if (!strstr(client_extensions, "EGL_EXT_platform_xcb")) { |
| abort(); |
| } |
| } |
| |
| xcb_screen_t *get_screen(xcb_connection_t *c, int screen) { |
| xcb_screen_iterator_t iter; |
| |
| iter = xcb_setup_roots_iterator(xcb_get_setup(c)); |
| for (; iter.rem; --screen, xcb_screen_next(&iter)) |
| if (screen == 0) |
| return iter.data; |
| |
| return NULL; |
| } |
| |
| int get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) { |
| const xcb_setup_t *setup = xcb_get_setup(c); |
| for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup); i.rem; |
| xcb_screen_next(&i)) { |
| for (xcb_depth_iterator_t j = |
| xcb_screen_allowed_depths_iterator(i.data); |
| j.rem; xcb_depth_next(&j)) { |
| const int len = xcb_depth_visuals_length(j.data); |
| const xcb_visualtype_t *visuals = xcb_depth_visuals(j.data); |
| for (int k = 0; k < len; k++) { |
| if (visual == visuals[k].visual_id) { |
| return j.data->depth; |
| } |
| } |
| } |
| } |
| abort(); |
| } |
| |
| static struct my_display get_display(void) { |
| struct my_display dpy; |
| |
| dpy.x11 = xcb_connect(NULL, &dpy.screen); |
| if (!dpy.x11) { |
| abort(); |
| } |
| |
| dpy.egl = eglGetPlatformDisplayEXT(EGL_PLATFORM_XCB_EXT, dpy.x11, |
| (const EGLint[]){ |
| EGL_PLATFORM_XCB_SCREEN_EXT, |
| dpy.screen, |
| EGL_NONE, |
| }); |
| |
| if (dpy.egl == EGL_NO_DISPLAY) { |
| abort(); |
| } |
| |
| EGLint major, minor; |
| if (!eglInitialize(dpy.egl, &major, &minor)) { |
| abort(); |
| } |
| |
| xcb_screen_t *screen = get_screen(dpy.x11, dpy.screen); |
| dpy.root_of_screen = screen->root; |
| |
| return dpy; |
| } |
| |
| static struct my_config get_config(struct my_display dpy) { |
| struct my_config config = { |
| .dpy = dpy, |
| }; |
| |
| EGLint egl_config_attribs[] = { |
| EGL_BUFFER_SIZE, |
| 32, |
| EGL_RED_SIZE, |
| 8, |
| EGL_GREEN_SIZE, |
| 8, |
| EGL_BLUE_SIZE, |
| 8, |
| EGL_ALPHA_SIZE, |
| 8, |
| |
| EGL_DEPTH_SIZE, |
| EGL_DONT_CARE, |
| EGL_STENCIL_SIZE, |
| EGL_DONT_CARE, |
| |
| EGL_RENDERABLE_TYPE, |
| EGL_OPENGL_ES2_BIT, |
| EGL_SURFACE_TYPE, |
| EGL_WINDOW_BIT | EGL_PIXMAP_BIT, |
| EGL_NONE, |
| }; |
| |
| EGLint num_configs; |
| if (!eglChooseConfig(dpy.egl, egl_config_attribs, &config.egl, 1, |
| &num_configs)) { |
| abort(); |
| } |
| if (num_configs == 0) { |
| abort(); |
| } |
| |
| if (!eglGetConfigAttrib(dpy.egl, config.egl, EGL_NATIVE_VISUAL_ID, |
| (EGLint *)&config.visualid)) { |
| abort(); |
| } |
| |
| config.colormap = xcb_generate_id(dpy.x11); |
| if (xcb_request_check(dpy.x11, |
| xcb_create_colormap_checked( |
| dpy.x11, XCB_COLORMAP_ALLOC_NONE, config.colormap, |
| dpy.root_of_screen, config.visualid))) { |
| abort(); |
| } |
| |
| config.depth = get_visual_depth(dpy.x11, config.visualid); |
| |
| return config; |
| } |
| |
| static struct my_window get_window(struct my_config config) { |
| xcb_generic_error_t *e; |
| |
| struct my_window window = { |
| .config = config, |
| }; |
| |
| window.x11 = xcb_generate_id(config.dpy.x11); |
| e = xcb_request_check( |
| config.dpy.x11, |
| xcb_create_window_checked(config.dpy.x11, // connection |
| XCB_COPY_FROM_PARENT, // depth |
| window.x11, // window id |
| config.dpy.root_of_screen, // root |
| 0, 0, // x, y |
| 256, 256, // width, height |
| 0, // border_width |
| XCB_WINDOW_CLASS_INPUT_OUTPUT, // class |
| config.visualid, // visual |
| XCB_CW_COLORMAP, // mask |
| (const int[]){ |
| config.colormap, |
| XCB_NONE, |
| })); |
| if (e) { |
| abort(); |
| } |
| |
| window.egl = eglCreatePlatformWindowSurfaceEXT(config.dpy.egl, config.egl, |
| &window.x11, NULL); |
| |
| if (window.egl == EGL_NO_SURFACE) { |
| abort(); |
| } |
| |
| return window; |
| } |
| |
| static struct my_pixmap get_pixmap(struct my_config config) { |
| struct my_pixmap pixmap = { |
| .config = config, |
| }; |
| |
| pixmap.x11 = xcb_generate_id(config.dpy.x11); |
| if (xcb_request_check( |
| config.dpy.x11, |
| xcb_create_pixmap(config.dpy.x11, config.depth, pixmap.x11, |
| config.dpy.root_of_screen, 256, 256))) { |
| abort(); |
| } |
| |
| pixmap.egl = eglCreatePlatformPixmapSurfaceEXT(config.dpy.egl, config.egl, |
| &pixmap.x11, NULL); |
| |
| if (pixmap.egl == EGL_NO_SURFACE) { |
| abort(); |
| } |
| |
| return pixmap; |
| } |
| |
| int main(void) { |
| check_extensions(); |
| |
| struct my_display dpy = get_display(); |
| struct my_config config = get_config(dpy); |
| struct my_window window = get_window(config); |
| struct my_pixmap pixmap = get_pixmap(config); |
| |
| return 0; |
| } |
| |
| Revision History |
| |
| Version 2, 2020.10.13 (Yuxuan Shui) |
| - Some wording changes |
| - Address the question about screen selection |
| |
| Version 1, 2020.08.28 (Yuxuan Shui) |
| - First draft |