From f67e90622350af6c05669586e2ea9e5da9fba0bb Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 11 Dec 2015 17:17:55 +0200 Subject: [PATCH] Initial, marginally functional version Can redirect and display windows, but does not react to configure events or many other things. --- .gitignore | 1 + Makefile | 2 + source/main.c | 521 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 524 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 source/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d0eef7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/geometrycompositor diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..21189b6 --- /dev/null +++ b/source/main.c @@ -0,0 +1,521 @@ +#define GL_GLEXT_PROTOTYPES +#include +#include +#include +#include +#include +#include +#include +#include + +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; inwindows; ++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; inscreens; ++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; idisplay = 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; inscreens; ++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; inscreens; ++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; inscreens; ++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; jnwindows; ++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; +} -- 2.43.0