]> git.tdb.fi Git - geometrycompositor.git/blob - source/main.c
35328543cbbb4cfc80329c8a531870ab6690aaa0
[geometrycompositor.git] / source / main.c
1 #define GL_GLEXT_PROTOTYPES
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <X11/Xlib.h>
6 #include <X11/extensions/Xcomposite.h>
7 #include <X11/extensions/Xdamage.h>
8 #include <X11/extensions/shape.h>
9 #include <GL/gl.h>
10 #include <GL/glx.h>
11
12 typedef struct CompositedWindow
13 {
14         Window window;
15         int x;
16         int y;
17         unsigned width;
18         unsigned height;
19         int map_state;
20         Damage damage;
21         Pixmap pixmap;
22         GLXPixmap glx_pixmap;
23         unsigned texture;
24 } CompositedWindow;
25
26 typedef struct CompositedScreen
27
28         int number;
29         Window root;
30         unsigned width;
31         unsigned height;
32         Window overlay;
33         Window render_window;
34         GLXFBConfig fbconfig;
35         GLXWindow glx_window;
36         GLXContext glx_context;
37         unsigned shaders[2];
38         unsigned program;
39         unsigned geometry_loc;
40         unsigned vertex_buffer;
41         unsigned vertex_array;
42         CompositedWindow *windows;
43         unsigned nwindows;
44         unsigned windows_capacity;
45 } CompositedScreen;
46
47 typedef struct Compositor
48 {
49         Display *display;
50         CompositedScreen *screens;
51         unsigned nscreens;
52         int damage_event;
53         PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
54         PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
55         PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
56 } Compositor;
57
58 static const char *vshader =
59         "#version 150\n"
60         "uniform vec4 geometry;\n"
61         "in vec2 vertex;\n"
62         "out vec2 texcoord;\n"
63         "void main()\n"
64         "{\n"
65         "  gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
66         "  texcoord = vec2(vertex.x, 1.0-vertex.y);\n"
67         "}\n";
68
69 static const char *fshader =
70         "#version 150\n"
71         "uniform sampler2D window;\n"
72         "in vec2 texcoord;\n"
73         "out vec4 frag_color;\n"
74         "void main()\n"
75         "{\n"
76         "       frag_color = texture(window, texcoord);\n"
77         "}\n";
78
79 static const float vertices[] =
80 {
81         0.0f, 1.0f,
82         0.0f, 0.0f,
83         1.0f, 1.0f,
84         1.0f, 0.0f
85 };
86
87 int with_error(const char *message)
88 {
89         fprintf(stderr, "%s\n", message);
90         return 0;
91 }
92
93 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
94 {
95         int attribs[9];
96         unsigned i;
97         GLXFBConfig *configs;
98         int nconfigs;
99         XVisualInfo *vi;
100         XSetWindowAttributes win_attr;
101
102         i = 0;
103         attribs[i++] = GLX_DRAWABLE_TYPE;
104         attribs[i++] = GLX_WINDOW_BIT;
105         attribs[i++] = GLX_RENDER_TYPE;
106         attribs[i++] = GLX_RGBA_BIT;
107         attribs[i++] = GLX_DOUBLEBUFFER;
108         attribs[i++] = True;
109         attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
110         attribs[i++] = True;
111         attribs[i] = None;
112
113         configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
114         if(!configs || !nconfigs)
115                 return with_error("Could not find a suitable FBConfig");
116         screen->fbconfig = configs[0];
117         XFree(configs);
118
119         vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
120         win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
121         screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
122         XMapWindow(compositor->display, screen->render_window);
123
124         screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
125
126         i = 0;
127         attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
128         attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
129         attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
130         attribs[i++] = 3;
131         attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
132         attribs[i++] = 0;
133         attribs[i] = None;
134
135         screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
136
137         XFree(vi);
138
139         return 1;
140 }
141
142 void use_gl(Compositor *compositor, CompositedScreen *screen)
143 {
144         glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
145 }
146
147 unsigned compile_shader(GLenum type, const char *source)
148 {
149         unsigned shader;
150         int status;
151         char info_log[1024];
152         GLsizei length;
153         
154         shader = glCreateShader(type);
155         glShaderSource(shader, 1, &source, NULL);
156         glCompileShader(shader);
157         glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
158         glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
159         if(!status)
160         {
161                 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
162                 glDeleteShader(shader);
163                 return 0;
164         }
165         else if(length)
166                 printf("Shader info log:\n%s\n", info_log);
167
168         return shader;
169 }
170
171 int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
172 {
173         int status;
174         char info_log[1024];
175         GLsizei length;
176
177         use_gl(compositor, screen);
178
179         screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
180         screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader);
181         if(!screen->shaders[0] || !screen->shaders[1])
182                 return 0;
183
184         screen->program = glCreateProgram();
185         glAttachShader(screen->program, screen->shaders[0]);
186         glAttachShader(screen->program, screen->shaders[1]);
187         glBindAttribLocation(screen->program, 0, "vertex");
188         glLinkProgram(screen->program);
189
190         screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
191
192         glGetProgramiv(screen->program, GL_LINK_STATUS, &status);
193         glGetProgramInfoLog(screen->program, sizeof(info_log), &length, info_log);
194         if(!status)
195         {
196                 fprintf(stderr, "Program link failed:\n%s\n", info_log);
197                 glDeleteProgram(screen->program);
198                 return 0;
199         }
200         else if(length)
201                 printf("Program info log:\n%s\n", info_log);
202
203         glGenBuffers(1, &screen->vertex_buffer);
204         glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
205         glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
206
207         glGenVertexArrays(1, &screen->vertex_array);
208         glBindVertexArray(screen->vertex_array);
209         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
210         glEnableVertexAttribArray(0);
211
212         glBindBuffer(GL_ARRAY_BUFFER, 0);
213
214         return 1;
215 }
216
217 CompositedWindow *find_window(CompositedScreen *screen, Window w)
218 {
219         unsigned i;
220
221         for(i=0; i<screen->nwindows; ++i)
222                 if(screen->windows[i].window==w)
223                         return &screen->windows[i];
224
225         return NULL;
226 }
227
228 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
229 {
230         int attribs[5];
231         unsigned i;
232
233         if(window->pixmap)
234         {
235                 glXDestroyPixmap(compositor->display, window->glx_pixmap);
236                 XFreePixmap(compositor->display, window->pixmap);
237         }
238
239         i = 0;
240         attribs[i++] = GLX_TEXTURE_TARGET_EXT;
241         attribs[i++] = GLX_TEXTURE_2D_EXT;
242         attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
243         attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
244         attribs[i++] = None;
245
246         window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
247         window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
248 }
249
250 void add_window(Compositor *compositor, CompositedScreen *screen, Window w)
251 {
252         CompositedWindow *window;
253         XWindowAttributes win_attr;
254
255         if(w==screen->root || w==screen->overlay)
256                 return;
257
258         XGetWindowAttributes(compositor->display, w, &win_attr);
259         if(win_attr.class==InputOnly)
260                 return;
261
262         if(find_window(screen, w))
263                 return;
264
265         if(screen->nwindows==screen->windows_capacity)
266         {
267                 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
268                 ++screen->windows_capacity;
269         }
270
271         window = &screen->windows[screen->nwindows++];
272
273         window->window = w;
274         window->x = win_attr.x;
275         window->y = win_attr.y;
276         window->width = win_attr.width;
277         window->height = win_attr.height;
278         window->map_state = win_attr.map_state;
279
280         window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
281         window->pixmap = None;
282         window->glx_pixmap = None;
283
284         XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
285         if(window->map_state==IsViewable)
286                 create_window_pixmap(compositor, screen, window);
287
288         glGenTextures(1, &window->texture);
289         glBindTexture(GL_TEXTURE_2D, window->texture);
290         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
291         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
292         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
293 }
294
295 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
296 {
297         unsigned i;
298
299         for(i=0; i<compositor->nscreens; ++i)
300                 if(compositor->screens[i].root==root)
301                         return &compositor->screens[i];
302
303         return NULL;
304 }
305
306 int initialize_screen(Compositor *compositor, unsigned number)
307 {
308         CompositedScreen *screen;
309         const char *extensions;
310         Window dummy_root;
311         int x, y;
312         unsigned border;
313         unsigned depth;
314         Window dummy_parent;
315         Window *children;
316         unsigned nchildren;
317         unsigned i;
318
319         screen = &compositor->screens[number];
320         screen->number = number;
321
322         extensions = glXQueryExtensionsString(compositor->display, screen->number);
323         if(!strstr(extensions, "GLX_ARB_create_context"))
324                 return with_error("GLX_ARB_create_context is required");
325         if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
326                 return with_error("GLX_EXT_texture_from_pixmap is required");
327
328         screen->root = RootWindow(compositor->display, screen->number);
329         XSelectInput(compositor->display, screen->root, SubstructureNotifyMask);
330         screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
331         XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
332         XShapeCombineRectangles(compositor->display, screen->overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
333
334         if(!initialize_gl(compositor, screen))
335                 return 0;
336
337         if(!create_gl_resources(compositor, screen))
338                 return 0;
339
340         XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
341
342         screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
343         screen->nwindows = 0;
344         screen->windows_capacity = nchildren;
345
346         for(i=0; i<nchildren; ++i)
347                 add_window(compositor, screen, children[i]);
348
349         XFree(children);
350
351         return 1;
352 }
353
354 int initialize_compositor(Compositor *compositor)
355 {
356         int event_base;
357         int error_base;
358         int major_ver;
359         int minor_ver;
360         unsigned i;
361
362         compositor->display = XOpenDisplay(NULL);
363         if(!compositor->display)
364                 return with_error("Could not open X display");
365
366         if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
367                 return with_error("XComposite is required but was not found");
368         else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
369                 return with_error("Cannot determine XComposite version");
370         else if(major_ver==0 && minor_ver<3)
371                 return with_error("XComposite 0.3 or later is required");
372
373         if(!glXQueryExtension(compositor->display, &event_base, &error_base))
374                 return with_error("GLX is required but was not found");
375         else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
376                 return with_error("Cannot determine GLX version");
377         else if(major_ver<1 || (major_ver==1 && minor_ver<4))
378                 return with_error("GLX 1.4 or later is required");
379
380         if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
381                 return with_error("XDamage is required but was not found");
382         else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
383                 return with_error("Cannot determine XDamage version");
384         else if(major_ver<1)
385                 return with_error("XDamage 1.0 or later is required");
386
387         if(!XShapeQueryExtension(compositor->display, &event_base, &error_base))
388                 return with_error("XShape is required but was not found");
389         else if(!XShapeQueryVersion(compositor->display, &major_ver, &minor_ver))
390                 return with_error("Cannot determine XShape version");
391         else if(major_ver<1 || (major_ver==1 && minor_ver<1))
392                 return with_error("XShape 1.1 or later is required");
393
394         compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
395         compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
396         compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
397
398         compositor->nscreens = ScreenCount(compositor->display);
399         compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
400         for(i=0; i<compositor->nscreens; ++i)
401                 if(!initialize_screen(compositor, i))
402                         return 0;
403
404         return 1;
405 }
406
407 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
408 {
409         glXDestroyContext(compositor->display, screen->glx_context);
410         glXDestroyWindow(compositor->display, screen->glx_window);
411         XDestroyWindow(compositor->display, screen->render_window);
412
413         XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
414 }
415
416 void shutdown_compositor(Compositor *compositor)
417 {
418         unsigned i;
419
420         glXMakeContextCurrent(compositor->display, 0, 0, NULL);
421         for(i=0; i<compositor->nscreens; ++i)
422                 shutdown_screen(compositor, &compositor->screens[i]);
423 }
424
425 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
426 {
427         CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
428         if(!screen)
429                 return;
430
431         add_window(compositor, screen, event->window);
432 }
433
434 void process_map_event(Compositor *compositor, XMapEvent *event)
435 {
436         CompositedScreen *screen = find_screen_by_root(compositor, event->event);
437         if(!screen)
438                 return;
439
440         CompositedWindow *window = find_window(screen, event->window);
441         if(window)
442         {
443                 window->map_state = IsViewable;
444                 create_window_pixmap(compositor, screen, window);
445         }
446 }
447
448 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
449 {
450         CompositedScreen *screen = find_screen_by_root(compositor, event->event);
451         if(!screen)
452                 return;
453
454         CompositedWindow *window = find_window(screen, event->window);
455         if(window)
456                 window->map_state = IsUnviewable;
457 }
458
459 void process_event(Compositor *compositor)
460 {
461         XEvent event;
462         XNextEvent(compositor->display, &event);
463         switch(event.type)
464         {
465         case CreateNotify:
466                 process_create_window_event(compositor, &event.xcreatewindow);
467                 break;
468         case MapNotify:
469                 process_map_event(compositor, &event.xmap);
470                 break;
471         case UnmapNotify:
472                 process_unmap_event(compositor, &event.xunmap);
473                 break;
474         default:
475                 if(event.type!=compositor->damage_event+XDamageNotify)
476                         printf("Event %d\n", event.type);
477         }
478 }
479
480 void refresh_screens(Compositor *compositor)
481 {
482         unsigned i, j;
483
484         for(i=0; i<compositor->nscreens; ++i)
485         {
486                 CompositedScreen *screen = &compositor->screens[i];
487                 use_gl(compositor, screen);
488
489                 glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
490                 glClear(GL_COLOR_BUFFER_BIT);
491
492                 glUseProgram(screen->program);
493                 glBindVertexArray(screen->vertex_array);
494                 XGrabServer(compositor->display);
495                 glXWaitX();
496                 for(j=0; j<screen->nwindows; ++j)
497                 {
498                         CompositedWindow *window = &screen->windows[j];
499                         if(window->map_state==IsViewable)
500                         {
501                                 glBindTexture(GL_TEXTURE_2D, window->texture);
502                                 compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
503                                 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);
504                                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
505                                 compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
506                                 XDamageSubtract(compositor->display, window->damage, None, None);
507                         }
508                 }
509                 XUngrabServer(compositor->display);
510                 glXSwapBuffers(compositor->display, screen->glx_window);
511         }
512 }
513
514 int main()
515 {
516         Compositor compositor;
517
518         if(!initialize_compositor(&compositor))
519                 return 1;
520
521         while(1)
522         {
523                 process_event(&compositor);
524                 refresh_screens(&compositor);
525         }
526
527         shutdown_compositor(&compositor);
528
529         return 0;
530 }