#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){}