1 #define GL_GLEXT_PROTOTYPES
6 #include <X11/extensions/Xcomposite.h>
7 #include <X11/extensions/Xdamage.h>
8 #include <X11/extensions/shape.h>
12 typedef struct CompositedWindow
27 typedef struct CompositedScreen
37 GLXContext glx_context;
40 unsigned geometry_loc;
41 unsigned vertex_buffer;
42 unsigned vertex_array;
43 CompositedWindow *windows;
45 unsigned windows_capacity;
49 typedef struct Compositor
52 CompositedScreen *screens;
55 PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
56 PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
57 PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
61 static const char *vshader =
63 "uniform vec4 geometry;\n"
65 "out vec2 texcoord;\n"
68 " gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
69 " texcoord = vec2(vertex.x, 1.0-vertex.y);\n"
72 static const char *fshader =
74 "uniform sampler2D window;\n"
76 "out vec4 frag_color;\n"
79 " frag_color = texture(window, texcoord);\n"
82 static const float vertices[] =
90 int x_error_handler(Display *display, XErrorEvent *event)
92 printf("Ignoring X error %d on resource %lx\n", event->error_code, event->resourceid);
97 int with_error(const char *message)
99 fprintf(stderr, "%s\n", message);
103 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
107 GLXFBConfig *configs;
110 XSetWindowAttributes win_attr;
113 attribs[i++] = GLX_DRAWABLE_TYPE;
114 attribs[i++] = GLX_WINDOW_BIT;
115 attribs[i++] = GLX_RENDER_TYPE;
116 attribs[i++] = GLX_RGBA_BIT;
117 attribs[i++] = GLX_DOUBLEBUFFER;
119 attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
123 configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
124 if(!configs || !nconfigs)
125 return with_error("Could not find a suitable FBConfig");
126 screen->fbconfig = configs[0];
129 vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
130 win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
131 screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
132 XMapWindow(compositor->display, screen->render_window);
134 screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
137 attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
138 attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
139 attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
141 attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
145 screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
152 void use_gl(Compositor *compositor, CompositedScreen *screen)
154 glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
157 unsigned compile_shader(GLenum type, const char *source)
164 shader = glCreateShader(type);
165 glShaderSource(shader, 1, &source, NULL);
166 glCompileShader(shader);
167 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
168 glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
171 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
172 glDeleteShader(shader);
176 printf("Shader info log:\n%s\n", info_log);
181 int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
187 use_gl(compositor, screen);
189 screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
190 screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader);
191 if(!screen->shaders[0] || !screen->shaders[1])
194 screen->program = glCreateProgram();
195 glAttachShader(screen->program, screen->shaders[0]);
196 glAttachShader(screen->program, screen->shaders[1]);
197 glBindAttribLocation(screen->program, 0, "vertex");
198 glLinkProgram(screen->program);
200 screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
202 glGetProgramiv(screen->program, GL_LINK_STATUS, &status);
203 glGetProgramInfoLog(screen->program, sizeof(info_log), &length, info_log);
206 fprintf(stderr, "Program link failed:\n%s\n", info_log);
207 glDeleteProgram(screen->program);
211 printf("Program info log:\n%s\n", info_log);
213 glGenBuffers(1, &screen->vertex_buffer);
214 glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
215 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
217 glGenVertexArrays(1, &screen->vertex_array);
218 glBindVertexArray(screen->vertex_array);
219 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
220 glEnableVertexAttribArray(0);
222 glBindBuffer(GL_ARRAY_BUFFER, 0);
227 CompositedWindow *find_window(CompositedScreen *screen, Window w)
231 for(i=0; i<screen->nwindows; ++i)
232 if(screen->windows[i].window==w)
233 return &screen->windows[i];
238 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
245 glXDestroyPixmap(compositor->display, window->glx_pixmap);
246 XFreePixmap(compositor->display, window->pixmap);
250 attribs[i++] = GLX_TEXTURE_TARGET_EXT;
251 attribs[i++] = GLX_TEXTURE_2D_EXT;
252 attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
253 attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
256 window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
257 window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
260 void add_window(Compositor *compositor, CompositedScreen *screen, Window w)
262 CompositedWindow *window;
263 XWindowAttributes win_attr;
265 if(w==screen->root || w==screen->overlay)
268 if(!XGetWindowAttributes(compositor->display, w, &win_attr))
270 printf("XGetWindowAttributes failed; probably the window was already destroyed\n");
273 if(win_attr.class==InputOnly)
276 if(find_window(screen, w))
279 if(screen->nwindows==screen->windows_capacity)
281 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
282 ++screen->windows_capacity;
285 window = &screen->windows[screen->nwindows++];
288 window->x = win_attr.x;
289 window->y = win_attr.y;
290 window->width = win_attr.width;
291 window->height = win_attr.height;
292 window->border = win_attr.border_width;
293 window->map_state = win_attr.map_state;
295 window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
296 window->pixmap = None;
297 window->glx_pixmap = None;
299 XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
300 if(window->map_state==IsViewable)
301 create_window_pixmap(compositor, screen, window);
303 glGenTextures(1, &window->texture);
304 glBindTexture(GL_TEXTURE_2D, window->texture);
305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
307 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
310 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
314 for(i=0; i<compositor->nscreens; ++i)
315 if(compositor->screens[i].root==root)
316 return &compositor->screens[i];
321 CompositedScreen *find_screen_by_window(Compositor *compositor, Window w)
325 for(i=0; i<compositor->nscreens; ++i)
326 for(j=0; j<compositor->screens[i].nwindows; ++j)
327 if(compositor->screens[i].windows[j].window==w)
328 return &compositor->screens[i];
333 int initialize_screen(Compositor *compositor, unsigned number)
335 CompositedScreen *screen;
336 const char *extensions;
346 screen = &compositor->screens[number];
347 screen->number = number;
349 extensions = glXQueryExtensionsString(compositor->display, screen->number);
350 if(!strstr(extensions, "GLX_ARB_create_context"))
351 return with_error("GLX_ARB_create_context is required");
352 if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
353 return with_error("GLX_EXT_texture_from_pixmap is required");
355 screen->root = RootWindow(compositor->display, screen->number);
356 XSelectInput(compositor->display, screen->root, SubstructureNotifyMask);
357 screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
358 XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
359 XShapeCombineRectangles(compositor->display, screen->overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
361 if(!initialize_gl(compositor, screen))
364 if(!create_gl_resources(compositor, screen))
367 XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
369 screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
370 screen->nwindows = 0;
371 screen->windows_capacity = nchildren;
373 for(i=0; i<nchildren; ++i)
374 add_window(compositor, screen, children[i]);
381 int initialize_compositor(Compositor *compositor)
389 compositor->display = XOpenDisplay(NULL);
390 if(!compositor->display)
391 return with_error("Could not open X display");
393 XSetErrorHandler(&x_error_handler);
395 if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
396 return with_error("XComposite is required but was not found");
397 else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
398 return with_error("Cannot determine XComposite version");
399 else if(major_ver==0 && minor_ver<3)
400 return with_error("XComposite 0.3 or later is required");
402 if(!glXQueryExtension(compositor->display, &event_base, &error_base))
403 return with_error("GLX is required but was not found");
404 else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
405 return with_error("Cannot determine GLX version");
406 else if(major_ver<1 || (major_ver==1 && minor_ver<4))
407 return with_error("GLX 1.4 or later is required");
409 if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
410 return with_error("XDamage is required but was not found");
411 else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
412 return with_error("Cannot determine XDamage version");
414 return with_error("XDamage 1.0 or later is required");
416 if(!XShapeQueryExtension(compositor->display, &event_base, &error_base))
417 return with_error("XShape is required but was not found");
418 else if(!XShapeQueryVersion(compositor->display, &major_ver, &minor_ver))
419 return with_error("Cannot determine XShape version");
420 else if(major_ver<1 || (major_ver==1 && minor_ver<1))
421 return with_error("XShape 1.1 or later is required");
423 compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
424 compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
425 compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
427 compositor->nscreens = ScreenCount(compositor->display);
428 compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
429 for(i=0; i<compositor->nscreens; ++i)
430 if(!initialize_screen(compositor, i))
436 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
438 glXDestroyContext(compositor->display, screen->glx_context);
439 glXDestroyWindow(compositor->display, screen->glx_window);
440 XDestroyWindow(compositor->display, screen->render_window);
442 XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
445 void shutdown_compositor(Compositor *compositor)
449 glXMakeContextCurrent(compositor->display, 0, 0, NULL);
450 for(i=0; i<compositor->nscreens; ++i)
451 shutdown_screen(compositor, &compositor->screens[i]);
454 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
456 CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
460 add_window(compositor, screen, event->window);
463 void process_map_event(Compositor *compositor, XMapEvent *event)
465 CompositedScreen *screen = find_screen_by_root(compositor, event->event);
469 CompositedWindow *window = find_window(screen, event->window);
472 window->map_state = IsViewable;
473 create_window_pixmap(compositor, screen, window);
477 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
479 CompositedScreen *screen = find_screen_by_root(compositor, event->event);
483 CompositedWindow *window = find_window(screen, event->window);
485 window->map_state = IsUnviewable;
488 void process_damage_event(Compositor *compositor, XDamageNotifyEvent *event)
490 CompositedScreen *screen = find_screen_by_window(compositor, event->drawable);
495 compositor->dirty = 1;
498 int process_event(Compositor *compositor)
501 if(compositor->dirty)
503 if(!XCheckMaskEvent(compositor->display, -1, &event))
507 XNextEvent(compositor->display, &event);
512 process_create_window_event(compositor, &event.xcreatewindow);
515 process_map_event(compositor, &event.xmap);
518 process_unmap_event(compositor, &event.xunmap);
521 if(event.type==compositor->damage_event+XDamageNotify)
522 process_damage_event(compositor, (XDamageNotifyEvent *)&event);
524 printf("Event %d\n", event.type);
530 void refresh_screens(Compositor *compositor)
534 for(i=0; i<compositor->nscreens; ++i)
536 CompositedScreen *screen = &compositor->screens[i];
537 use_gl(compositor, screen);
539 glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
540 glClear(GL_COLOR_BUFFER_BIT);
542 glUseProgram(screen->program);
543 glBindVertexArray(screen->vertex_array);
544 for(j=0; j<screen->nwindows; ++j)
546 CompositedWindow *window = &screen->windows[j];
547 if(window->map_state==IsViewable)
549 glBindTexture(GL_TEXTURE_2D, window->texture);
550 compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
551 glUniform4f(screen->geometry_loc, (float)window->x/screen->width, (float)(screen->height-window->y-window->height)/screen->height, (float)(window->width+2*window->border)/screen->width, (float)(window->height+2*window->border)/screen->height);
552 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
553 compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
554 XDamageSubtract(compositor->display, window->damage, None, None);
557 glXSwapBuffers(compositor->display, screen->glx_window);
562 compositor->dirty = 0;
567 Compositor compositor;
569 if(!initialize_compositor(&compositor))
574 if(!process_event(&compositor))
575 refresh_screens(&compositor);
578 shutdown_compositor(&compositor);