Skip to content

Instantly share code, notes, and snippets.

@andrew-harter
Last active July 3, 2024 10:15
Show Gist options
  • Save andrew-harter/8725e5959979f0db66db4479cfe80e0c to your computer and use it in GitHub Desktop.
Save andrew-harter/8725e5959979f0db66db4479cfe80e0c to your computer and use it in GitHub Desktop.
Barebones X11 Smooth Resizing

Barebones X11 Smooth Resizing

This example illustrates how you can use the X11 XSync extension (not to be confused with XSync()) to achieve smooth interactive resizing on the majority of X environments, assuming your program can redraw fast enough.

DISCLAIMER: This example shows only the basic usage of XSync using a single counter, which is the bare minimum required to support smooth resizing.

Basic Rundown:

  1. Indicate that your client is willing to receive sync requests:

    • Add _NET_WM_SYNC_REQUEST to your window's WM_PROTOCOLS.
    • Create an XSync Counter and set the property _NET_WM_SYNC_REQUEST_COUNTER to its ID.
  2. Handle the _NET_WM_SYNC_REQUEST client message event:

    • You will get this event when a resize is issued, but before the resize is presented.
    • When you receive this client message, hold on to the counter value contained within the message, and initiate a redraw.
  3. Update the counter:

    • After drawing in response to the resize, use the stored counter value from earlier to update the server's counter with XSyncSetCounter(). This indicates that the resize is ready to be presented.

NOTE: If you're using OpenGL for graphics, resizing may be sluggish on some systems if you don't disable vsync: (egl/glx)SwapInterval(0)

References:

The Example:

Below is the actual implementation. If this program does not work as expected, please bring it to my attention!

Compile with: cc -o smooth_resize smooth_resize.c -lX11 -lXext

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/sync.h>
int main(int argument_count, char* arguments[])
{
Display* display = XOpenDisplay(0);
int screen = DefaultScreen(display);
// Test to see if XSync is supported.
{
int major_opcode, first_event, first_error;
if(!XQueryExtension(display, "SYNC", &major_opcode, &first_event, &first_error))
{
fprintf(stderr, "XSync is not supported. This may or may not be an issue.\n");
}
}
Atom WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", false);
Atom _NET_WM_SYNC_REQUEST = XInternAtom(display, "_NET_WM_SYNC_REQUEST", false);
Atom _NET_WM_SYNC_REQUEST_COUNTER = XInternAtom(display, "_NET_WM_SYNC_REQUEST_COUNTER", false);
Window window = XCreateSimpleWindow(display, XDefaultRootWindow(display), 0, 0, 700, 700, 0, BlackPixel(display, screen), WhitePixel(display, screen));
XStoreName(display, window, "Smooth Resizing Demo");
XSetWMProtocols(display, window, &_NET_WM_SYNC_REQUEST, 1);
XID counter;
{
XSyncValue initial_value;
XSyncIntToValue(&initial_value, 0);
counter = XSyncCreateCounter(display, initial_value);
}
XChangeProperty(display, window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (uint8_t*)&counter, 1);
XMapWindow(display, window);
uint64_t counter_value = 0;
bool running = true;
bool needs_redraw = true;
while(running)
{
XEvent event;
while(XPending(display) > 0)
{
XNextEvent(display, &event);
if(event.type == Expose) needs_redraw = true;
if(event.type == ClientMessage && event.xclient.message_type == WM_PROTOCOLS && event.xclient.data.l[0] == _NET_WM_SYNC_REQUEST)
{
counter_value = 0;
counter_value |= event.xclient.data.l[2];
counter_value |= (event.xclient.data.l[3] << 32);
needs_redraw = true;
}
}
if(needs_redraw)
{
needs_redraw = false;
// Drawing code here
{
XSyncValue value;
XSyncIntToValue(&value, counter_value);
XSyncSetCounter(display, counter, value);
}
}
}
XDestroyWindow(display, window);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment