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 "in vec2 texture_coord;\n"
67 "out vec2 texcoord;\n"
70 " gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
71 " texcoord = texture_coord;\n"
74 static const char *fshader =
76 "uniform sampler2D image;\n"
78 "out vec4 frag_color;\n"
81 " frag_color = texture(image, texcoord);\n"
84 static const float vertices[] =
87 0.0f, 1.0f, 0.0f, 0.0f,
88 0.0f, 0.0f, 0.0f, 1.0f,
89 1.0f, 1.0f, 1.0f, 0.0f,
90 1.0f, 0.0f, 1.0f, 1.0f
93 int terminate_requested = 0;
95 int x_error_handler(Display *display, XErrorEvent *event)
97 printf("Ignoring X error %d on resource %lx\n", event->error_code, event->resourceid);
102 int with_error(const char *message)
104 fprintf(stderr, "%s\n", message);
108 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
112 GLXFBConfig *configs;
115 XSetWindowAttributes win_attr;
118 attribs[i++] = GLX_DRAWABLE_TYPE;
119 attribs[i++] = GLX_WINDOW_BIT;
120 attribs[i++] = GLX_RENDER_TYPE;
121 attribs[i++] = GLX_RGBA_BIT;
122 attribs[i++] = GLX_DOUBLEBUFFER;
124 attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
128 configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
129 if(!configs || !nconfigs)
130 return with_error("Could not find a suitable FBConfig");
131 screen->fbconfig = configs[0];
134 vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
135 win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
136 screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
137 XMapWindow(compositor->display, screen->render_window);
139 screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
142 attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
143 attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
144 attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
146 attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
150 screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
157 void use_gl(Compositor *compositor, CompositedScreen *screen)
159 glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
162 unsigned compile_shader(GLenum type, const char *source)
169 shader = glCreateShader(type);
170 glShaderSource(shader, 1, &source, NULL);
171 glCompileShader(shader);
172 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
173 glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
176 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
177 glDeleteShader(shader);
181 printf("Shader info log:\n%s\n", info_log);
186 unsigned link_program(unsigned *shaders, unsigned nshaders)
194 program = glCreateProgram();
195 for(i=0; i<nshaders; ++i)
196 glAttachShader(program, shaders[i]);
197 glBindAttribLocation(program, 0, "vertex");
198 glBindAttribLocation(program, 1, "texture_coord");
199 glBindFragDataLocation(program, 0, "frag_color");
200 glLinkProgram(program);
202 glGetProgramiv(program, GL_LINK_STATUS, &status);
203 glGetProgramInfoLog(program, sizeof(info_log), &length, info_log);
206 fprintf(stderr, "Program link failed:\n%s\n", info_log);
207 glDeleteProgram(program);
211 printf("Program info log:\n%s\n", info_log);
216 int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
220 use_gl(compositor, screen);
222 screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
223 screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader);
224 if(!screen->shaders[0] || !screen->shaders[1])
227 screen->program = link_program(screen->shaders, 2);
231 screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
233 glGenBuffers(1, &screen->vertex_buffer);
234 glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
235 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
237 stride = 4*sizeof(float);
238 glGenVertexArrays(1, &screen->vertex_array);
239 glBindVertexArray(screen->vertex_array);
240 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, NULL);
241 glEnableVertexAttribArray(0);
242 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void *)(2*sizeof(float)));
243 glEnableVertexAttribArray(1);
244 glBindVertexArray(0);
246 glBindBuffer(GL_ARRAY_BUFFER, 0);
251 CompositedWindow *find_window(CompositedScreen *screen, Window w)
255 for(i=0; i<screen->nwindows; ++i)
256 if(screen->windows[i].window==w)
257 return &screen->windows[i];
262 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
269 glXDestroyPixmap(compositor->display, window->glx_pixmap);
270 XFreePixmap(compositor->display, window->pixmap);
274 attribs[i++] = GLX_TEXTURE_TARGET_EXT;
275 attribs[i++] = GLX_TEXTURE_2D_EXT;
276 attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
277 attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
280 window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
281 window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
284 CompositedWindow *add_window(Compositor *compositor, CompositedScreen *screen, Window w)
286 CompositedWindow *window;
287 XWindowAttributes win_attr;
289 if(w==screen->root || w==screen->overlay)
292 if(!XGetWindowAttributes(compositor->display, w, &win_attr))
294 printf("XGetWindowAttributes failed; probably the window was already destroyed\n");
297 if(win_attr.class==InputOnly)
300 if(find_window(screen, w))
303 if(screen->nwindows==screen->windows_capacity)
305 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
306 ++screen->windows_capacity;
309 window = &screen->windows[screen->nwindows++];
312 window->x = win_attr.x;
313 window->y = win_attr.y;
314 window->width = win_attr.width;
315 window->height = win_attr.height;
316 window->border = win_attr.border_width;
317 window->map_state = win_attr.map_state;
319 window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
320 window->pixmap = None;
321 window->glx_pixmap = None;
323 XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
324 if(window->map_state==IsViewable)
325 create_window_pixmap(compositor, screen, window);
327 glGenTextures(1, &window->texture);
328 glBindTexture(GL_TEXTURE_2D, window->texture);
329 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
330 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
331 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
336 void remove_window(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window, int destroyed)
340 glDeleteTextures(1, &window->texture);
343 XDamageDestroy(compositor->display, window->damage);
346 glXDestroyPixmap(compositor->display, window->glx_pixmap);
347 XFreePixmap(compositor->display, window->pixmap);
349 XCompositeUnredirectWindow(compositor->display, window->window, CompositeRedirectManual);
353 for(i=window-screen->windows; i<screen->nwindows; ++i)
354 screen->windows[i] = screen->windows[i+1];
357 CompositedWindow *reorder_window(CompositedScreen *screen, CompositedWindow *window, Window above)
360 CompositedWindow hold;
362 i = window-screen->windows;
365 for(j=0; j<screen->nwindows; ++j)
366 if(screen->windows[j].window==above)
369 if(j>=screen->nwindows || i==j+1)
382 screen->windows[i] = screen->windows[i+1];
387 screen->windows[i] = screen->windows[i-1];
389 screen->windows[j] = hold;
391 return &screen->windows[j];
394 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
398 for(i=0; i<compositor->nscreens; ++i)
399 if(compositor->screens[i].root==root)
400 return &compositor->screens[i];
405 CompositedScreen *find_screen_by_window(Compositor *compositor, Window w)
409 for(i=0; i<compositor->nscreens; ++i)
410 for(j=0; j<compositor->screens[i].nwindows; ++j)
411 if(compositor->screens[i].windows[j].window==w)
412 return &compositor->screens[i];
417 int initialize_screen(Compositor *compositor, unsigned number)
419 CompositedScreen *screen;
420 const char *extensions;
430 screen = &compositor->screens[number];
431 screen->number = number;
433 extensions = glXQueryExtensionsString(compositor->display, screen->number);
434 if(!strstr(extensions, "GLX_ARB_create_context"))
435 return with_error("GLX_ARB_create_context is required");
436 if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
437 return with_error("GLX_EXT_texture_from_pixmap is required");
439 screen->root = RootWindow(compositor->display, screen->number);
440 XSelectInput(compositor->display, screen->root, SubstructureNotifyMask);
441 screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
442 XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
443 XShapeCombineRectangles(compositor->display, screen->overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
445 if(!initialize_gl(compositor, screen))
448 if(!create_gl_resources(compositor, screen))
451 XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
453 screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
454 screen->nwindows = 0;
455 screen->windows_capacity = nchildren;
457 for(i=0; i<nchildren; ++i)
458 add_window(compositor, screen, children[i]);
467 int initialize_compositor(Compositor *compositor)
475 compositor->display = XOpenDisplay(NULL);
476 if(!compositor->display)
477 return with_error("Could not open X display");
479 XSetErrorHandler(&x_error_handler);
481 if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
482 return with_error("XComposite is required but was not found");
483 else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
484 return with_error("Cannot determine XComposite version");
485 else if(major_ver==0 && minor_ver<3)
486 return with_error("XComposite 0.3 or later is required");
488 if(!glXQueryExtension(compositor->display, &event_base, &error_base))
489 return with_error("GLX is required but was not found");
490 else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
491 return with_error("Cannot determine GLX version");
492 else if(major_ver<1 || (major_ver==1 && minor_ver<4))
493 return with_error("GLX 1.4 or later is required");
495 if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
496 return with_error("XDamage is required but was not found");
497 else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
498 return with_error("Cannot determine XDamage version");
500 return with_error("XDamage 1.0 or later is required");
502 if(!XShapeQueryExtension(compositor->display, &event_base, &error_base))
503 return with_error("XShape is required but was not found");
504 else if(!XShapeQueryVersion(compositor->display, &major_ver, &minor_ver))
505 return with_error("Cannot determine XShape version");
506 else if(major_ver<1 || (major_ver==1 && minor_ver<1))
507 return with_error("XShape 1.1 or later is required");
509 compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
510 compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
511 compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
513 compositor->nscreens = ScreenCount(compositor->display);
514 compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
515 for(i=0; i<compositor->nscreens; ++i)
516 if(!initialize_screen(compositor, i))
519 compositor->dirty = 1;
524 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
528 use_gl(compositor, screen);
530 for(i=0; i<screen->nwindows; ++i)
532 glDeleteTextures(1, &screen->windows[i].texture);
533 if(screen->windows[i].pixmap)
535 glXDestroyPixmap(compositor->display, screen->windows[i].glx_pixmap);
536 XFreePixmap(compositor->display, screen->windows[i].pixmap);
537 XDamageDestroy(compositor->display, screen->windows[i].damage);
541 glXMakeContextCurrent(compositor->display, 0, 0, NULL);
542 glXDestroyContext(compositor->display, screen->glx_context);
543 glXDestroyWindow(compositor->display, screen->glx_window);
544 XDestroyWindow(compositor->display, screen->render_window);
546 XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
548 free(screen->windows);
551 void shutdown_compositor(Compositor *compositor)
555 for(i=0; i<compositor->nscreens; ++i)
556 shutdown_screen(compositor, &compositor->screens[i]);
557 free(compositor->screens);
559 XCloseDisplay(compositor->display);
562 void mark_dirty(Compositor *compositor, CompositedScreen *screen)
564 compositor->dirty = 1;
568 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
570 CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
574 add_window(compositor, screen, event->window);
577 void process_destroy_window_event(Compositor *compositor, XDestroyWindowEvent *event)
579 CompositedScreen *screen;
580 CompositedWindow *window;
582 screen = find_screen_by_root(compositor, event->event);
586 window = find_window(screen, event->window);
588 remove_window(compositor, screen, window, 1);
591 void process_map_event(Compositor *compositor, XMapEvent *event)
593 CompositedScreen *screen;
594 CompositedWindow *window;
596 screen = find_screen_by_root(compositor, event->event);
600 window = find_window(screen, event->window);
604 window->map_state = IsViewable;
605 create_window_pixmap(compositor, screen, window);
607 mark_dirty(compositor, screen);
610 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
612 CompositedScreen *screen;
613 CompositedWindow *window;
615 screen = find_screen_by_root(compositor, event->event);
619 window = find_window(screen, event->window);
621 window->map_state = IsUnviewable;
623 mark_dirty(compositor, screen);
626 void process_reparent_event(Compositor *compositor, XReparentEvent *event)
628 CompositedScreen *screen;
629 CompositedWindow *window;
631 screen = find_screen_by_root(compositor, event->event);
635 if(event->parent==screen->root)
636 window = add_window(compositor, screen, event->window);
639 window = find_window(screen, event->window);
643 remove_window(compositor, screen, window, 0);
646 if(window && window->map_state==IsViewable)
647 mark_dirty(compositor, screen);
650 void process_configure_event(Compositor *compositor, XConfigureEvent *event)
652 CompositedScreen *screen;
653 CompositedWindow *window;
655 screen = find_screen_by_root(compositor, event->event);
659 window = find_window(screen, event->window);
663 window->x = event->x;
664 window->y = event->y;
665 if((unsigned)event->width!=window->width || (unsigned)event->height!=window->height || (unsigned)event->border_width!=window->border)
667 window->width = event->width;
668 window->height = event->height;
669 window->border = event->border_width;
670 create_window_pixmap(compositor, screen, window);
672 reorder_window(screen, window, event->above);
674 if(window->map_state==IsViewable)
675 mark_dirty(compositor, screen);
678 void process_damage_event(Compositor *compositor, XDamageNotifyEvent *event)
680 CompositedScreen *screen;
681 CompositedWindow *window;
683 screen = find_screen_by_window(compositor, event->drawable);
687 window = find_window(screen, event->drawable);
688 if(window->map_state==IsViewable)
689 mark_dirty(compositor, screen);
692 int process_event(Compositor *compositor)
695 if(compositor->dirty)
697 if(!XCheckMaskEvent(compositor->display, -1, &event))
701 XNextEvent(compositor->display, &event);
706 process_create_window_event(compositor, &event.xcreatewindow);
709 process_destroy_window_event(compositor, &event.xdestroywindow);
712 process_map_event(compositor, &event.xmap);
715 process_unmap_event(compositor, &event.xunmap);
718 process_reparent_event(compositor, &event.xreparent);
720 case ConfigureNotify:
721 process_configure_event(compositor, &event.xconfigure);
724 if(event.type==compositor->damage_event+XDamageNotify)
725 process_damage_event(compositor, (XDamageNotifyEvent *)&event);
727 printf("Event %d\n", event.type);
733 void refresh_screens(Compositor *compositor)
737 for(i=0; i<compositor->nscreens; ++i)
739 CompositedScreen *screen = &compositor->screens[i];
743 use_gl(compositor, screen);
745 glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
746 glClear(GL_COLOR_BUFFER_BIT);
748 glUseProgram(screen->program);
749 glBindVertexArray(screen->vertex_array);
750 for(j=0; j<screen->nwindows; ++j)
752 CompositedWindow *window = &screen->windows[j];
753 if(window->map_state==IsViewable)
755 glBindTexture(GL_TEXTURE_2D, window->texture);
756 compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
757 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);
758 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
759 compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
760 XDamageSubtract(compositor->display, window->damage, None, None);
763 glXSwapBuffers(compositor->display, screen->glx_window);
768 compositor->dirty = 0;
771 void sighandler(int sig)
773 terminate_requested = 1;
779 Compositor compositor;
781 signal(SIGINT, &sighandler);
782 signal(SIGTERM, &sighandler);
784 if(!initialize_compositor(&compositor))
787 while(!terminate_requested)
789 if(!process_event(&compositor))
790 refresh_screens(&compositor);
793 shutdown_compositor(&compositor);