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