1 #define GL_GLEXT_PROTOTYPES
6 #include <X11/extensions/Xcomposite.h>
7 #include <X11/extensions/Xdamage.h>
11 typedef struct CompositedWindow
25 typedef struct CompositedScreen
35 GLXContext glx_context;
38 unsigned geometry_loc;
39 unsigned vertex_buffer;
40 unsigned vertex_array;
41 CompositedWindow *windows;
43 unsigned windows_capacity;
46 typedef struct Compositor
49 CompositedScreen *screens;
52 PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
53 PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
54 PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
57 static const char *vshader =
59 "uniform vec4 geometry;\n"
61 "out vec2 texcoord;\n"
64 " gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
65 " texcoord = vec2(vertex.x, 1.0-vertex.y);\n"
68 static const char *fshader =
70 "uniform sampler2D window;\n"
72 "out vec4 frag_color;\n"
75 " frag_color = texture(window, texcoord);\n"
78 static const float vertices[] =
86 int with_error(const char *message)
88 fprintf(stderr, "%s\n", message);
92 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
99 XSetWindowAttributes win_attr;
102 attribs[i++] = GLX_DRAWABLE_TYPE;
103 attribs[i++] = GLX_WINDOW_BIT;
104 attribs[i++] = GLX_RENDER_TYPE;
105 attribs[i++] = GLX_RGBA_BIT;
106 attribs[i++] = GLX_DOUBLEBUFFER;
108 attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
112 configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
113 if(!configs || !nconfigs)
114 return with_error("Could not find a suitable FBConfig");
115 screen->fbconfig = configs[0];
118 vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
119 win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
120 screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
121 XMapWindow(compositor->display, screen->render_window);
123 screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
126 attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
127 attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
128 attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
130 attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
134 screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
141 void use_gl(Compositor *compositor, CompositedScreen *screen)
143 glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
146 unsigned compile_shader(GLenum type, const char *source)
153 shader = glCreateShader(type);
154 glShaderSource(shader, 1, &source, NULL);
155 glCompileShader(shader);
156 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
157 glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
160 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
161 glDeleteShader(shader);
165 printf("Shader info log:\n%s\n", info_log);
170 int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
176 use_gl(compositor, screen);
178 screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
179 screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader);
180 if(!screen->shaders[0] || !screen->shaders[1])
183 screen->program = glCreateProgram();
184 glAttachShader(screen->program, screen->shaders[0]);
185 glAttachShader(screen->program, screen->shaders[1]);
186 glBindAttribLocation(screen->program, 0, "vertex");
187 glLinkProgram(screen->program);
189 screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
191 glGetProgramiv(screen->program, GL_LINK_STATUS, &status);
192 glGetProgramInfoLog(screen->program, sizeof(info_log), &length, info_log);
195 fprintf(stderr, "Program link failed:\n%s\n", info_log);
196 glDeleteProgram(screen->program);
200 printf("Program info log:\n%s\n", info_log);
202 glGenBuffers(1, &screen->vertex_buffer);
203 glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
204 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
206 glGenVertexArrays(1, &screen->vertex_array);
207 glBindVertexArray(screen->vertex_array);
208 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
209 glEnableVertexAttribArray(0);
211 glBindBuffer(GL_ARRAY_BUFFER, 0);
216 CompositedWindow *find_window(CompositedScreen *screen, Window w)
220 for(i=0; i<screen->nwindows; ++i)
221 if(screen->windows[i].window==w)
222 return &screen->windows[i];
227 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
234 glXDestroyPixmap(compositor->display, window->glx_pixmap);
235 XFreePixmap(compositor->display, window->pixmap);
239 attribs[i++] = GLX_TEXTURE_TARGET_EXT;
240 attribs[i++] = GLX_TEXTURE_2D_EXT;
241 attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
242 attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
245 window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
246 window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
249 void add_window(Compositor *compositor, CompositedScreen *screen, Window w)
251 CompositedWindow *window;
252 XWindowAttributes win_attr;
254 if(w==screen->root || w==screen->overlay)
257 XGetWindowAttributes(compositor->display, w, &win_attr);
258 if(win_attr.class==InputOnly)
261 if(find_window(screen, w))
264 if(screen->nwindows==screen->windows_capacity)
266 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
267 ++screen->windows_capacity;
270 window = &screen->windows[screen->nwindows++];
273 window->x = win_attr.x;
274 window->y = win_attr.y;
275 window->width = win_attr.width;
276 window->height = win_attr.height;
277 window->map_state = win_attr.map_state;
279 window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
280 window->pixmap = None;
281 window->glx_pixmap = None;
283 XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
284 if(window->map_state==IsViewable)
285 create_window_pixmap(compositor, screen, window);
287 glGenTextures(1, &window->texture);
288 glBindTexture(GL_TEXTURE_2D, window->texture);
289 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
290 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
291 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
294 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
298 for(i=0; i<compositor->nscreens; ++i)
299 if(compositor->screens[i].root==root)
300 return &compositor->screens[i];
305 int initialize_screen(Compositor *compositor, unsigned number)
307 CompositedScreen *screen;
308 const char *extensions;
318 screen = &compositor->screens[number];
319 screen->number = number;
321 extensions = glXQueryExtensionsString(compositor->display, screen->number);
322 if(!strstr(extensions, "GLX_ARB_create_context"))
323 return with_error("GLX_ARB_create_context is required");
324 if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
325 return with_error("GLX_EXT_texture_from_pixmap is required");
327 screen->root = RootWindow(compositor->display, screen->number);
328 XSelectInput(compositor->display, screen->root, SubstructureNotifyMask);
329 screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
330 XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
332 if(!initialize_gl(compositor, screen))
335 if(!create_gl_resources(compositor, screen))
338 XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
340 screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
341 screen->nwindows = 0;
342 screen->windows_capacity = nchildren;
344 for(i=0; i<nchildren; ++i)
345 add_window(compositor, screen, children[i]);
352 int initialize_compositor(Compositor *compositor)
360 compositor->display = XOpenDisplay(NULL);
361 if(!compositor->display)
362 return with_error("Could not open X display");
364 if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
365 return with_error("XComposite is required but was not found");
366 else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
367 return with_error("Cannot determine XComposite version");
368 else if(major_ver==0 && minor_ver<3)
369 return with_error("XComposite 0.3 or later is required");
371 if(!glXQueryExtension(compositor->display, &event_base, &error_base))
372 return with_error("GLX is required but was not found");
373 else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
374 return with_error("Cannot determine GLX version");
375 else if(major_ver<1 || (major_ver==1 && minor_ver<4))
376 return with_error("GLX 1.4 or later is required");
378 if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
379 return with_error("XDamage is required but was not found");
380 else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
381 return with_error("Cannot determine XDamage version");
383 return with_error("XDamage 1.0 or later is required");
385 compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
386 compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
387 compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
389 compositor->nscreens = ScreenCount(compositor->display);
390 compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
391 for(i=0; i<compositor->nscreens; ++i)
392 if(!initialize_screen(compositor, i))
398 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
400 glXDestroyContext(compositor->display, screen->glx_context);
401 glXDestroyWindow(compositor->display, screen->glx_window);
402 XDestroyWindow(compositor->display, screen->render_window);
404 XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
407 void shutdown_compositor(Compositor *compositor)
411 glXMakeContextCurrent(compositor->display, 0, 0, NULL);
412 for(i=0; i<compositor->nscreens; ++i)
413 shutdown_screen(compositor, &compositor->screens[i]);
416 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
418 CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
422 add_window(compositor, screen, event->window);
425 void process_map_event(Compositor *compositor, XMapEvent *event)
427 CompositedScreen *screen = find_screen_by_root(compositor, event->event);
431 CompositedWindow *window = find_window(screen, event->window);
434 window->map_state = IsViewable;
435 create_window_pixmap(compositor, screen, window);
439 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
441 CompositedScreen *screen = find_screen_by_root(compositor, event->event);
445 CompositedWindow *window = find_window(screen, event->window);
447 window->map_state = IsUnviewable;
450 void process_event(Compositor *compositor)
453 XNextEvent(compositor->display, &event);
457 process_create_window_event(compositor, &event.xcreatewindow);
460 process_map_event(compositor, &event.xmap);
463 process_unmap_event(compositor, &event.xunmap);
466 if(event.type!=compositor->damage_event+XDamageNotify)
467 printf("Event %d\n", event.type);
471 void refresh_screens(Compositor *compositor)
475 for(i=0; i<compositor->nscreens; ++i)
477 CompositedScreen *screen = &compositor->screens[i];
478 use_gl(compositor, screen);
480 glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
481 glClear(GL_COLOR_BUFFER_BIT);
483 glUseProgram(screen->program);
484 glBindVertexArray(screen->vertex_array);
485 XGrabServer(compositor->display);
487 for(j=0; j<screen->nwindows; ++j)
489 CompositedWindow *window = &screen->windows[j];
490 if(window->map_state==IsViewable)
492 glBindTexture(GL_TEXTURE_2D, window->texture);
493 compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
494 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);
495 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
496 compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
497 XDamageSubtract(compositor->display, window->damage, None, None);
500 XUngrabServer(compositor->display);
501 glXSwapBuffers(compositor->display, screen->glx_window);
507 Compositor compositor;
509 if(!initialize_compositor(&compositor))
514 process_event(&compositor);
515 refresh_screens(&compositor);
518 shutdown_compositor(&compositor);