aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-01-11 20:18:34 +0100
committerdec05eba <dec05eba@protonmail.com>2024-01-11 20:18:34 +0100
commit2b9389ae016c8048a16ba033f27ca5c256985e4d (patch)
tree56665698e299fe9a5754be2607719369059e5c53 /src/main.c
parentf3d89d03acaad7ebd750d9a39e4ca8c589de4782 (diff)
Display windows with minimal delay, locked to 60 fps
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c185
1 files changed, 185 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/shapeconst.h>
+
+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;
+}