#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); //XCompositeRedirectSubwindows(dpy, root_window, CompositeRedirectManual); XSelectInput(dpy, root_window, ExposureMask | StructureNotifyMask | SubstructureNotifyMask | PropertyChangeMask); 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) { Window above_window = None; if(i > 0) above_window = children[i - 1]; ngxc_compositor_add_window(&compositor, children[i], above_window); } if(children) XFree(children); XEvent xev; for(;;) { while(XPending(dpy)) { XNextEvent(dpy, &xev); if(xev.type != 22) fprintf(stderr, "event: %d\n", xev.type); switch(xev.type) { case CreateNotify: { // TODO: Might be needed to have configure notify work with "above" field //ngxc_compositor_add_window(&compositor, xev.xcreatewindow.window); break; } case DestroyNotify: { // TODO: //ngxc_compositor_remove_window(&compositor, xev.xdestroywindow.window); break; } case MapNotify: { ngxc_compositor_add_window(&compositor, xev.xmap.window, None); break; } case UnmapNotify: { ngxc_compositor_remove_window(&compositor, xev.xunmap.window); break; } case ReparentNotify: { if(xev.xreparent.parent == root_window) ngxc_compositor_add_window(&compositor, xev.xreparent.window, None); else ngxc_compositor_remove_window(&compositor, xev.xreparent.window); break; } 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; } case CirculateNotify: { fprintf(stderr, "circulate!\n"); ngxc_compositor_on_circulate(&compositor, &xev.xcirculate); break; } default: break; } } ngxc_compositor_render(&compositor); } return 0; }