extensions: Add EGL_EXT_platform_xcb. (#112)

* extensions: Add EGL_EXT_platform_xcb.

Co-authored-by: Yuxuan Shui <yshuiv7@gmail.com>

* Mark EXT_platform_xcb complete

* EGL_EXT_platform_xcb: clarify screen selection.
diff --git a/api/EGL/eglext.h b/api/EGL/eglext.h
index adfa14d..c8df7f1 100644
--- a/api/EGL/eglext.h
+++ b/api/EGL/eglext.h
@@ -869,6 +869,12 @@
 #define EGL_PLATFORM_X11_SCREEN_EXT       0x31D6
 #endif /* EGL_EXT_platform_x11 */
 
+#ifndef EGL_EXT_platform_xcb
+#define EGL_EXT_platform_xcb 1
+#define EGL_PLATFORM_XCB_EXT              0x31DC
+#define EGL_PLATFORM_XCB_SCREEN_EXT       0x31DE
+#endif /* EGL_EXT_platform_xcb */
+
 #ifndef EGL_EXT_protected_content
 #define EGL_EXT_protected_content 1
 #define EGL_PROTECTED_CONTENT_EXT         0x32C0
diff --git a/api/egl.xml b/api/egl.xml
index 8b05c79..4d27853 100644
--- a/api/egl.xml
+++ b/api/egl.xml
@@ -572,9 +572,10 @@
         <enum value="0x31D7" name="EGL_PLATFORM_GBM_MESA" alias="EGL_PLATFORM_GBM_KHR"/>
         <enum value="0x31D8" name="EGL_PLATFORM_WAYLAND_KHR"/>
         <enum value="0x31D8" name="EGL_PLATFORM_WAYLAND_EXT" alias="EGL_PLATFORM_WAYLAND_KHR"/>
-            <unused start="0x31DC" end="0x31DC"/>
+        <enum value="0x31DC" name="EGL_PLATFORM_XCB_EXT"/>
         <enum value="0x31DD" name="EGL_PLATFORM_SURFACELESS_MESA"/>
-            <unused start="0x31DE" end="0x31DF"/>
+        <enum value="0x31DE" name="EGL_PLATFORM_XCB_SCREEN_EXT"/>
+            <unused start="0x31DF" end="0x31DF"/>
     </enums>
 
     <!-- Due to an oversight in development, these enums alias the above MESA
@@ -2563,6 +2564,12 @@
                 <enum name="EGL_PLATFORM_X11_SCREEN_EXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_platform_xcb" supported="egl">
+            <require>
+                <enum name="EGL_PLATFORM_XCB_EXT"/>
+                <enum name="EGL_PLATFORM_XCB_SCREEN_EXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_protected_content" supported="egl">
             <require>
                 <enum name="EGL_PROTECTED_CONTENT_EXT"/>
diff --git a/extensions/EXT/EGL_EXT_platform_xcb.txt b/extensions/EXT/EGL_EXT_platform_xcb.txt
new file mode 100644
index 0000000..1411db6
--- /dev/null
+++ b/extensions/EXT/EGL_EXT_platform_xcb.txt
@@ -0,0 +1,382 @@
+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
diff --git a/index.php b/index.php
index 4a7c49f..adb4136 100644
--- a/index.php
+++ b/index.php
@@ -347,6 +347,8 @@
 </li>
 <li value=140> <a href="extensions/EXT/EGL_EXT_device_query_name.txt">EGL_EXT_device_query_name</a>
 </li>
+<li value=141> <a href="extensions/EXT/EGL_EXT_platform_xcb.txt">EGL_EXT_platform_xcb</a>
+</li>
 </ol>
 
 <h6> Providing Feedback on the Registry </h6>
diff --git a/registry.tcl b/registry.tcl
index c14199a..bd9c052 100644
--- a/registry.tcl
+++ b/registry.tcl
@@ -727,4 +727,9 @@
     flags       public
     filename    extensions/EXT/EGL_EXT_device_query_name.txt
 }
+extension EGL_EXT_platform_xcb {
+    number      141
+    flags       public
+    filename    extensions/EXT/EGL_EXT_platform_xcb.txt
+}
 # Next free extension number: 141