Name EXT_platform_xcb Name Strings EGL_EXT_platform_xcb Contributors Yuxuan Shui Contacts Yuxuan Shui 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 argument of eglGetPlatformDisplayEXT: EGL_PLATFORM_XCB_EXT 0x31DC Accepted as an attribute name in the 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 set to EGL_PLATFORM_XCB_EXT. The parameter specifies the X11 display connection to use, and must point to a valid xcb `xcb_connection_t` or be EGL_DEFAULT_DISPLAY. If 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 , and 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 that belongs to X11 and a that points to an xcb_window_t. To obtain an offscreen rendering surface from an X11 Pixmap, call eglCreatePlatformPixmapSurfaceEXT with a that belongs to X11 and a 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 is EGL_DEFAULT_DISPLAY or not. * Otherwise, if is not EGL_DEFAULT_DISPLAY, then screen 0 will be used. * Otherwise, which is to say 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 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 #include #include #include #include #include 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