]> git.tdb.fi Git - geometrycompositor.git/commitdiff
Initial, marginally functional version
authorMikko Rasa <tdb@tdb.fi>
Fri, 11 Dec 2015 15:17:55 +0000 (17:17 +0200)
committerMikko Rasa <tdb@tdb.fi>
Fri, 11 Dec 2015 15:17:55 +0000 (17:17 +0200)
Can redirect and display windows, but does not react to configure
events or many other things.

.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
source/main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0d0eef7
--- /dev/null
@@ -0,0 +1 @@
+/geometrycompositor
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..63e1992
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+geometrycompositor: source/main.c
+       gcc -o $@ $< -Wall -Wextra -Werror -ggdb -lX11 -lXcomposite -lXdamage -lGL
diff --git a/source/main.c b/source/main.c
new file mode 100644 (file)
index 0000000..21189b6
--- /dev/null
@@ -0,0 +1,521 @@
+#define GL_GLEXT_PROTOTYPES
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xdamage.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+typedef struct CompositedWindow
+{
+       Window window;
+       int x;
+       int y;
+       unsigned width;
+       unsigned height;
+       int map_state;
+       Damage damage;
+       Pixmap pixmap;
+       GLXPixmap glx_pixmap;
+       unsigned texture;
+} CompositedWindow;
+
+typedef struct CompositedScreen
+{ 
+       int number;
+       Window root;
+       unsigned width;
+       unsigned height;
+       Window overlay;
+       Window render_window;
+       GLXFBConfig fbconfig;
+       GLXWindow glx_window;
+       GLXContext glx_context;
+       unsigned shaders[2];
+       unsigned program;
+       unsigned geometry_loc;
+       unsigned vertex_buffer;
+       unsigned vertex_array;
+       CompositedWindow *windows;
+       unsigned nwindows;
+       unsigned windows_capacity;
+} CompositedScreen;
+
+typedef struct Compositor
+{
+       Display *display;
+       CompositedScreen *screens;
+       unsigned nscreens;
+       int damage_event;
+       PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
+       PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
+       PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
+} Compositor;
+
+static const char *vshader =
+       "#version 150\n"
+       "uniform vec4 geometry;\n"
+       "in vec2 vertex;\n"
+       "out vec2 texcoord;\n"
+       "void main()\n"
+       "{\n"
+       "  gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
+       "  texcoord = vec2(vertex.x, 1.0-vertex.y);\n"
+       "}\n";
+
+static const char *fshader =
+       "#version 150\n"
+       "uniform sampler2D window;\n"
+       "in vec2 texcoord;\n"
+       "out vec4 frag_color;\n"
+       "void main()\n"
+       "{\n"
+       "       frag_color = texture(window, texcoord);\n"
+       "}\n";
+
+static const float vertices[] =
+{
+       0.0f, 1.0f,
+       0.0f, 0.0f,
+       1.0f, 1.0f,
+       1.0f, 0.0f
+};
+
+int with_error(const char *message)
+{
+       fprintf(stderr, "%s\n", message);
+       return 0;
+}
+
+int initialize_gl(Compositor *compositor, CompositedScreen *screen)
+{
+       int attribs[9];
+       unsigned i;
+       GLXFBConfig *configs;
+       int nconfigs;
+       XVisualInfo *vi;
+       XSetWindowAttributes win_attr;
+
+       i = 0;
+       attribs[i++] = GLX_DRAWABLE_TYPE;
+       attribs[i++] = GLX_WINDOW_BIT;
+       attribs[i++] = GLX_RENDER_TYPE;
+       attribs[i++] = GLX_RGBA_BIT;
+       attribs[i++] = GLX_DOUBLEBUFFER;
+       attribs[i++] = True;
+       attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
+       attribs[i++] = True;
+       attribs[i] = None;
+
+       configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
+       if(!configs || !nconfigs)
+               return with_error("Could not find a suitable FBConfig");
+       screen->fbconfig = configs[0];
+       XFree(configs);
+
+       vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
+       win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
+       screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
+       XMapWindow(compositor->display, screen->render_window);
+
+       screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
+
+       i = 0;
+       attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
+       attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
+       attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
+       attribs[i++] = 3;
+       attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
+       attribs[i++] = 0;
+       attribs[i] = None;
+
+       screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
+
+       XFree(vi);
+
+       return 1;
+}
+
+void use_gl(Compositor *compositor, CompositedScreen *screen)
+{
+       glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
+}
+
+unsigned compile_shader(GLenum type, const char *source)
+{
+       unsigned shader;
+       int status;
+       char info_log[1024];
+       GLsizei length;
+       
+       shader = glCreateShader(type);
+       glShaderSource(shader, 1, &source, NULL);
+       glCompileShader(shader);
+       glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+       glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
+       if(!status)
+       {
+               fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
+               glDeleteShader(shader);
+               return 0;
+       }
+       else if(length)
+               printf("Shader info log:\n%s\n", info_log);
+
+       return shader;
+}
+
+int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
+{
+       int status;
+       char info_log[1024];
+       GLsizei length;
+
+       use_gl(compositor, screen);
+
+       screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
+       screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader);
+       if(!screen->shaders[0] || !screen->shaders[1])
+               return 0;
+
+       screen->program = glCreateProgram();
+       glAttachShader(screen->program, screen->shaders[0]);
+       glAttachShader(screen->program, screen->shaders[1]);
+       glBindAttribLocation(screen->program, 0, "vertex");
+       glLinkProgram(screen->program);
+
+       screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
+
+       glGetProgramiv(screen->program, GL_LINK_STATUS, &status);
+       glGetProgramInfoLog(screen->program, sizeof(info_log), &length, info_log);
+       if(!status)
+       {
+               fprintf(stderr, "Program link failed:\n%s\n", info_log);
+               glDeleteProgram(screen->program);
+               return 0;
+       }
+       else if(length)
+               printf("Program info log:\n%s\n", info_log);
+
+       glGenBuffers(1, &screen->vertex_buffer);
+       glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
+       glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+       glGenVertexArrays(1, &screen->vertex_array);
+       glBindVertexArray(screen->vertex_array);
+       glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
+       glEnableVertexAttribArray(0);
+
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+       return 1;
+}
+
+CompositedWindow *find_window(CompositedScreen *screen, Window w)
+{
+       unsigned i;
+
+       for(i=0; i<screen->nwindows; ++i)
+               if(screen->windows[i].window==w)
+                       return &screen->windows[i];
+
+       return NULL;
+}
+
+void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
+{
+       int attribs[5];
+       unsigned i;
+
+       if(window->pixmap)
+       {
+               glXDestroyPixmap(compositor->display, window->glx_pixmap);
+               XFreePixmap(compositor->display, window->pixmap);
+       }
+
+       i = 0;
+       attribs[i++] = GLX_TEXTURE_TARGET_EXT;
+       attribs[i++] = GLX_TEXTURE_2D_EXT;
+       attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
+       attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
+       attribs[i++] = None;
+
+       window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
+       window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
+}
+
+void add_window(Compositor *compositor, CompositedScreen *screen, Window w)
+{
+       CompositedWindow *window;
+       XWindowAttributes win_attr;
+
+       if(w==screen->root || w==screen->overlay)
+               return;
+
+       XGetWindowAttributes(compositor->display, w, &win_attr);
+       if(win_attr.class==InputOnly)
+               return;
+
+       if(find_window(screen, w))
+               return;
+
+       if(screen->nwindows==screen->windows_capacity)
+       {
+               screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
+               ++screen->windows_capacity;
+       }
+
+       window = &screen->windows[screen->nwindows++];
+
+       window->window = w;
+       window->x = win_attr.x;
+       window->y = win_attr.y;
+       window->width = win_attr.width;
+       window->height = win_attr.height;
+       window->map_state = win_attr.map_state;
+
+       window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
+       window->pixmap = None;
+       window->glx_pixmap = None;
+
+       XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
+       if(window->map_state==IsViewable)
+               create_window_pixmap(compositor, screen, window);
+
+       glGenTextures(1, &window->texture);
+       glBindTexture(GL_TEXTURE_2D, window->texture);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
+{
+       unsigned i;
+
+       for(i=0; i<compositor->nscreens; ++i)
+               if(compositor->screens[i].root==root)
+                       return &compositor->screens[i];
+
+       return NULL;
+}
+
+int initialize_screen(Compositor *compositor, unsigned number)
+{
+       CompositedScreen *screen;
+       const char *extensions;
+       Window dummy_root;
+       int x, y;
+       unsigned border;
+       unsigned depth;
+       Window dummy_parent;
+       Window *children;
+       unsigned nchildren;
+       unsigned i;
+
+       screen = &compositor->screens[number];
+       screen->number = number;
+
+       extensions = glXQueryExtensionsString(compositor->display, screen->number);
+       if(!strstr(extensions, "GLX_ARB_create_context"))
+               return with_error("GLX_ARB_create_context is required");
+       if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
+               return with_error("GLX_EXT_texture_from_pixmap is required");
+
+       screen->root = RootWindow(compositor->display, screen->number);
+       XSelectInput(compositor->display, screen->root, SubstructureNotifyMask);
+       screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
+       XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
+
+       if(!initialize_gl(compositor, screen))
+               return 0;
+
+       if(!create_gl_resources(compositor, screen))
+               return 0;
+
+       XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
+
+       screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
+       screen->nwindows = 0;
+       screen->windows_capacity = nchildren;
+
+       for(i=0; i<nchildren; ++i)
+               add_window(compositor, screen, children[i]);
+
+       XFree(children);
+
+       return 1;
+}
+
+int initialize_compositor(Compositor *compositor)
+{
+       int event_base;
+       int error_base;
+       int major_ver;
+       int minor_ver;
+       unsigned i;
+
+       compositor->display = XOpenDisplay(NULL);
+       if(!compositor->display)
+               return with_error("Could not open X display");
+
+       if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
+               return with_error("XComposite is required but was not found");
+       else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
+               return with_error("Cannot determine XComposite version");
+       else if(major_ver==0 && minor_ver<3)
+               return with_error("XComposite 0.3 or later is required");
+
+       if(!glXQueryExtension(compositor->display, &event_base, &error_base))
+               return with_error("GLX is required but was not found");
+       else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
+               return with_error("Cannot determine GLX version");
+       else if(major_ver<1 || (major_ver==1 && minor_ver<4))
+               return with_error("GLX 1.4 or later is required");
+
+       if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
+               return with_error("XDamage is required but was not found");
+       else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
+               return with_error("Cannot determine XDamage version");
+       else if(major_ver<1)
+               return with_error("XDamage 1.0 or later is required");
+
+       compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
+       compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
+       compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
+
+       compositor->nscreens = ScreenCount(compositor->display);
+       compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
+       for(i=0; i<compositor->nscreens; ++i)
+               if(!initialize_screen(compositor, i))
+                       return 0;
+
+       return 1;
+}
+
+void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
+{
+       glXDestroyContext(compositor->display, screen->glx_context);
+       glXDestroyWindow(compositor->display, screen->glx_window);
+       XDestroyWindow(compositor->display, screen->render_window);
+
+       XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
+}
+
+void shutdown_compositor(Compositor *compositor)
+{
+       unsigned i;
+
+       glXMakeContextCurrent(compositor->display, 0, 0, NULL);
+       for(i=0; i<compositor->nscreens; ++i)
+               shutdown_screen(compositor, &compositor->screens[i]);
+}
+
+void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
+{
+       CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
+       if(!screen)
+               return;
+
+       add_window(compositor, screen, event->window);
+}
+
+void process_map_event(Compositor *compositor, XMapEvent *event)
+{
+       CompositedScreen *screen = find_screen_by_root(compositor, event->event);
+       if(!screen)
+               return;
+
+       CompositedWindow *window = find_window(screen, event->window);
+       if(window)
+       {
+               window->map_state = IsViewable;
+               create_window_pixmap(compositor, screen, window);
+       }
+}
+
+void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
+{
+       CompositedScreen *screen = find_screen_by_root(compositor, event->event);
+       if(!screen)
+               return;
+
+       CompositedWindow *window = find_window(screen, event->window);
+       if(window)
+               window->map_state = IsUnviewable;
+}
+
+void process_event(Compositor *compositor)
+{
+       XEvent event;
+       XNextEvent(compositor->display, &event);
+       switch(event.type)
+       {
+       case CreateNotify:
+               process_create_window_event(compositor, &event.xcreatewindow);
+               break;
+       case MapNotify:
+               process_map_event(compositor, &event.xmap);
+               break;
+       case UnmapNotify:
+               process_unmap_event(compositor, &event.xunmap);
+               break;
+       default:
+               if(event.type!=compositor->damage_event+XDamageNotify)
+                       printf("Event %d\n", event.type);
+       }
+}
+
+void refresh_screens(Compositor *compositor)
+{
+       unsigned i, j;
+
+       for(i=0; i<compositor->nscreens; ++i)
+       {
+               CompositedScreen *screen = &compositor->screens[i];
+               use_gl(compositor, screen);
+
+               glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
+               glClear(GL_COLOR_BUFFER_BIT);
+
+               glUseProgram(screen->program);
+               glBindVertexArray(screen->vertex_array);
+               XGrabServer(compositor->display);
+               glXWaitX();
+               for(j=0; j<screen->nwindows; ++j)
+               {
+                       CompositedWindow *window = &screen->windows[j];
+                       if(window->map_state==IsViewable)
+                       {
+                               glBindTexture(GL_TEXTURE_2D, window->texture);
+                               compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
+                               glUniform4f(screen->geometry_loc, (float)window->x/screen->width, (float)(screen->height-window->y-window->height)/screen->height, (float)window->width/screen->width, (float)window->height/screen->height);
+                               glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+                               compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
+                               XDamageSubtract(compositor->display, window->damage, None, None);
+                       }
+               }
+               XUngrabServer(compositor->display);
+               glXSwapBuffers(compositor->display, screen->glx_window);
+       }
+}
+
+int main()
+{
+       Compositor compositor;
+
+       if(!initialize_compositor(&compositor))
+               return 1;
+
+       while(1)
+       {
+               process_event(&compositor);
+               refresh_screens(&compositor);
+       }
+
+       shutdown_compositor(&compositor);
+
+       return 0;
+}