]> git.tdb.fi Git - geometrycompositor.git/blob - source/main.c
Initial, marginally functional version
[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 <GL/gl.h>
9 #include <GL/glx.h>
10
11 typedef struct CompositedWindow
12 {
13         Window window;
14         int x;
15         int y;
16         unsigned width;
17         unsigned height;
18         int map_state;
19         Damage damage;
20         Pixmap pixmap;
21         GLXPixmap glx_pixmap;
22         unsigned texture;
23 } CompositedWindow;
24
25 typedef struct CompositedScreen
26
27         int number;
28         Window root;
29         unsigned width;
30         unsigned height;
31         Window overlay;
32         Window render_window;
33         GLXFBConfig fbconfig;
34         GLXWindow glx_window;
35         GLXContext glx_context;
36         unsigned shaders[2];
37         unsigned program;
38         unsigned geometry_loc;
39         unsigned vertex_buffer;
40         unsigned vertex_array;
41         CompositedWindow *windows;
42         unsigned nwindows;
43         unsigned windows_capacity;
44 } CompositedScreen;
45
46 typedef struct Compositor
47 {
48         Display *display;
49         CompositedScreen *screens;
50         unsigned nscreens;
51         int damage_event;
52         PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
53         PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
54         PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
55 } Compositor;
56
57 static const char *vshader =
58         "#version 150\n"
59         "uniform vec4 geometry;\n"
60         "in vec2 vertex;\n"
61         "out vec2 texcoord;\n"
62         "void main()\n"
63         "{\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"
66         "}\n";
67
68 static const char *fshader =
69         "#version 150\n"
70         "uniform sampler2D window;\n"
71         "in vec2 texcoord;\n"
72         "out vec4 frag_color;\n"
73         "void main()\n"
74         "{\n"
75         "       frag_color = texture(window, texcoord);\n"
76         "}\n";
77
78 static const float vertices[] =
79 {
80         0.0f, 1.0f,
81         0.0f, 0.0f,
82         1.0f, 1.0f,
83         1.0f, 0.0f
84 };
85
86 int with_error(const char *message)
87 {
88         fprintf(stderr, "%s\n", message);
89         return 0;
90 }
91
92 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
93 {
94         int attribs[9];
95         unsigned i;
96         GLXFBConfig *configs;
97         int nconfigs;
98         XVisualInfo *vi;
99         XSetWindowAttributes win_attr;
100
101         i = 0;
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;
107         attribs[i++] = True;
108         attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
109         attribs[i++] = True;
110         attribs[i] = None;
111
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];
116         XFree(configs);
117
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);
122
123         screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
124
125         i = 0;
126         attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
127         attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
128         attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
129         attribs[i++] = 3;
130         attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
131         attribs[i++] = 0;
132         attribs[i] = None;
133
134         screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
135
136         XFree(vi);
137
138         return 1;
139 }
140
141 void use_gl(Compositor *compositor, CompositedScreen *screen)
142 {
143         glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
144 }
145
146 unsigned compile_shader(GLenum type, const char *source)
147 {
148         unsigned shader;
149         int status;
150         char info_log[1024];
151         GLsizei length;
152         
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);
158         if(!status)
159         {
160                 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
161                 glDeleteShader(shader);
162                 return 0;
163         }
164         else if(length)
165                 printf("Shader info log:\n%s\n", info_log);
166
167         return shader;
168 }
169
170 int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
171 {
172         int status;
173         char info_log[1024];
174         GLsizei length;
175
176         use_gl(compositor, screen);
177
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])
181                 return 0;
182
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);
188
189         screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
190
191         glGetProgramiv(screen->program, GL_LINK_STATUS, &status);
192         glGetProgramInfoLog(screen->program, sizeof(info_log), &length, info_log);
193         if(!status)
194         {
195                 fprintf(stderr, "Program link failed:\n%s\n", info_log);
196                 glDeleteProgram(screen->program);
197                 return 0;
198         }
199         else if(length)
200                 printf("Program info log:\n%s\n", info_log);
201
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);
205
206         glGenVertexArrays(1, &screen->vertex_array);
207         glBindVertexArray(screen->vertex_array);
208         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
209         glEnableVertexAttribArray(0);
210
211         glBindBuffer(GL_ARRAY_BUFFER, 0);
212
213         return 1;
214 }
215
216 CompositedWindow *find_window(CompositedScreen *screen, Window w)
217 {
218         unsigned i;
219
220         for(i=0; i<screen->nwindows; ++i)
221                 if(screen->windows[i].window==w)
222                         return &screen->windows[i];
223
224         return NULL;
225 }
226
227 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
228 {
229         int attribs[5];
230         unsigned i;
231
232         if(window->pixmap)
233         {
234                 glXDestroyPixmap(compositor->display, window->glx_pixmap);
235                 XFreePixmap(compositor->display, window->pixmap);
236         }
237
238         i = 0;
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;
243         attribs[i++] = None;
244
245         window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
246         window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
247 }
248
249 void add_window(Compositor *compositor, CompositedScreen *screen, Window w)
250 {
251         CompositedWindow *window;
252         XWindowAttributes win_attr;
253
254         if(w==screen->root || w==screen->overlay)
255                 return;
256
257         XGetWindowAttributes(compositor->display, w, &win_attr);
258         if(win_attr.class==InputOnly)
259                 return;
260
261         if(find_window(screen, w))
262                 return;
263
264         if(screen->nwindows==screen->windows_capacity)
265         {
266                 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
267                 ++screen->windows_capacity;
268         }
269
270         window = &screen->windows[screen->nwindows++];
271
272         window->window = w;
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;
278
279         window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
280         window->pixmap = None;
281         window->glx_pixmap = None;
282
283         XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
284         if(window->map_state==IsViewable)
285                 create_window_pixmap(compositor, screen, window);
286
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);
292 }
293
294 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
295 {
296         unsigned i;
297
298         for(i=0; i<compositor->nscreens; ++i)
299                 if(compositor->screens[i].root==root)
300                         return &compositor->screens[i];
301
302         return NULL;
303 }
304
305 int initialize_screen(Compositor *compositor, unsigned number)
306 {
307         CompositedScreen *screen;
308         const char *extensions;
309         Window dummy_root;
310         int x, y;
311         unsigned border;
312         unsigned depth;
313         Window dummy_parent;
314         Window *children;
315         unsigned nchildren;
316         unsigned i;
317
318         screen = &compositor->screens[number];
319         screen->number = number;
320
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");
326
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);
331
332         if(!initialize_gl(compositor, screen))
333                 return 0;
334
335         if(!create_gl_resources(compositor, screen))
336                 return 0;
337
338         XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
339
340         screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
341         screen->nwindows = 0;
342         screen->windows_capacity = nchildren;
343
344         for(i=0; i<nchildren; ++i)
345                 add_window(compositor, screen, children[i]);
346
347         XFree(children);
348
349         return 1;
350 }
351
352 int initialize_compositor(Compositor *compositor)
353 {
354         int event_base;
355         int error_base;
356         int major_ver;
357         int minor_ver;
358         unsigned i;
359
360         compositor->display = XOpenDisplay(NULL);
361         if(!compositor->display)
362                 return with_error("Could not open X display");
363
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");
370
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");
377
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");
382         else if(major_ver<1)
383                 return with_error("XDamage 1.0 or later is required");
384
385         compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
386         compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
387         compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
388
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))
393                         return 0;
394
395         return 1;
396 }
397
398 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
399 {
400         glXDestroyContext(compositor->display, screen->glx_context);
401         glXDestroyWindow(compositor->display, screen->glx_window);
402         XDestroyWindow(compositor->display, screen->render_window);
403
404         XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
405 }
406
407 void shutdown_compositor(Compositor *compositor)
408 {
409         unsigned i;
410
411         glXMakeContextCurrent(compositor->display, 0, 0, NULL);
412         for(i=0; i<compositor->nscreens; ++i)
413                 shutdown_screen(compositor, &compositor->screens[i]);
414 }
415
416 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
417 {
418         CompositedScreen *screen = find_screen_by_root(compositor, event->parent);
419         if(!screen)
420                 return;
421
422         add_window(compositor, screen, event->window);
423 }
424
425 void process_map_event(Compositor *compositor, XMapEvent *event)
426 {
427         CompositedScreen *screen = find_screen_by_root(compositor, event->event);
428         if(!screen)
429                 return;
430
431         CompositedWindow *window = find_window(screen, event->window);
432         if(window)
433         {
434                 window->map_state = IsViewable;
435                 create_window_pixmap(compositor, screen, window);
436         }
437 }
438
439 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
440 {
441         CompositedScreen *screen = find_screen_by_root(compositor, event->event);
442         if(!screen)
443                 return;
444
445         CompositedWindow *window = find_window(screen, event->window);
446         if(window)
447                 window->map_state = IsUnviewable;
448 }
449
450 void process_event(Compositor *compositor)
451 {
452         XEvent event;
453         XNextEvent(compositor->display, &event);
454         switch(event.type)
455         {
456         case CreateNotify:
457                 process_create_window_event(compositor, &event.xcreatewindow);
458                 break;
459         case MapNotify:
460                 process_map_event(compositor, &event.xmap);
461                 break;
462         case UnmapNotify:
463                 process_unmap_event(compositor, &event.xunmap);
464                 break;
465         default:
466                 if(event.type!=compositor->damage_event+XDamageNotify)
467                         printf("Event %d\n", event.type);
468         }
469 }
470
471 void refresh_screens(Compositor *compositor)
472 {
473         unsigned i, j;
474
475         for(i=0; i<compositor->nscreens; ++i)
476         {
477                 CompositedScreen *screen = &compositor->screens[i];
478                 use_gl(compositor, screen);
479
480                 glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
481                 glClear(GL_COLOR_BUFFER_BIT);
482
483                 glUseProgram(screen->program);
484                 glBindVertexArray(screen->vertex_array);
485                 XGrabServer(compositor->display);
486                 glXWaitX();
487                 for(j=0; j<screen->nwindows; ++j)
488                 {
489                         CompositedWindow *window = &screen->windows[j];
490                         if(window->map_state==IsViewable)
491                         {
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);
498                         }
499                 }
500                 XUngrabServer(compositor->display);
501                 glXSwapBuffers(compositor->display, screen->glx_window);
502         }
503 }
504
505 int main()
506 {
507         Compositor compositor;
508
509         if(!initialize_compositor(&compositor))
510                 return 1;
511
512         while(1)
513         {
514                 process_event(&compositor);
515                 refresh_screens(&compositor);
516         }
517
518         shutdown_compositor(&compositor);
519
520         return 0;
521 }