From 2b9389ae016c8048a16ba033f27ca5c256985e4d Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 11 Jan 2024 20:18:34 +0100 Subject: Display windows with minimal delay, locked to 60 fps --- src/main.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/main.c (limited to 'src/main.c') diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d660e1d --- /dev/null +++ b/src/main.c @@ -0,0 +1,185 @@ +#include "compositor.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void fatal_if(bool condition, const char *fmt, ...) { + if(!condition) + return; + + va_list args; + va_start(args, fmt); + fprintf(stderr, "fatal: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(1); +} + +static int x_error_handler(Display *dpy, XErrorEvent *ee) { + (void)dpy; + (void)ee; + return 0; +} + +static void glx_context_choose(Display *dpy, GLXFBConfig **configs, XVisualInfo **visual_info) { + const int visual_attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 0, + GLX_DEPTH_SIZE, 0, + None, + }; + + int c; + *configs = glXChooseFBConfig(dpy, 0, visual_attribs, &c); + fatal_if(!configs, "failed to choose fb config"); + + for (int i = 0; i < c; i++) { + GLXFBConfig *config = configs[i]; + XVisualInfo *visual = glXGetVisualFromFBConfig(dpy, *config); + if(!visual) + continue; + + /*if(attr.depth != visual->depth) { + XFree(visual); + continue; + }*/ + + *visual_info = visual; + *configs = config; + return; + } + + fatal_if(true, "failed to find a valid fb config"); +} + +static void allow_input_passthrough(Display *dpy, Window w) { + XserverRegion region = XFixesCreateRegion(dpy, NULL, 0); + + XFixesSetWindowShapeRegion(dpy, w, ShapeBounding, 0, 0, 0); + XFixesSetWindowShapeRegion(dpy, w, ShapeInput, 0, 0, region); + + XFixesDestroyRegion (dpy, region); +} + +static bool is_compositor_running(Display *dpy) { + Atom prop_atom = XInternAtom(dpy, "_NET_WM_CM_S0", False); + return XGetSelectionOwner(dpy, prop_atom) != None; +} + +static void set_compositor_owner(Display *dpy, Window window) { + Atom prop_atom = XInternAtom(dpy, "_NET_WM_CM_S0", False); + XSetSelectionOwner(dpy, prop_atom, window, CurrentTime); +} + +static bool x11_supports_composite_named_window_pixmap(Display *display) { + int extension_major; + int extension_minor; + if(!XCompositeQueryExtension(display, &extension_major, &extension_minor)) + return false; + + int major_version; + int minor_version; + return XCompositeQueryVersion(display, &major_version, &minor_version) && (major_version > 0 || minor_version >= 2); +} + +int main(int argc, char **argv) { + /* Make nvidia act reasonably */ + setenv("__GL_MaxFramesAllowed", "1", true); + setenv("__GL_YIELD", "", true); + + Display *dpy = XOpenDisplay(NULL); + fatal_if(!dpy, "failed to connect to the X server"); + XSetErrorHandler(x_error_handler); + + fatal_if(!x11_supports_composite_named_window_pixmap(dpy), "your X server server is too old (Xcomposite extension version is less than 0.2)"); + fatal_if(is_compositor_running(dpy), "another compositor is already running"); + + const Window root_window = DefaultRootWindow(dpy); + XSelectInput(dpy, root_window, SubstructureNotifyMask); + + GLXFBConfig *fb_config; + XVisualInfo *xvisual_info; + glx_context_choose(dpy, &fb_config, &xvisual_info); + GLXContext glx_context = glXCreateNewContext(dpy, *fb_config, GLX_RGBA_TYPE, NULL, True); + fatal_if(!glx_context, "failed to create glx context"); + + //Colormap colormap = XCreateColormap(dpy, root_window, xvisual_info->visual, AllocNone); + //fatal_if(!colormap, "failed to create colormap"); + + const Window overlay_window = XCompositeGetOverlayWindow(dpy, root_window); + fatal_if(!overlay_window, "failed to get overlay window"); + allow_input_passthrough(dpy, overlay_window); + + // TODO: Create one window for each monitor + const int composite_window_width = WidthOfScreen(DefaultScreenOfDisplay(dpy)); + const int composite_window_height = HeightOfScreen(DefaultScreenOfDisplay(dpy)); + const Window composite_window = XCreateWindow(dpy, overlay_window, 0, 0, composite_window_width, composite_window_height, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); + fatal_if(!composite_window, "failed to create X window"); + // TODO: Set window title to ngxc and other properties that are needed + set_compositor_owner(dpy, composite_window); + + glXMakeContextCurrent(dpy, composite_window, composite_window, glx_context); + + glXSwapIntervalEXT(dpy, composite_window, true); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + allow_input_passthrough(dpy, composite_window); + XMapWindow(dpy, composite_window); + XFlush(dpy); + + // TODO: Also do this on monitor reconfigure + glViewport(0, 0, composite_window_width, composite_window_height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, composite_window_width, composite_window_height, 0.0, 0.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + ngxc_compositor compositor; + ngxc_compositor_init(&compositor, dpy, composite_window); + + Window root_return, parent_return; + Window *children = NULL; + unsigned int num_children = 0; + XQueryTree(dpy, root_window, &root_return, &parent_return, &children, &num_children); + + for(unsigned int i = 0; i < num_children; ++i) { + ngxc_compositor_add_window(&compositor, children[i]); + } + + if(children) + XFree(children); + + XEvent xev; + for(;;) { + while(XPending(dpy)) { + XNextEvent(dpy, &xev); + switch(xev.type) { + case ConfigureNotify: { + /* TODO: Instead of doing this potentially multiple times at once, only do this for the last configure notify event for each window */ + ngxc_compositor_on_configure(&compositor, &xev.xconfigure); + break; + } + default: + break; + } + } + ngxc_compositor_render(&compositor); + } + + return 0; +} -- cgit v1.2.3