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