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