1 #define GL_GLEXT_PROTOTYPES
7 #include <X11/extensions/Xcomposite.h>
8 #include <X11/extensions/Xdamage.h>
9 #include <X11/extensions/shape.h>
13 typedef struct CompositedWindow
28 typedef struct CompositedScreen
38 GLXContext glx_context;
41 unsigned geometry_loc;
42 unsigned vertex_buffer;
43 unsigned vertex_array;
44 CompositedWindow *windows;
46 unsigned windows_capacity;
50 typedef struct Compositor
53 CompositedScreen *screens;
56 PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
57 PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
58 PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
62 static const char *vshader =
64 "uniform vec4 geometry;\n"
66 "out vec2 texcoord;\n"
69 " gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
70 " texcoord = vec2(vertex.x, 1.0-vertex.y);\n"
73 static const char *fshader =
75 "uniform sampler2D window;\n"
77 "out vec4 frag_color;\n"
80 " frag_color = texture(window, texcoord);\n"
83 static const float vertices[] =
91 int terminate_requested = 0;
93 int x_error_handler(Display *display, XErrorEvent *event)
95 printf("Ignoring X error %d on resource %lx\n", event->error_code, event->resourceid);
100 int with_error(const char *message)
102 fprintf(stderr, "%s\n", message);
106 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
110 GLXFBConfig *configs;
113 XSetWindowAttributes win_attr;
116 attribs[i++] = GLX_DRAWABLE_TYPE;
117 attribs[i++] = GLX_WINDOW_BIT;
118 attribs[i++] = GLX_RENDER_TYPE;
119 attribs[i++] = GLX_RGBA_BIT;
120 attribs[i++] = GLX_DOUBLEBUFFER;
122 attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
126 configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
127 if(!configs || !nconfigs)
128 return with_error("Could not find a suitable FBConfig");
129 screen->fbconfig = configs[0];
132 vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
133 win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
134 screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
135 XMapWindow(compositor->display, screen->render_window);
137 screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
140 attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
141 attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
142 attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
144 attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
148 screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
155 void use_gl(Compositor *compositor, CompositedScreen *screen)
157 glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
160 unsigned compile_shader(GLenum type, const char *source)
167 shader = glCreateShader(type);
168 glShaderSource(shader, 1, &source, NULL);
169 glCompileShader(shader);
170 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
171 glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
174 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
175 glDeleteShader(shader);
179 printf("Shader info log:\n%s\n", info_log);
184 unsigned link_program(unsigned *shaders, unsigned nshaders)
192 program = glCreateProgram();
193 for(i=0; i<nshaders; ++i)
194 glAttachShader(program, shaders[i]);
195 glBindAttribLocation(program, 0, "vertex");
196 glLinkProgram(program);
198 glGetProgramiv(program, GL_LINK_STATUS, &status);
199 glGetProgramInfoLog(program, sizeof(info_log), &length, info_log);
202 fprintf(stderr, "Program link failed:\n%s\n", info_log);
203 glDeleteProgram(program);
207 printf("Program info log:\n%s\n", info_log);
212 int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
214 use_gl(compositor, screen);
216 screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
217 screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader);
218 if(!screen->shaders[0] || !screen->shaders[1])
221 screen->program = link_program(screen->shaders, 2);
225 screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
227 glGenBuffers(1, &screen->vertex_buffer);
228 glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
229 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
231 glGenVertexArrays(1, &screen->vertex_array);
232 glBindVertexArray(screen->vertex_array);
233 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
234 glEnableVertexAttribArray(0);
236 glBindBuffer(GL_ARRAY_BUFFER, 0);
241 CompositedWindow *find_window(CompositedScreen *screen, Window w)
245 for(i=0; i<screen->nwindows; ++i)
246 if(screen->windows[i].window==w)
247 return &screen->windows[i];
252 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
259 glXDestroyPixmap(compositor->display, window->glx_pixmap);
260 XFreePixmap(compositor->display, window->pixmap);
264 attribs[i++] = GLX_TEXTURE_TARGET_EXT;
265 attribs[i++] = GLX_TEXTURE_2D_EXT;
266 attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
267 attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
270 window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
271 window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
274 CompositedWindow *add_window(Compositor *compositor, CompositedScreen *screen, Window w)
276 CompositedWindow *window;
277 XWindowAttributes win_attr;
279 if(w==screen->root || w==screen->overlay)
282 if(!XGetWindowAttributes(compositor->display, w, &win_attr))
284 printf("XGetWindowAttributes failed; probably the window was already destroyed\n");
287 if(win_attr.class==InputOnly)
290 if(find_window(screen, w))
293 if(screen->nwindows==screen->windows_capacity)
295 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
296 ++screen->windows_capacity;
299 window = &screen->windows[screen->nwindows++];
302 window->x = win_attr.x;
303 window->y = win_attr.y;
304 window->width = win_attr.width;
305 window->height = win_attr.height;
306 window->border = win_attr.border_width;
307 window->map_state = win_attr.map_state;
309 window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
310 window->pixmap = None;
311 window->glx_pixmap = None;
313 XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
314 if(window->map_state==IsViewable)
315 create_window_pixmap(compositor, screen, window);
317 glGenTextures(1, &window->texture);
318 glBindTexture(GL_TEXTURE_2D, window->texture);
319 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
320 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
321 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
326 void remove_window(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window, int destroyed)
330 glDeleteTextures(1, &window->texture);
333 XDamageDestroy(compositor->display, window->damage);
336 glXDestroyPixmap(compositor->display, window->glx_pixmap);
337 XFreePixmap(compositor->display, window->pixmap);
339 XCompositeUnredirectWindow(compositor->display, window->window, CompositeRedirectManual);
343 for(i=window-screen->windows; i<screen->nwindows; ++i)
344 screen->windows[i] = screen->windows[i+1];
347 CompositedWindow *reorder_window(CompositedScreen *screen, CompositedWindow *window, Window above)
350 CompositedWindow hold;
352 i = window-screen->windows;
355 for(j=0; j<screen->nwindows; ++j)
356 if(screen->windows[j].window==above)
359 if(j>=screen->nwindows || i==j+1)
372 screen->windows[i] = screen->windows[i+1];
377 screen->windows[i] = screen->windows[i-1];
379 screen->windows[j] = hold;
381 return &screen->windows[j];
384 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
388 for(i=0; i<compositor->nscreens; ++i)
389 if(compositor->screens[i].root==root)
390 return &compositor->screens[i];
395 CompositedScreen *find_screen_by_window(Compositor *compositor, Window w)
399 for(i=0; i<compositor->nscreens; ++i)
400 for(j=0; j<compositor->screens[i].nwindows; ++j)
401 if(compositor->screens[i].windows[j].window==w)
402 return &compositor->screens[i];
407 int initialize_screen(Compositor *compositor, unsigned number)
409 CompositedScreen *screen;
410 const char *extensions;
420 screen = &compositor->screens[number];
421 screen->number = number;
423 extensions = glXQueryExtensionsString(compositor->display, screen->number);
424 if(!strstr(extensions, "GLX_ARB_create_context"))
425 return with_error("GLX_ARB_create_context is required");
426 if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
427 return with_error("GLX_EXT_texture_from_pixmap is required");
429 screen->root = RootWindow(compositor->display, screen->number);
430 XSelectInput(compositor->display, screen->root, SubstructureNotifyMask);
431 screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
432 XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
433 XShapeCombineRectangles(compositor->display, screen->overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
435 if(!initialize_gl(compositor, screen))
438 if(!create_gl_resources(compositor, screen))
441 XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
443 screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
444 screen->nwindows = 0;
445 screen->windows_capacity = nchildren;
447 for(i=0; i<nchildren; ++i)
448 add_window(compositor, screen, children[i]);
457 int initialize_compositor(Compositor *compositor)
465 compositor->display = XOpenDisplay(NULL);
466 if(!compositor->display)
467 return with_error("Could not open X display");
469 XSetErrorHandler(&x_error_handler);
471 if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
472 return with_error("XComposite is required but was not found");
473 else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
474 return with_error("Cannot determine XComposite version");
475 else if(major_ver==0 && minor_ver<3)
476 return with_error("XComposite 0.3 or later is required");
478 if(!glXQueryExtension(compositor->display, &event_base, &error_base))
479 return with_error("GLX is required but was not found");
480 else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
481 return with_error("Cannot determine GLX version");
482 else if(major_ver<1 || (major_ver==1 && minor_ver<4))
483 return with_error("GLX 1.4 or later is required");
485 if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
486 return with_error("XDamage is required but was not found");
487 else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
488 return with_error("Cannot determine XDamage version");
490 return with_error("XDamage 1.0 or later is required");
492 if(!XShapeQueryExtension(compositor->display, &event_base, &error_base))
493 return with_error("XShape is required but was not found");
494 else if(!XShapeQueryVersion(compositor->display, &major_ver, &minor_ver))
495 return with_error("Cannot determine XShape version");
496 else if(major_ver<1 || (major_ver==1 && minor_ver<1))
497 return with_error("XShape 1.1 or later is required");
499 compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
500 compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
501 compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
503 compositor->nscreens = ScreenCount(compositor->display);
504 compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
505 for(i=0; i<compositor->nscreens; ++i)
506 if(!initialize_screen(compositor, i))
509 compositor->dirty = 1;
514 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
518 use_gl(compositor, screen);
520 for(i=0; i<screen->nwindows; ++i)
522 glDeleteTextures(1, &screen->windows[i].texture);
523 if(screen->windows[i].pixmap)
525 glXDestroyPixmap(compositor->display, screen->windows[i].glx_pixmap);
526 XFreePixmap(compositor->display, screen->windows[i].pixmap);
527 XDamageDestroy(compositor->display, screen->windows[i].damage);
531 glXMakeContextCurrent(compositor->display, 0, 0, NULL);
532 glXDestroyContext(compositor->display, screen->glx_context);
533 glXDestroyWindow(compositor->display, screen->glx_window);
534 XDestroyWindow(compositor->display, screen->render_window);
536 XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
538 free(screen->windows);
541 void shutdown_compositor(Compositor *compositor)
545 for(i=0; i<compositor->nscreens; ++i)
546 shutdown_screen(compositor, &compositor->screens[i]);
547 free(compositor->screens);
549 XCloseDisplay(compositor->display);
552 void mark_dirty(Compositor *compositor, CompositedScreen *screen)
554 compositor->dirty = 1;
558 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
560 CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
564 add_window(compositor, screen, event->window);
567 void process_destroy_window_event(Compositor *compositor, XDestroyWindowEvent *event)
569 CompositedScreen *screen;
570 CompositedWindow *window;
572 screen = find_screen_by_root(compositor, event->event);
576 window = find_window(screen, event->window);
578 remove_window(compositor, screen, window, 1);
581 void process_map_event(Compositor *compositor, XMapEvent *event)
583 CompositedScreen *screen;
584 CompositedWindow *window;
586 screen = find_screen_by_root(compositor, event->event);
590 window = find_window(screen, event->window);
594 window->map_state = IsViewable;
595 create_window_pixmap(compositor, screen, window);
597 mark_dirty(compositor, screen);
600 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
602 CompositedScreen *screen;
603 CompositedWindow *window;
605 screen = find_screen_by_root(compositor, event->event);
609 window = find_window(screen, event->window);
611 window->map_state = IsUnviewable;
613 mark_dirty(compositor, screen);
616 void process_reparent_event(Compositor *compositor, XReparentEvent *event)
618 CompositedScreen *screen;
619 CompositedWindow *window;
621 screen = find_screen_by_root(compositor, event->event);
625 if(event->parent==screen->root)
626 window = add_window(compositor, screen, event->window);
629 window = find_window(screen, event->window);
633 remove_window(compositor, screen, window, 0);
636 if(window && window->map_state==IsViewable)
637 mark_dirty(compositor, screen);
640 void process_configure_event(Compositor *compositor, XConfigureEvent *event)
642 CompositedScreen *screen;
643 CompositedWindow *window;
645 screen = find_screen_by_root(compositor, event->event);
649 window = find_window(screen, event->window);
653 window->x = event->x;
654 window->y = event->y;
655 if((unsigned)event->width!=window->width || (unsigned)event->height!=window->height || (unsigned)event->border_width!=window->border)
657 window->width = event->width;
658 window->height = event->height;
659 window->border = event->border_width;
660 create_window_pixmap(compositor, screen, window);
662 reorder_window(screen, window, event->above);
664 if(window->map_state==IsViewable)
665 mark_dirty(compositor, screen);
668 void process_damage_event(Compositor *compositor, XDamageNotifyEvent *event)
670 CompositedScreen *screen;
671 CompositedWindow *window;
673 screen = find_screen_by_window(compositor, event->drawable);
677 window = find_window(screen, event->drawable);
678 if(window->map_state==IsViewable)
679 mark_dirty(compositor, screen);
682 int process_event(Compositor *compositor)
685 if(compositor->dirty)
687 if(!XCheckMaskEvent(compositor->display, -1, &event))
691 XNextEvent(compositor->display, &event);
696 process_create_window_event(compositor, &event.xcreatewindow);
699 process_destroy_window_event(compositor, &event.xdestroywindow);
702 process_map_event(compositor, &event.xmap);
705 process_unmap_event(compositor, &event.xunmap);
708 process_reparent_event(compositor, &event.xreparent);
710 case ConfigureNotify:
711 process_configure_event(compositor, &event.xconfigure);
714 if(event.type==compositor->damage_event+XDamageNotify)
715 process_damage_event(compositor, (XDamageNotifyEvent *)&event);
717 printf("Event %d\n", event.type);
723 void refresh_screens(Compositor *compositor)
727 for(i=0; i<compositor->nscreens; ++i)
729 CompositedScreen *screen = &compositor->screens[i];
733 use_gl(compositor, screen);
735 glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
736 glClear(GL_COLOR_BUFFER_BIT);
738 glUseProgram(screen->program);
739 glBindVertexArray(screen->vertex_array);
740 for(j=0; j<screen->nwindows; ++j)
742 CompositedWindow *window = &screen->windows[j];
743 if(window->map_state==IsViewable)
745 glBindTexture(GL_TEXTURE_2D, window->texture);
746 compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
747 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);
748 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
749 compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
750 XDamageSubtract(compositor->display, window->damage, None, None);
753 glXSwapBuffers(compositor->display, screen->glx_window);
758 compositor->dirty = 0;
761 void sighandler(int sig)
763 terminate_requested = 1;
769 Compositor compositor;
771 signal(SIGINT, &sighandler);
772 signal(SIGTERM, &sighandler);
774 if(!initialize_compositor(&compositor))
777 while(!terminate_requested)
779 if(!process_event(&compositor))
780 refresh_screens(&compositor);
783 shutdown_compositor(&compositor);