From c6b989334000f0c760bf6427091a50b760ba99d5 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Thu, 19 Feb 2026 09:36:27 -0800 Subject: [PATCH] [x11_with_sync] --- x11.c | 88 +++++++++--------- x11_egl.c | 158 ++++++++++++++++---------------- x11_with_sync.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 125 deletions(-) create mode 100755 x11_with_sync.c diff --git a/x11.c b/x11.c index 87efd23..d59b0a6 100755 --- a/x11.c +++ b/x11.c @@ -59,58 +59,52 @@ int main(int argc, char **argv){ } } - /* (1) /ICC/client-to-window-manager/XStoreName.html - ** " assigns the name passed to window_name to the specified window " - */ - XStoreName(display, window, "Example Window"); - - /* (1) /window/XMapWindow.html - ** " maps the window and all of its subwindows that have had map requests " - **~ NOTE: This makes the window visible on the screen. - */ - XMapWindow(display, window); - - /*~ NOTE: Main loop */ - int exit_loop = 0; - for (;!exit_loop;){ - - /* (1) /event-handling/XPending.html - ** " returns the number of events that have been received from the X - ** server but have not been removed from the event queue " - **~ NOTE: The docs say this returns the number of events, but in - ** since that number could grow as we process inputs, so it's more - ** usually used as a way to check if there is at least one input. + if (window != 0){ + /* (1) /ICC/client-to-window-manager/XStoreName.html + ** " assigns the name passed to window_name to the specified window " */ - for (;XPending(display) > 0;){ - /* (1) /event-handling/manipulating-event-queue/XNextEvent.html - ** " copies the first event from the event queue into the specified - ** XEvent structure and then removes it from the queue " - */ - XEvent event; - XNextEvent(display, &event); + XStoreName(display, window, "Example Window"); + + /* (1) /window/XMapWindow.html + ** " maps the window and all of its subwindows that have had map requests " + **~ NOTE: This makes the window visible on the screen. + */ + XMapWindow(display, window); + + /*~ NOTE: Main loop */ + int exit_loop = 0; + for (;!exit_loop;){ - /* (1) /events/structures.html - ** " The XEvent structure is a union of the individual structures - ** declared for each event type. Depending on the type, you should - ** access members of each event by using the XEvent union. " + /* (1) /event-handling/XPending.html + ** " returns the number of events that have been received from the X + ** server but have not been removed from the event queue " + **~ NOTE: The docs say this returns the number of events, but it's + ** easier, and possibly more reliable to just use it to check if + ** there is at lesat one input. */ - switch (event.type){ - case ClientMessage: { - Atom atom = event.xclient.data.l[0]; - if (atom == atom__WM_DELETE_WINDOW){ - exit_loop = 1; - } - }break; + for (;XPending(display) > 0;){ + /* (1) /event-handling/manipulating-event-queue/XNextEvent.html + ** " copies the first event from the event queue into the specified + ** XEvent structure and then removes it from the queue " + */ + XEvent event; + XNextEvent(display, &event); + + /* (1) /events/structures.html + ** " The XEvent structure is a union of the individual structures + ** declared for each event type. Depending on the type, you should + ** access members of each event by using the XEvent union. " + */ + switch (event.type){ + case ClientMessage: { + Atom atom = event.xclient.data.l[0]; + if (atom == atom__WM_DELETE_WINDOW){ + exit_loop = 1; + } + }break; + } } } - - /* (1) /window-information/XGetWindowAttributes.html - ** " returns the current attributes for the specified window " - */ - XWindowAttributes window_attr = {0}; - if (!XGetWindowAttributes(display, window, &window_attr)){ - printf("XGetWindowAttributes failed\n"); - } } /*~ NOTE: Shutdown */ diff --git a/x11_egl.c b/x11_egl.c index 3cf38a1..e578e42 100755 --- a/x11_egl.c +++ b/x11_egl.c @@ -171,7 +171,7 @@ int main(int argc, char **argv){ } EGLSurface *surface = EGL_NO_SURFACE; - { + if (window != 0){ /* (2) eglChooseConfig ** " returns in configs a list of all EGL frame buffer configurations ** that match the attributes specified in attrib_list " @@ -218,88 +218,90 @@ int main(int argc, char **argv){ } } - /* (1) /ICC/client-to-window-manager/XStoreName.html - ** " assigns the name passed to window_name to the specified window " - */ - XStoreName(display, window, "Example Window"); - - /* (1) /window/XMapWindow.html - ** " maps the window and all of its subwindows that have had map requests " - **~ NOTE: This makes the window visible on the screen. - */ - XMapWindow(display, window); - - /* (2) eglMakeCurrent */ - EGLBoolean make_current_success2 = 0; - if (surface != EGL_NO_SURFACE){ - make_current_success2 = eglMakeCurrent(egl_display, surface, surface, context); - } - - /* (2) eglSwapInterval - ** " Specifies the minimum number of video frames that are displayed before - ** a buffer swap will occur " - */ - EGLBoolean swap_interval_success = 0; - if (make_current_success2){ - swap_interval_success = eglSwapInterval(egl_display, 1); - } - - /*~ NOTE: Main loop */ - int exit_loop = 0; - for (;!exit_loop;){ + if (surface != 0){ + /* (1) /ICC/client-to-window-manager/XStoreName.html + ** " assigns the name passed to window_name to the specified window " + */ + XStoreName(display, window, "Example Window"); - /* (1) /event-handling/XPending.html + /* (1) /window/XMapWindow.html + ** " maps the window and all of its subwindows that have had map requests " + **~ NOTE: This makes the window visible on the screen. + */ + XMapWindow(display, window); + + /* (2) eglMakeCurrent */ + EGLBoolean make_current_success2 = 0; + if (surface != EGL_NO_SURFACE){ + make_current_success2 = eglMakeCurrent(egl_display, surface, surface, context); + } + + /* (2) eglSwapInterval + ** " Specifies the minimum number of video frames that are displayed before + ** a buffer swap will occur " + */ + EGLBoolean swap_interval_success = 0; + if (make_current_success2){ + swap_interval_success = eglSwapInterval(egl_display, 1); + } + + /*~ NOTE: Main loop */ + int exit_loop = 0; + for (;!exit_loop;){ + + /* (1) /event-handling/XPending.html ** " returns the number of events that have been received from the X ** server but have not been removed from the event queue " - **~ NOTE: The docs say this returns the number of events, but in - ** since that number could grow as we process inputs, so it's more - ** usually used as a way to check if there is at least one input. + **~ NOTE: The docs say this returns the number of events, but it's + ** easier, and possibly more reliable to just use it to check if + ** there is at lesat one input. */ - for (;XPending(display) > 0;){ - /* (1) /event-handling/manipulating-event-queue/XNextEvent.html - ** " copies the first event from the event queue into the specified - ** XEvent structure and then removes it from the queue " - */ - XEvent event; - XNextEvent(display, &event); - - /* (1) /events/structures.html - ** " The XEvent structure is a union of the individual structures - ** declared for each event type. Depending on the type, you should - ** access members of each event by using the XEvent union. " - */ - switch (event.type){ - case ClientMessage: { - Atom atom = event.xclient.data.l[0]; - if (atom == atom__WM_DELETE_WINDOW){ - exit_loop = 1; - } - }break; + for (;XPending(display) > 0;){ + /* (1) /event-handling/manipulating-event-queue/XNextEvent.html + ** " copies the first event from the event queue into the specified + ** XEvent structure and then removes it from the queue " + */ + XEvent event; + XNextEvent(display, &event); + + /* (1) /events/structures.html + ** " The XEvent structure is a union of the individual structures + ** declared for each event type. Depending on the type, you should + ** access members of each event by using the XEvent union. " + */ + switch (event.type){ + case ClientMessage: { + Atom atom = event.xclient.data.l[0]; + if (atom == atom__WM_DELETE_WINDOW){ + exit_loop = 1; + } + }break; + } + } + + /* (1) /window-information/XGetWindowAttributes.html + ** " returns the current attributes for the specified window " + */ + XWindowAttributes window_attr = {0}; + if (!XGetWindowAttributes(display, window, &window_attr)){ + printf("XGetWindowAttributes failed\n"); + } + + if (window_attr.width > 0 && window_attr.height > 0){ + glDrawBuffer(GL_BACK); + glViewport(0, 0, window_attr.width, window_attr.height); + glClearColor(0.90f, 0.15f, 0.40f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + } + + /* (2) eglSwapBuffers + ** " back-buffered window surface, then the color buffer is copied + ** (posted) to the native window associated with that surface " + */ + EGLBoolean swap_success = eglSwapBuffers(egl_display, surface); + if (!swap_success){ + printf("eglSwapBuffers failed\n"); } - } - - /* (1) /window-information/XGetWindowAttributes.html - ** " returns the current attributes for the specified window " - */ - XWindowAttributes window_attr = {0}; - if (!XGetWindowAttributes(display, window, &window_attr)){ - printf("XGetWindowAttributes failed\n"); - } - - if (window_attr.width > 0 && window_attr.height > 0){ - glDrawBuffer(GL_BACK); - glViewport(0, 0, window_attr.width, window_attr.height); - glClearColor(0.90f, 0.15f, 0.40f, 1.f); - glClear(GL_COLOR_BUFFER_BIT); - } - - /* (2) eglSwapBuffers - ** " back-buffered window surface, then the color buffer is copied - ** (posted) to the native window associated with that surface " - */ - EGLBoolean swap_success = eglSwapBuffers(egl_display, surface); - if (!swap_success){ - printf("eglSwapBuffers failed\n"); } } diff --git a/x11_with_sync.c b/x11_with_sync.c new file mode 100755 index 0000000..35bfb83 --- /dev/null +++ b/x11_with_sync.c @@ -0,0 +1,236 @@ +#if 0 +mkdir -p build +clang -o build/demo -g x11_with_sync.c -lX11 -lXext +exit 0 +#endif + +/* +** Reading From: +** (1) "xlib manual" https://tronche.com/gui/x/xlib/ +** (2) "X Synchronization Extension Library" \ +** https://www.x.org/releases/X11R7.6/doc/libXext/synclib.html +** (3) "Extended Window Manager Hints" \ +** https://specifications.freedesktop.org/wm/latest/ +*/ + +#include +#include +#include + +#include +#include + +typedef unsigned int U32; + +void app_draw(void); + +int main(int argc, char **argv){ + /* (1) /display/opening.html + ** " The XOpenDisplay() function returns a Display structure that serves + ** as the connection to the X server and that contains all the + ** information about that X server. " + */ + Display *display = XOpenDisplay(0); + if (display == 0){ + printf("XOpenDisplay failed\n"); + } + + /* (2) XSyncQueryExtension +** " If display supports the SYNC extension, XSyncQueryExtension + ** returns True " +*/ + int event_base = 0; + int error_base = 0; + Status has_sync_ext = 0; + if (display != 0){ + has_sync_ext = XSyncQueryExtension(display, &event_base, &error_base); + if (!has_sync_ext){ + printf("XSyncQueryExtension failed\n"); + } + } + + /* (2) XSyncInitialize +** " If display supports the SYNC extension, XSyncQueryExtension + ** returns True " +*/ + int server_event_base = 0; + int server_error_base = 0; + Status sync_init = 0; + if (has_sync_ext){ + sync_init = XSyncInitialize(display, &server_event_base, &server_error_base); + if (!sync_init){ + printf("XSyncInitialize failed\n"); + } + } + + /* (2) XSyncCreateCounter + ** " creates a counter on the display with the given initial value and + ** returns the counter ID " + */ + XSyncCounter xsync_counter = 0; + if (has_sync_ext){ + XSyncValue zero; + XSyncMinValue(&zero); + xsync_counter = XSyncCreateCounter(display, zero); + if (!xsync_counter){ + printf("XSyncCreateCounter failed\n"); + } + } + + /* (1) /window-information/XInternAtom.html + ** " returns the atom identifier associated with the specified + ** atom_name string " + */ + Atom atom__WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False); + Atom atom__NET_WM_SYNC_REQUEST = XInternAtom(display, "_NET_WM_SYNC_REQUEST", False); + Atom atom__NET_WM_SYNC_REQUEST_COUNTER = XInternAtom(display, "_NET_WM_SYNC_REQUEST_COUNTER", False); + + /* (1) /window/XCreateWindow.html + ** " The XCreateWindow function creates an unmapped subwindow for a + ** specified parent window " + */ + Window window = 0; + if (sync_init){ + Window parent = DefaultRootWindow(display); + int x = 0; + int y = 0; + int w = 640; + int h = 480; + int border_width = 0; + int depth = CopyFromParent; + unsigned int xclass = InputOutput; + Visual *visual = CopyFromParent; + unsigned long valuemask = CWEventMask; + + XSetWindowAttributes attributes = {0}; + attributes.event_mask = StructureNotifyMask; + + window = XCreateWindow(display, parent, x, y, w, h, border_width, + depth, xclass, visual, valuemask, + &attributes); + + if (window == 0){ + printf("XCreateWindow failed\n"); + } + } + + if (window != 0){ + /* (1) /ICC/client-to-window-manager/XSetWMProtocols.html + ** " replaces the WM_PROTOCOLS property on the specified window " + */ + Atom protocols[] = { + atom__WM_DELETE_WINDOW, + atom__NET_WM_SYNC_REQUEST, + }; + XSetWMProtocols(display, window, protocols, sizeof(protocols)/sizeof(Atom)); + + /* (1) /window-information/XChangeProperty.html + ** " alters the property for the specified window " + ** + ** (3) /ar01s06.html _NET_WM_SYNC_REQUEST_COUNTER + ** " " + */ + + XChangeProperty(display, window, atom__NET_WM_SYNC_REQUEST_COUNTER, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)&xsync_counter, 1); + + /* (1) /ICC/client-to-window-manager/XStoreName.html + ** " assigns the name passed to window_name to the specified window " + */ + XStoreName(display, window, "Example Window"); + + /* (1) /window/XMapWindow.html + ** " maps the window and all of its subwindows that have had map requests " + **~ NOTE: This makes the window visible on the screen. + */ + XMapWindow(display, window); + + /*~ NOTE: Main loop */ + int exit_loop = 0; + int has_sync_serial = 0; + XSyncValue sync_serial; + for (;!exit_loop;){ + + /* (1) /event-handling/XPending.html + ** " returns the number of events that have been received from the X + ** server but have not been removed from the event queue " + **~ NOTE: The docs say this returns the number of events, but it's + ** easier, and possibly more reliable to just use it to check if + ** there is at lesat one input. + */ + for (;XPending(display) > 0;){ + /* (1) /event-handling/manipulating-event-queue/XNextEvent.html + ** " copies the first event from the event queue into the specified + ** XEvent structure and then removes it from the queue " + */ + XEvent event; + XNextEvent(display, &event); + + /* (1) /events/structures.html + ** " The XEvent structure is a union of the individual structures + ** declared for each event type. Depending on the type, you should + ** access members of each event by using the XEvent union. " + */ + switch (event.type){ + case ClientMessage: { + Atom atom = event.xclient.data.l[0]; + if (atom == atom__WM_DELETE_WINDOW){ + exit_loop = 1; + } + else if (atom == atom__NET_WM_SYNC_REQUEST){ + U32 serial_lo = event.xclient.data.l[2]; + U32 serial_hi = event.xclient.data.l[3]; + XSyncIntsToValue(&sync_serial, serial_lo, serial_hi); + has_sync_serial = 1; + } + }break; + + case ConfigureNotify: { + app_draw(); + }break; + } + } + + /*~ NOTE: After processing events, set counter*/ + if (has_sync_serial){ + has_sync_serial = 0; + /* (2) XSyncSetCounter + ** " sets counter to value " + */ + XSyncSetCounter(display, xsync_counter, sync_serial); + } + + /* (1) /window-information/XGetWindowAttributes.html + ** " returns the current attributes for the specified window " + */ + XWindowAttributes window_attr = {0}; + if (!XGetWindowAttributes(display, window, &window_attr)){ + printf("XGetWindowAttributes failed\n"); + } + + app_draw(); + } + } + + /*~ NOTE: Shutdown */ + + /* (1) /window/XDestroyWindow.html + ** " destroys the specified window as well as all of its subwindows " + */ + if (display != 0 && window != 0){ + XDestroyWindow(display, window); + } + + /* (1) /display/closing.html + ** " To close a display or disconnect from the X server, use + ** XCloseDisplay(). " + */ + if (display != 0){ + XCloseDisplay(display); + } + + return(0); +} + +void app_draw(void){}