]> git.tdb.fi Git - geometrycompositor.git/blob - source/main.c
Implement a spherical curvature correction
[geometrycompositor.git] / source / main.c
1 #define GL_GLEXT_PROTOTYPES
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <signal.h>
6 #include <math.h>
7 #include <X11/Xlib.h>
8 #include <X11/Xatom.h>
9 #include <X11/extensions/Xcomposite.h>
10 #include <X11/extensions/Xdamage.h>
11 #include <X11/extensions/shape.h>
12 #include <X11/extensions/Xrandr.h>
13 #include <GL/gl.h>
14 #include <GL/glx.h>
15
16 typedef struct CompositedWindow
17 {
18         Window window;
19         int x;
20         int y;
21         unsigned width;
22         unsigned height;
23         unsigned border;
24         int map_state;
25         Damage damage;
26         Pixmap pixmap;
27         GLXPixmap glx_pixmap;
28         int recreate_pixmap;
29         unsigned texture;
30         unsigned mask_texture;
31         int use_mask;
32         int recreate_mask;
33 } CompositedWindow;
34
35 enum
36 {
37         CYLINDRICAL = 1,
38         SPHERICAL
39 };
40
41 typedef struct CompositedMonitor
42 {
43         char *name;
44         int enabled;
45         int x;
46         int y;
47         unsigned width;
48         unsigned height;
49         float keystone_vertical;
50         float curvature_type;
51         float curvature_depth;
52         float vertical_center;
53         float perspective;
54         unsigned vertex_buffer;
55         unsigned index_buffer;
56         unsigned vertex_array;
57         unsigned tessellation;
58         unsigned nelements;
59         short *geometry_data;
60         unsigned geometry_data_size;
61 } CompositedMonitor;
62
63 typedef struct CompositedScreen
64
65         int number;
66         Window root;
67         unsigned width;
68         unsigned height;
69         Window overlay;
70         Window render_window;
71         GLXFBConfig fbconfig;
72         GLXWindow glx_window;
73         GLXContext glx_context;
74         unsigned shaders[3];
75         unsigned program;
76         int geometry_loc;
77         unsigned masked_program;
78         int masked_geometry_loc;
79         unsigned window_vertex_buffer;
80         unsigned window_vertex_array;
81         unsigned framebuffer;
82         unsigned fb_texture;
83         Pixmap root_pixmap;
84         GLXPixmap root_glx_pixmap;
85         unsigned root_texture;
86         CompositedWindow *windows;
87         unsigned nwindows;
88         unsigned windows_capacity;
89         CompositedMonitor *monitors;
90         unsigned nmonitors;
91         int dirty;
92 } CompositedScreen;
93
94 typedef struct Compositor
95 {
96         Display *display;
97         CompositedScreen *screens;
98         unsigned nscreens;
99         int damage_event;
100         int shape_event;
101         PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
102         PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
103         PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
104         Atom root_pmap_atom;
105         Atom correction_atom;
106         Atom monitors_atom;
107         Atom geometry_data_atom;
108         Window selection_window;
109         int dirty;
110 } Compositor;
111
112 static const char *vshader_src =
113         "#version 130\n"
114         "uniform vec4 geometry;\n"
115         "in vec2 vertex;\n"
116         "in vec2 texture_coord;\n"
117         "out vec2 texcoord;\n"
118         "void main()\n"
119         "{\n"
120         "  gl_Position = vec4((geometry.xy+vertex*geometry.zw)*2.0-1.0, 0.0, 1.0);\n"
121         "  texcoord = texture_coord;\n"
122         "}\n";
123
124 static const char *fshader_src =
125         "#version 130\n"
126         "uniform sampler2D image;\n"
127         "in vec2 texcoord;\n"
128         "out vec4 frag_color;\n"
129         "void main()\n"
130         "{\n"
131         "  frag_color = texture(image, texcoord);\n"
132         "}\n";
133
134 static const char *masked_fshader_src =
135         "#version 130\n"
136         "uniform sampler2D image;\n"
137         "uniform sampler2D mask;\n"
138         "in vec2 texcoord;\n"
139         "out vec4 frag_color;\n"
140         "void main()\n"
141         "{\n"
142         "  if(texture(mask, texcoord).r==0.0)\n"
143         "    discard;\n"
144         "  frag_color = texture(image, texcoord);\n"
145         "}\n";
146
147 static const float window_vertices[] =
148 {
149         /* vertex    texcoord */
150         0.0f, 1.0f,  0.0f, 0.0f,
151         0.0f, 0.0f,  0.0f, 1.0f,
152         1.0f, 1.0f,  1.0f, 0.0f,
153         1.0f, 0.0f,  1.0f, 1.0f
154 };
155
156 int terminate_requested = 0;
157
158 int x_error_handler(Display *display, XErrorEvent *event)
159 {
160         printf("Ignoring X error %d on resource %lx\n", event->error_code, event->resourceid);
161         (void)display;
162         return 0;
163 }
164
165 int with_error(const char *message)
166 {
167         fprintf(stderr, "%s\n", message);
168         return 0;
169 }
170
171 int initialize_gl(Compositor *compositor, CompositedScreen *screen)
172 {
173         int attribs[9];
174         unsigned i;
175         GLXFBConfig *configs;
176         int nconfigs;
177         XVisualInfo *vi;
178         XSetWindowAttributes win_attr;
179
180         i = 0;
181         attribs[i++] = GLX_DRAWABLE_TYPE;
182         attribs[i++] = GLX_WINDOW_BIT;
183         attribs[i++] = GLX_RENDER_TYPE;
184         attribs[i++] = GLX_RGBA_BIT;
185         attribs[i++] = GLX_DOUBLEBUFFER;
186         attribs[i++] = True;
187         attribs[i++] = GLX_BIND_TO_TEXTURE_RGBA_EXT;
188         attribs[i++] = True;
189         attribs[i] = None;
190
191         configs = glXChooseFBConfig(compositor->display, screen->number, attribs, &nconfigs);
192         if(!configs || !nconfigs)
193                 return with_error("Could not find a suitable FBConfig");
194         screen->fbconfig = configs[0];
195         XFree(configs);
196
197         vi = glXGetVisualFromFBConfig(compositor->display, screen->fbconfig);
198         win_attr.colormap = XCreateColormap(compositor->display, screen->root, vi->visual, AllocNone);
199         screen->render_window = XCreateWindow(compositor->display, screen->overlay, 0, 0, screen->width, screen->height, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
200         XMapWindow(compositor->display, screen->render_window);
201
202         screen->glx_window = glXCreateWindow(compositor->display, screen->fbconfig, screen->render_window, NULL);
203
204         i = 0;
205         attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
206         attribs[i++] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
207         attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
208         attribs[i++] = 3;
209         attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
210         attribs[i++] = 1;
211         attribs[i] = None;
212
213         screen->glx_context = compositor->glXCreateContextAttribsARB(compositor->display, screen->fbconfig, NULL, True, attribs);
214
215         XFree(vi);
216
217         return 1;
218 }
219
220 void use_gl(Compositor *compositor, CompositedScreen *screen)
221 {
222         glXMakeContextCurrent(compositor->display, screen->glx_window, screen->glx_window, screen->glx_context);
223 }
224
225 unsigned compile_shader(GLenum type, const char *source)
226 {
227         unsigned shader;
228         int status;
229         char info_log[1024];
230         GLsizei length;
231         
232         shader = glCreateShader(type);
233         glShaderSource(shader, 1, &source, NULL);
234         glCompileShader(shader);
235         glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
236         glGetShaderInfoLog(shader, sizeof(info_log), &length, info_log);
237         if(!status)
238         {
239                 fprintf(stderr, "Shader compilation failed:\n%s\n", info_log);
240                 glDeleteShader(shader);
241                 return 0;
242         }
243         else if(length)
244                 printf("Shader info log:\n%s\n", info_log);
245
246         return shader;
247 }
248
249 unsigned link_program(unsigned vshader, unsigned fshader)
250 {
251         unsigned program;
252         int status;
253         char info_log[1024];
254         GLsizei length;
255
256         program = glCreateProgram();
257         glAttachShader(program, vshader);
258         glAttachShader(program, fshader);
259         glBindAttribLocation(program, 0, "vertex");
260         glBindAttribLocation(program, 1, "texture_coord");
261         glBindFragDataLocation(program, 0, "frag_color");
262         glLinkProgram(program);
263
264         glGetProgramiv(program, GL_LINK_STATUS, &status);
265         glGetProgramInfoLog(program, sizeof(info_log), &length, info_log);
266         if(!status)
267         {
268                 fprintf(stderr, "Program link failed:\n%s\n", info_log);
269                 glDeleteProgram(program);
270                 return 0;
271         }
272         else if(length)
273                 printf("Program info log:\n%s\n", info_log);
274
275         return program;
276 }
277
278 unsigned create_2d_texture()
279 {
280         unsigned texture;
281         glGenTextures(1, &texture);
282         glBindTexture(GL_TEXTURE_2D, texture);
283         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
284         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
285         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
286         return texture;
287 }
288
289 int create_gl_resources(CompositedScreen *screen)
290 {
291         unsigned stride;
292         int loc;
293
294         screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader_src);
295         screen->shaders[1] = compile_shader(GL_FRAGMENT_SHADER, fshader_src);
296         screen->shaders[2] = compile_shader(GL_FRAGMENT_SHADER, masked_fshader_src);
297         if(!screen->shaders[0] || !screen->shaders[1] || !screen->shaders[2])
298                 return 0;
299
300         screen->program = link_program(screen->shaders[0], screen->shaders[1]);
301         if(!screen->program)
302                 return 0;
303
304         screen->masked_program = link_program(screen->shaders[0], screen->shaders[2]);
305         if(!screen->masked_program)
306                 return 0;
307
308         screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
309         screen->masked_geometry_loc = glGetUniformLocation(screen->masked_program, "geometry");
310
311         loc = glGetUniformLocation(screen->masked_program, "mask");
312         if(loc>=0)
313         {
314                 glUseProgram(screen->masked_program);
315                 glUniform1i(loc, 1);
316         }
317
318         glGenBuffers(1, &screen->window_vertex_buffer);
319         glBindBuffer(GL_ARRAY_BUFFER, screen->window_vertex_buffer);
320         glBufferData(GL_ARRAY_BUFFER, sizeof(window_vertices), window_vertices, GL_STATIC_DRAW);
321
322         stride = 4*sizeof(float);
323         glGenVertexArrays(1, &screen->window_vertex_array);
324         glBindVertexArray(screen->window_vertex_array);
325         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, NULL);
326         glEnableVertexAttribArray(0);
327         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void *)(2*sizeof(float)));
328         glEnableVertexAttribArray(1);
329         glBindVertexArray(0);
330
331         glBindBuffer(GL_ARRAY_BUFFER, 0);
332
333         screen->fb_texture = create_2d_texture();
334         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, screen->width, screen->height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
335         glBindTexture(GL_TEXTURE_2D, 0);
336
337         glGenFramebuffers(1, &screen->framebuffer);
338         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, screen->framebuffer);
339         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screen->fb_texture, 0);
340         glDepthMask(GL_FALSE);
341         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
342
343         screen->root_texture = create_2d_texture();
344
345         return 1;
346 }
347
348 CompositedWindow *find_window(CompositedScreen *screen, Window w)
349 {
350         unsigned i;
351
352         for(i=0; i<screen->nwindows; ++i)
353                 if(screen->windows[i].window==w)
354                         return &screen->windows[i];
355
356         return NULL;
357 }
358
359 CompositedWindow *find_window_global(Compositor *compositor, Window w, CompositedScreen **screen)
360 {
361         unsigned i, j;
362
363         for(i=0; i<compositor->nscreens; ++i)
364                 for(j=0; j<compositor->screens[i].nwindows; ++j)
365                         if(compositor->screens[i].windows[j].window==w)
366                         {
367                                 if(screen)
368                                         *screen = &compositor->screens[i];
369                                 return &compositor->screens[i].windows[j];
370                         }
371
372         if(screen)
373                 *screen = NULL;
374         return NULL;
375 }
376
377 GLXPixmap pixmap_to_glx_pixmap(Compositor *compositor, CompositedScreen *screen, Pixmap pixmap)
378 {
379         int attribs[5];
380         unsigned i;
381
382         i = 0;
383         attribs[i++] = GLX_TEXTURE_TARGET_EXT;
384         attribs[i++] = GLX_TEXTURE_2D_EXT;
385         attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
386         attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
387         attribs[i++] = None;
388
389         return glXCreatePixmap(compositor->display, screen->fbconfig, pixmap, attribs);
390 }
391
392 void create_window_pixmap(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window)
393 {
394         if(window->pixmap)
395         {
396                 glXDestroyPixmap(compositor->display, window->glx_pixmap);
397                 XFreePixmap(compositor->display, window->pixmap);
398         }
399
400         window->pixmap = XCompositeNameWindowPixmap(compositor->display, window->window);
401         window->glx_pixmap = pixmap_to_glx_pixmap(compositor, screen, window->pixmap);
402         window->recreate_pixmap = 0;
403 }
404
405 void update_window_mask(Compositor *compositor, CompositedWindow *window)
406 {
407         Bool bounding_shaped;
408         Bool clip_shaped;
409         int xi, yi;
410         XRectangle *rects;
411         int rect_count;
412         int rect_order;
413         unsigned width;
414         unsigned height;
415         unsigned char *data;
416         int i;
417         unsigned y;
418
419         XShapeQueryExtents(compositor->display, window->window, &bounding_shaped, &xi, &yi, &width, &height, &clip_shaped, &xi, &yi, &width, &height);
420         window->use_mask = bounding_shaped;
421         if(!window->use_mask)
422                 return;
423
424         rects = XShapeGetRectangles(compositor->display, window->window, ShapeBounding, &rect_count, &rect_order);
425
426         width = window->width+2*window->border;
427         height = window->height+2*window->border;
428         data = (unsigned char *)malloc(width*height);
429         memset(data, 0, width*height);
430         for(i=0; i<rect_count; ++i)
431         {
432                 rects[i].x += window->border;
433                 rects[i].y += window->border;
434                 if(rects[i].x>=(int)width || rects[i].y>=(int)height)
435                         continue;
436
437                 if(rects[i].x<0)
438                 {
439                         if(-rects[i].x>rects[i].width)
440                                 continue;
441                         rects[i].width += rects[i].x;
442                         rects[i].x = 0;
443                 }
444
445                 if(rects[i].y<0)
446                 {
447                         if(-rects[i].y>rects[i].height)
448                                 continue;
449                         rects[i].height += rects[i].y;
450                         rects[i].y = 0;
451                 }
452
453                 if(rects[i].x+rects[i].width>(int)width)
454                         rects[i].width = width-rects[i].x;
455                 if(rects[i].y+rects[i].height>(int)height)
456                         rects[i].height = height-rects[i].y;
457
458                 for(y=0; y<rects[i].height; ++y)
459                 {
460                         unsigned char *row = data+(rects[i].y+y)*width+rects[i].x;
461                         memset(row, 255, rects[i].width);
462                 }
463         }
464
465         XFree(rects);
466
467         glBindTexture(GL_TEXTURE_2D, window->mask_texture);
468         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
469         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, data);
470
471         free(data);
472
473         window->recreate_mask = 0;
474 }
475
476 CompositedWindow *add_window(Compositor *compositor, CompositedScreen *screen, Window w)
477 {
478         CompositedWindow *window;
479         XWindowAttributes win_attr;
480
481         if(w==screen->root || w==screen->overlay)
482                 return NULL;
483
484         if(!XGetWindowAttributes(compositor->display, w, &win_attr))
485         {
486                 printf("XGetWindowAttributes failed; probably the window was already destroyed\n");
487                 return NULL;
488         }
489         if(win_attr.class==InputOnly)
490                 return NULL;
491
492         if(find_window(screen, w))
493                 return NULL;
494
495         if(screen->nwindows==screen->windows_capacity)
496         {
497                 screen->windows = (CompositedWindow *)realloc(screen->windows, (screen->windows_capacity+1)*sizeof(CompositedWindow));
498                 ++screen->windows_capacity;
499         }
500
501         window = &screen->windows[screen->nwindows++];
502
503         window->window = w;
504         window->x = win_attr.x;
505         window->y = win_attr.y;
506         window->width = win_attr.width;
507         window->height = win_attr.height;
508         window->border = win_attr.border_width;
509         window->map_state = win_attr.map_state;
510
511         window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
512         window->pixmap = None;
513         window->glx_pixmap = None;
514
515         XCompositeRedirectWindow(compositor->display, window->window, CompositeRedirectManual);
516         window->recreate_pixmap = (window->map_state==IsViewable);
517
518         window->texture = create_2d_texture();
519         window->mask_texture = create_2d_texture();
520         window->use_mask = 0;
521         window->recreate_mask = (window->map_state==IsViewable);
522
523         XShapeSelectInput(compositor->display, window->window, ShapeNotifyMask);
524
525         return window;
526 }
527
528 void remove_window(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window, int destroyed)
529 {
530         unsigned i;
531
532         glDeleteTextures(1, &window->texture);
533         if(!destroyed)
534         {
535                 XDamageDestroy(compositor->display, window->damage);
536                 if(window->pixmap)
537                 {
538                         glXDestroyPixmap(compositor->display, window->glx_pixmap);
539                         XFreePixmap(compositor->display, window->pixmap);
540                 }
541                 XCompositeUnredirectWindow(compositor->display, window->window, CompositeRedirectManual);
542         }
543
544         --screen->nwindows;
545         for(i=window-screen->windows; i<screen->nwindows; ++i)
546                 screen->windows[i] = screen->windows[i+1];
547 }
548
549 CompositedWindow *reorder_window(CompositedScreen *screen, CompositedWindow *window, Window above)
550 {
551         unsigned i, j;
552         CompositedWindow hold;
553
554         i = window-screen->windows;
555         if(above)
556         {
557                 for(j=0; j<screen->nwindows; ++j)
558                         if(screen->windows[j].window==above)
559                                 break;
560
561                 if(j>=screen->nwindows || i==j+1)
562                         return window;
563
564                 if(j<i)
565                         ++j;
566         }
567         else
568                 j = 0;
569
570         hold = *window;
571         if(i<j)
572         {
573                 for(; i<j; ++i)
574                         screen->windows[i] = screen->windows[i+1];
575         }
576         else
577         {
578                 for(; i>j; --i)
579                         screen->windows[i] = screen->windows[i-1];
580         }
581         screen->windows[j] = hold;
582
583         return &screen->windows[j];
584 }
585
586 CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
587 {
588         unsigned i;
589
590         for(i=0; i<compositor->nscreens; ++i)
591                 if(compositor->screens[i].root==root)
592                         return &compositor->screens[i];
593
594         return NULL;
595 }
596
597 void update_monitor_vertices(CompositedScreen *screen, CompositedMonitor *monitor)
598 {
599         unsigned t;
600         unsigned data_size;
601         float *vertex_data;
602         unsigned short *index_data;
603         unsigned x, y;
604         unsigned i;
605         float aspect;
606         float cyl_radius;
607         float cyl_arc;
608         float sin_ksv;
609         float cos_ksv;
610         float distance;
611         float eye[3];
612         float look[3];
613
614         t = monitor->tessellation;
615
616         data_size = (t+1)*(t+1)*4*sizeof(float);
617         vertex_data = (float *)malloc(data_size);
618
619         aspect = (float)monitor->width/monitor->height;
620
621         if(monitor->curvature_depth)
622         {
623                 cyl_radius = (monitor->curvature_depth*monitor->curvature_depth+0.25f)/(2.0f*monitor->curvature_depth);
624                 cyl_arc = 2.0f*asin(0.5f/cyl_radius);
625         }
626
627         sin_ksv = monitor->keystone_vertical/sqrt(1.0f+monitor->keystone_vertical*monitor->keystone_vertical);
628         cos_ksv = sqrt(1.0f-sin_ksv*sin_ksv);
629         distance = monitor->perspective+sin_ksv*((sin_ksv>0)-monitor->vertical_center)/aspect;
630
631         eye[0] = 0.0f;
632         eye[1] = (monitor->vertical_center-0.5f)/aspect+sin_ksv*distance;
633         eye[2] = cos_ksv*distance;
634
635         look[0] = 0.0f;
636         look[1] = -sin_ksv;
637         look[2] = -cos_ksv;
638
639         for(y=0; y<=t; ++y)
640                 for(x=0; x<=t; ++x)
641                 {
642                         float *v;
643
644                         v = vertex_data+(y*(t+1)+x)*3;
645                         v[0] = (float)x/t-0.5f;
646                         v[1] = ((float)y/t-0.5f)/aspect;
647                         v[2] = 0;
648                         if(monitor->curvature_depth)
649                         {
650                                 if(monitor->curvature_type==CYLINDRICAL)
651                                 {
652                                         v[2] = (1.0f-cos(v[0]*cyl_arc))*cyl_radius-monitor->curvature_depth;
653                                         v[0] = sin(v[0]*cyl_arc)*cyl_radius;
654                                 }
655                                 else if(monitor->curvature_type==SPHERICAL)
656                                 {
657                                         float r;
658
659                                         v[0] = tan(v[0]*cyl_arc)*cyl_radius;
660                                         v[1] = tan(v[1]*cyl_arc)*cyl_radius;
661                                         r = sqrt(v[0]*v[0]+v[1]*v[1]+cyl_radius*cyl_radius)/fabs(cyl_radius);
662                                         v[0] /= r;
663                                         v[1] /= r;
664                                         v[2] = cyl_radius-cyl_radius/r-monitor->curvature_depth;
665                                 }
666                         }
667                 }
668
669         monitor->geometry_data[0] = t;
670         for(i=0; i<3; ++i)
671                 monitor->geometry_data[1+i] = eye[i]*4096;
672         for(y=0; y<2; ++y)
673                 for(x=0; x<2; ++x)
674                 {
675                         i = 1+(1+y*2+x)*3;
676                         monitor->geometry_data[i] = ((x-0.5f)*distance/monitor->perspective)*4096;
677                         monitor->geometry_data[i+1] = (eye[1]+look[1]*distance-(y-monitor->vertical_center)*look[2]*distance/monitor->perspective/aspect)*4096;
678                         monitor->geometry_data[i+2] = (eye[2]+look[2]*distance+(y-monitor->vertical_center)*look[1]*distance/monitor->perspective/aspect)*4096;
679                 }
680         for(i=0; i<(t+1)*(t+1)*3; ++i)
681                 monitor->geometry_data[16+i] = vertex_data[i]*4096;
682
683         for(y=t; y<=t; --y)
684                 for(x=t; x<=t; --x)
685                 {
686                         float *v;
687                         float px, py, pz;
688                         float scale;
689
690                         v = vertex_data+(y*(t+1)+x)*3;
691                         px = v[0]-eye[0];
692                         py = (v[0]-eye[0])*look[0] - (v[1]-eye[1])*look[2] + (v[2]-eye[2])*look[1];
693                         pz = (v[0]-eye[0])*look[0] + (v[1]-eye[1])*look[1] + (v[2]-eye[2])*look[2];
694                         scale = monitor->perspective/pz;
695
696                         v = vertex_data+(y*(t+1)+x)*4;
697                         v[0] = px*scale+0.5f;
698                         v[1] = py*aspect*scale+monitor->vertical_center;
699                         v[2] = (monitor->x+(float)x*monitor->width/t)/screen->width;
700                         v[3] = 1.0f-(monitor->y+(1.0f-(float)y/t)*monitor->height)/screen->height;
701                 }
702
703         glBindBuffer(GL_ARRAY_BUFFER, monitor->vertex_buffer);
704         glBufferData(GL_ARRAY_BUFFER, data_size, vertex_data, GL_STATIC_DRAW);
705         glBindBuffer(GL_ARRAY_BUFFER, 0);
706         free(vertex_data);
707
708         monitor->nelements = t*((t+1)*2+1)-1;
709         data_size = monitor->nelements*sizeof(unsigned short);
710         index_data = (unsigned short *)malloc(data_size);
711         i = 0;
712         for(y=0; y<t; ++y)
713         {
714                 if(y>0)
715                         index_data[i++] = 0xFFFF;
716                 for(x=0; x<=t; ++x)
717                 {
718                         index_data[i++] = (y+1)*(t+1)+x;
719                         index_data[i++] = y*(t+1)+x;
720                 }
721         }
722         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, monitor->index_buffer);
723         glBufferData(GL_ELEMENT_ARRAY_BUFFER, data_size, index_data, GL_STATIC_DRAW);
724         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
725         free(index_data);
726 }
727
728 int initialize_monitor(Compositor *compositor, CompositedScreen *screen, XRRScreenResources *xrr_res, unsigned index)
729 {
730         CompositedMonitor *monitor;
731         XRROutputInfo *output;
732         int namelen;
733         XRRCrtcInfo *crtc;
734         unsigned buffers[2];
735         unsigned stride;
736
737         monitor = &screen->monitors[index];
738
739         output = XRRGetOutputInfo(compositor->display, xrr_res, xrr_res->outputs[index]);
740         namelen = strlen(output->name);
741         monitor->name = (char *)malloc(namelen+1);
742         strcpy(monitor->name, output->name);
743         monitor->enabled = !!output->crtc;
744         if(!monitor->enabled)
745         {
746                 XRRFreeOutputInfo(output);
747                 monitor->geometry_data = NULL;
748                 return 1;
749         }
750
751         crtc = XRRGetCrtcInfo(compositor->display, xrr_res, output->crtc);
752         monitor->x = crtc->x;
753         monitor->y = crtc->y;
754         monitor->width = crtc->width;
755         monitor->height = crtc->height;
756         XRRFreeCrtcInfo(crtc);
757         XRRFreeOutputInfo(output);
758
759         monitor->keystone_vertical = 0.0f;
760         monitor->curvature_type = CYLINDRICAL;
761         monitor->curvature_depth = 0.0f;
762         monitor->vertical_center = 0.5f;
763         monitor->perspective = 1.0f;
764
765         glGenBuffers(2, buffers);
766         monitor->vertex_buffer = buffers[0];
767         monitor->index_buffer = buffers[1];
768
769         monitor->tessellation = 50;
770         monitor->geometry_data_size = (monitor->tessellation+1)*(monitor->tessellation+1)*3+16;
771         monitor->geometry_data = (short *)malloc(monitor->geometry_data_size*sizeof(short));
772         update_monitor_vertices(screen, monitor);
773         stride = 4*sizeof(float);
774
775         glGenVertexArrays(1, &monitor->vertex_array);
776         glBindVertexArray(monitor->vertex_array);
777         glBindBuffer(GL_ARRAY_BUFFER, monitor->vertex_buffer);
778         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, NULL);
779         glEnableVertexAttribArray(0);
780         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void *)(2*sizeof(float)));
781         glEnableVertexAttribArray(1);
782         glBindBuffer(GL_ARRAY_BUFFER, 0);
783         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, monitor->index_buffer);
784         glBindVertexArray(0);
785
786         return 1;
787 }
788
789 CompositedMonitor *find_monitor_by_name(CompositedScreen *screen, char *name)
790 {
791         unsigned i;
792
793         for(i=0; i<screen->nmonitors; ++i)
794                 if(!strcmp(screen->monitors[i].name, name))
795                         return &screen->monitors[i];
796
797         return NULL;
798 }
799
800 CompositedMonitor *find_monitor_by_name_global(Compositor *compositor, char *name, CompositedScreen **screen)
801 {
802         unsigned i, j;
803
804         for(i=0; i<compositor->nscreens; ++i)
805                 for(j=0; j<compositor->screens[i].nmonitors; ++j)
806                         if(!strcmp(compositor->screens[i].monitors[j].name, name))
807                         {
808                                 if(screen)
809                                         *screen = &compositor->screens[i];
810                                 return &compositor->screens[i].monitors[j];
811                         }
812
813         if(screen)
814                 *screen = NULL;
815         return NULL;
816 }
817
818 void update_geometry_correction(Compositor *compositor, CompositedScreen *screen)
819 {
820         Atom prop_type;
821         int prop_format;
822         unsigned long overflow;
823         unsigned long names_length;
824         char *names;
825         unsigned long values_length;
826         short *values;
827         char *name_ptr;
828         unsigned i;
829
830         XGetWindowProperty(compositor->display, screen->root, compositor->monitors_atom, 0, 64, False, XA_STRING,
831                 &prop_type, &prop_format, &names_length, &overflow, (unsigned char **)&names);
832         if(prop_type!=XA_STRING || prop_format!=8)
833                 return;
834
835         XGetWindowProperty(compositor->display, screen->root, compositor->correction_atom, 0, 64, False, XA_INTEGER,
836                 &prop_type, &prop_format, &values_length, &overflow, (unsigned char **)&values);
837         if(prop_type!=XA_INTEGER || prop_format!=16)
838         {
839                 XFree(names);
840                 return;
841         }
842
843         use_gl(compositor, screen);
844
845         name_ptr = names;
846         for(i=0; i*5+4<values_length; ++i)
847         {
848                 CompositedMonitor *monitor;
849
850                 if(name_ptr>=names+names_length)
851                         break;
852
853                 monitor = find_monitor_by_name(screen, name_ptr);
854                 if(monitor)
855                 {
856                         monitor->keystone_vertical = values[i*5]/4096.0f;
857                         monitor->curvature_type = values[i*5+1];
858                         monitor->curvature_depth = values[i*5+2]/4096.0f;
859                         monitor->vertical_center = values[i*5+3]/4096.0f;
860                         monitor->perspective = values[i*5+4]/4096.0f;
861
862                         if(monitor->enabled)
863                                 update_monitor_vertices(screen, monitor);
864                 }
865
866                 name_ptr += strlen(name_ptr)+1;
867         }
868
869         XFree(names);
870         XFree(values);
871 }
872
873 void update_root_pixmap(Compositor *compositor, CompositedScreen *screen)
874 {
875         Atom prop_type;
876         int prop_format;
877         unsigned long overflow;
878         unsigned long length;
879         long *pixmap;
880         Window root;
881         int x, y;
882         unsigned width;
883         unsigned height;
884         unsigned border;
885         unsigned depth;
886
887         use_gl(compositor, screen);
888
889         if(screen->root_glx_pixmap)
890         {
891                 glXDestroyPixmap(compositor->display, screen->root_glx_pixmap);
892                 screen->root_glx_pixmap = None;
893         }
894
895         XGetWindowProperty(compositor->display, screen->root, compositor->root_pmap_atom, 0, 1, False, XA_PIXMAP,
896                 &prop_type, &prop_format, &length, &overflow, (unsigned char **)&pixmap);
897         if(prop_type!=XA_PIXMAP || prop_format!=32)
898         {
899                 screen->root_pixmap = None;
900                 return;
901         }
902
903         screen->root_pixmap = pixmap[0];
904         if(XGetGeometry(compositor->display, screen->root_pixmap, &root, &x, &y, &width, &height, &border, &depth))
905                 screen->root_glx_pixmap = pixmap_to_glx_pixmap(compositor, screen, screen->root_pixmap);
906         else
907                 screen->root_pixmap = None;
908
909         XFree(pixmap);
910 }
911
912 int initialize_screen(Compositor *compositor, unsigned number)
913 {
914         CompositedScreen *screen;
915         const char *extensions;
916         Window dummy_root;
917         int x, y;
918         unsigned border;
919         unsigned depth;
920         XRRScreenResources *xrr_res;
921         Window dummy_parent;
922         Window *children;
923         unsigned nchildren;
924         unsigned i;
925
926         screen = &compositor->screens[number];
927         screen->number = number;
928
929         extensions = glXQueryExtensionsString(compositor->display, screen->number);
930         if(!strstr(extensions, "GLX_ARB_create_context"))
931                 return with_error("GLX_ARB_create_context is required");
932         if(!strstr(extensions, "GLX_EXT_texture_from_pixmap"))
933                 return with_error("GLX_EXT_texture_from_pixmap is required");
934
935         screen->root = RootWindow(compositor->display, screen->number);
936         XSelectInput(compositor->display, screen->root, SubstructureNotifyMask|PropertyChangeMask);
937         screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
938         XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
939         XShapeCombineRectangles(compositor->display, screen->overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
940
941         if(!initialize_gl(compositor, screen))
942                 return 0;
943
944         use_gl(compositor, screen);
945
946         if(!create_gl_resources(screen))
947                 return 0;
948
949         xrr_res = XRRGetScreenResources(compositor->display, screen->root);
950         screen->nmonitors = xrr_res->noutput;
951         screen->monitors = (CompositedMonitor *)malloc(screen->nmonitors*sizeof(CompositedMonitor));
952         for(i=0; i<screen->nmonitors; ++i)
953                 if(!initialize_monitor(compositor, screen, xrr_res, i))
954                         return 0;
955         XRRFreeScreenResources(xrr_res);
956
957         screen->root_pixmap = None;
958         screen->root_glx_pixmap = None;
959
960         update_geometry_correction(compositor, screen);
961         update_root_pixmap(compositor, screen);
962
963         XQueryTree(compositor->display, screen->root, &dummy_root, &dummy_parent, &children, &nchildren);
964
965         screen->windows = (CompositedWindow *)malloc(nchildren*sizeof(CompositedWindow));
966         screen->nwindows = 0;
967         screen->windows_capacity = nchildren;
968
969         for(i=0; i<nchildren; ++i)
970                 add_window(compositor, screen, children[i]);
971
972         XFree(children);
973
974         screen->dirty = 1;
975
976         return 1;
977 }
978
979 int initialize_compositor(Compositor *compositor)
980 {
981         int event_base;
982         int error_base;
983         int major_ver;
984         int minor_ver;
985         unsigned i;
986
987         compositor->display = XOpenDisplay(NULL);
988         if(!compositor->display)
989                 return with_error("Could not open X display");
990
991         XSetErrorHandler(&x_error_handler);
992
993         if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
994                 return with_error("XComposite is required but was not found");
995         else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
996                 return with_error("Cannot determine XComposite version");
997         else if(major_ver==0 && minor_ver<3)
998                 return with_error("XComposite 0.3 or later is required");
999
1000         if(!glXQueryExtension(compositor->display, &event_base, &error_base))
1001                 return with_error("GLX is required but was not found");
1002         else if(!glXQueryVersion(compositor->display, &major_ver, &minor_ver))
1003                 return with_error("Cannot determine GLX version");
1004         else if(major_ver<1 || (major_ver==1 && minor_ver<4))
1005                 return with_error("GLX 1.4 or later is required");
1006
1007         if(!XDamageQueryExtension(compositor->display, &compositor->damage_event, &error_base))
1008                 return with_error("XDamage is required but was not found");
1009         else if(!XDamageQueryVersion(compositor->display, &major_ver, &minor_ver))
1010                 return with_error("Cannot determine XDamage version");
1011         else if(major_ver<1)
1012                 return with_error("XDamage 1.0 or later is required");
1013
1014         if(!XShapeQueryExtension(compositor->display, &compositor->shape_event, &error_base))
1015                 return with_error("XShape is required but was not found");
1016         else if(!XShapeQueryVersion(compositor->display, &major_ver, &minor_ver))
1017                 return with_error("Cannot determine XShape version");
1018         else if(major_ver<1 || (major_ver==1 && minor_ver<1))
1019                 return with_error("XShape 1.1 or later is required");
1020
1021         if(!XRRQueryExtension(compositor->display, &event_base, &error_base))
1022                 return with_error("XRandR is required but was not found");
1023         else if(!XRRQueryVersion(compositor->display, &major_ver, &minor_ver))
1024                 return with_error("Cannot determine XRandR version");
1025         else if(major_ver<1 || (major_ver==1 && minor_ver<2))
1026                 return with_error("XRandR 1.2 or later is required");
1027
1028         compositor->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((unsigned char *)"glXCreateContextAttribsARB");
1029         compositor->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXBindTexImageEXT");
1030         compositor->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((unsigned char *)"glXReleaseTexImageEXT");
1031
1032         compositor->root_pmap_atom = XInternAtom(compositor->display, "_XROOTPMAP_ID", False);
1033         compositor->correction_atom = XInternAtom(compositor->display, "_MSP_GEOMETRY_CORRECTION", False);
1034         compositor->monitors_atom = XInternAtom(compositor->display, "_MSP_GEOMETRY_CORRECTION_MONITORS", False);
1035         compositor->geometry_data_atom = XInternAtom(compositor->display, "_MSP_GEOMETRY_CORRECTION_DATA", False);
1036
1037         compositor->nscreens = ScreenCount(compositor->display);
1038         compositor->screens = (CompositedScreen *)malloc(compositor->nscreens*sizeof(CompositedScreen));
1039         for(i=0; i<compositor->nscreens; ++i)
1040                 if(!initialize_screen(compositor, i))
1041                         return 0;
1042
1043         compositor->selection_window = XCreateWindow(compositor->display, compositor->screens[0].root, 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
1044         XSetSelectionOwner(compositor->display, compositor->geometry_data_atom, compositor->selection_window, CurrentTime);
1045
1046         compositor->dirty = 1;
1047
1048         return 1;
1049 }
1050
1051 void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
1052 {
1053         unsigned i;
1054
1055         use_gl(compositor, screen);
1056
1057         for(i=0; i<screen->nwindows; ++i)
1058         {
1059                 glDeleteTextures(1, &screen->windows[i].texture);
1060                 glDeleteTextures(1, &screen->windows[i].mask_texture);
1061                 if(screen->windows[i].pixmap)
1062                 {
1063                         glXDestroyPixmap(compositor->display, screen->windows[i].glx_pixmap);
1064                         XFreePixmap(compositor->display, screen->windows[i].pixmap);
1065                         XDamageDestroy(compositor->display, screen->windows[i].damage);
1066                 }
1067         }
1068
1069         for(i=0; i<screen->nmonitors; ++i)
1070         {
1071                 free(screen->monitors[i].name);
1072                 if(screen->monitors[i].enabled)
1073                 {
1074                         glDeleteBuffers(1, &screen->monitors[i].vertex_buffer);
1075                         glDeleteBuffers(1, &screen->monitors[i].index_buffer);
1076                         glDeleteVertexArrays(1, &screen->monitors[i].vertex_array);
1077                 }
1078                 if(screen->monitors[i].geometry_data)
1079                         free(screen->monitors[i].geometry_data);
1080         }
1081
1082         glDeleteTextures(1, &screen->root_texture);
1083         glXDestroyPixmap(compositor->display, screen->root_glx_pixmap);
1084
1085         glDeleteBuffers(1, &screen->window_vertex_buffer);
1086         glDeleteVertexArrays(1, &screen->window_vertex_array);
1087         glDeleteFramebuffers(1, &screen->framebuffer);
1088         glDeleteTextures(1, &screen->fb_texture);
1089         glDeleteProgram(screen->program);
1090         glDeleteProgram(screen->masked_program);
1091         for(i=0; i<3; ++i)
1092                 glDeleteShader(screen->shaders[i]);
1093
1094         glXMakeContextCurrent(compositor->display, None, None, NULL);
1095         glXDestroyContext(compositor->display, screen->glx_context);
1096         glXDestroyWindow(compositor->display, screen->glx_window);
1097         XDestroyWindow(compositor->display, screen->render_window);
1098
1099         XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
1100
1101         free(screen->windows);
1102         free(screen->monitors);
1103 }
1104
1105 void shutdown_compositor(Compositor *compositor)
1106 {
1107         unsigned i;
1108
1109         for(i=0; i<compositor->nscreens; ++i)
1110                 shutdown_screen(compositor, &compositor->screens[i]);
1111         free(compositor->screens);
1112
1113         XDestroyWindow(compositor->display, compositor->selection_window);
1114
1115         XCloseDisplay(compositor->display);
1116 }
1117
1118 void mark_dirty(Compositor *compositor, CompositedScreen *screen)
1119 {
1120         compositor->dirty = 1;
1121         screen->dirty = 1;
1122 }
1123
1124 void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
1125 {
1126         CompositedScreen *screen;
1127
1128         if((screen = find_screen_by_root(compositor, event->parent)))
1129                 add_window(compositor, screen, event->window);
1130 }
1131
1132 void process_destroy_window_event(Compositor *compositor, XDestroyWindowEvent *event)
1133 {
1134         CompositedScreen *screen;
1135         CompositedWindow *window;
1136
1137         if((screen = find_screen_by_root(compositor, event->event)))
1138                 if((window = find_window(screen, event->window)))
1139                 {
1140                         use_gl(compositor, screen);
1141                         remove_window(compositor, screen, window, 1);
1142                 }
1143 }
1144
1145 void process_map_event(Compositor *compositor, XMapEvent *event)
1146 {
1147         CompositedScreen *screen;
1148         CompositedWindow *window;
1149
1150         screen = find_screen_by_root(compositor, event->event);
1151         if(!screen)
1152                 return;
1153
1154         window = find_window(screen, event->window);
1155         if(!window)
1156                 return;
1157
1158         window->map_state = IsViewable;
1159         window->recreate_pixmap = 1;
1160         window->recreate_mask = 1;
1161
1162         mark_dirty(compositor, screen);
1163 }
1164
1165 void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
1166 {
1167         CompositedScreen *screen;
1168         CompositedWindow *window;
1169
1170         screen = find_screen_by_root(compositor, event->event);
1171         if(!screen)
1172                 return;
1173
1174         window = find_window(screen, event->window);
1175         if(window)
1176                 window->map_state = IsUnviewable;
1177
1178         mark_dirty(compositor, screen);
1179 }
1180
1181 void process_reparent_event(Compositor *compositor, XReparentEvent *event)
1182 {
1183         CompositedScreen *screen;
1184         CompositedWindow *window;
1185
1186         screen = find_screen_by_root(compositor, event->event);
1187         if(!screen)
1188                 return;
1189
1190         if(event->parent==screen->root)
1191                 window = add_window(compositor, screen, event->window);
1192         else
1193         {
1194                 window = find_window(screen, event->window);
1195                 if(!window)
1196                         return;
1197
1198                 remove_window(compositor, screen, window, 0);
1199         }
1200
1201         if(window && window->map_state==IsViewable)
1202                 mark_dirty(compositor, screen);
1203 }
1204
1205 void process_configure_event(Compositor *compositor, XConfigureEvent *event)
1206 {
1207         CompositedScreen *screen;
1208         CompositedWindow *window;
1209
1210         screen = find_screen_by_root(compositor, event->event);
1211         if(!screen)
1212                 return;
1213
1214         window = find_window(screen, event->window);
1215         if(!window)
1216                 return;
1217
1218         window->x = event->x;
1219         window->y = event->y;
1220         if((unsigned)event->width!=window->width || (unsigned)event->height!=window->height || (unsigned)event->border_width!=window->border)
1221         {
1222                 window->width = event->width;
1223                 window->height = event->height;
1224                 window->border = event->border_width;
1225                 window->recreate_pixmap = 1;
1226         }
1227         reorder_window(screen, window, event->above);
1228
1229         if(window->map_state==IsViewable)
1230                 mark_dirty(compositor, screen);
1231 }
1232
1233 void process_property_event(Compositor *compositor, XPropertyEvent *event)
1234 {
1235         CompositedScreen *screen;
1236
1237         screen = find_screen_by_root(compositor, event->window);
1238         if(!screen)
1239                 return;
1240
1241         if(event->atom==compositor->correction_atom)
1242                 update_geometry_correction(compositor, screen);
1243         else if(event->atom==compositor->root_pmap_atom)
1244                 update_root_pixmap(compositor, screen);
1245 }
1246
1247 void process_selection_request_event(Compositor *compositor, XSelectionRequestEvent *event)
1248 {
1249         Atom prop_type;
1250         int prop_format;
1251         unsigned long overflow;
1252         unsigned long names_length;
1253         XSelectionEvent notify;
1254
1255         if(event->selection==compositor->geometry_data_atom)
1256         {
1257                 char *monitor_name;
1258                 CompositedMonitor *monitor;
1259
1260                 XGetWindowProperty(compositor->display, event->requestor, event->property, 0, 64, False, XA_STRING,
1261                         &prop_type, &prop_format, &names_length, &overflow, (unsigned char **)&monitor_name);
1262                 if(prop_type!=XA_STRING || prop_format!=8)
1263                         return;
1264
1265                 monitor = find_monitor_by_name_global(compositor, monitor_name, NULL);
1266                 if(monitor && monitor->enabled)
1267                         XChangeProperty(compositor->display, event->requestor, event->property, XA_INTEGER, 16, PropModeReplace, (unsigned char *)monitor->geometry_data, monitor->geometry_data_size);
1268
1269                 notify.type = SelectionNotify;
1270                 notify.requestor = event->requestor;
1271                 notify.selection = event->selection;
1272                 notify.target = event->target;
1273                 notify.property = (monitor ? event->property : None);
1274                 notify.time = event->time;
1275                 XSendEvent(compositor->display, event->requestor, False, 0, (XEvent *)&notify);
1276         }
1277 }
1278
1279 void process_damage_event(Compositor *compositor, XDamageNotifyEvent *event)
1280 {
1281         CompositedScreen *screen;
1282         CompositedWindow *window;
1283
1284         window = find_window_global(compositor, event->drawable, &screen);
1285         if(window && window->map_state==IsViewable)
1286                 mark_dirty(compositor, screen);
1287 }
1288
1289 void process_shape_event(Compositor *compositor, XShapeEvent *event)
1290 {
1291         CompositedScreen *screen;
1292         CompositedWindow *window;
1293
1294         if(event->kind!=ShapeBounding)
1295                 return;
1296
1297         window = find_window_global(compositor, event->window, &screen);
1298         if(window && window->map_state==IsViewable)
1299         {
1300                 window->recreate_mask = 1;
1301                 mark_dirty(compositor, screen);
1302         }
1303 }
1304
1305 void process_events(Compositor *compositor)
1306 {
1307         int pending;
1308         XEvent event;
1309
1310         pending = 0;
1311         while((pending || !compositor->dirty) && !terminate_requested)
1312         {
1313                 if(!pending)
1314                         pending = XPending(compositor->display);
1315
1316                 XNextEvent(compositor->display, &event);
1317                 if(pending)
1318                         --pending;
1319
1320                 switch(event.type)
1321                 {
1322                 case CreateNotify:
1323                         process_create_window_event(compositor, &event.xcreatewindow);
1324                         break;
1325                 case DestroyNotify:
1326                         process_destroy_window_event(compositor, &event.xdestroywindow);
1327                         break;
1328                 case MapNotify:
1329                         process_map_event(compositor, &event.xmap);
1330                         break;
1331                 case UnmapNotify:
1332                         process_unmap_event(compositor, &event.xunmap);
1333                         break;
1334                 case ReparentNotify:
1335                         process_reparent_event(compositor, &event.xreparent);
1336                         break;
1337                 case ConfigureNotify:
1338                         process_configure_event(compositor, &event.xconfigure);
1339                         break;
1340                 case PropertyNotify:
1341                         process_property_event(compositor, &event.xproperty);
1342                         break;
1343                 case SelectionRequest:
1344                         process_selection_request_event(compositor, &event.xselectionrequest);
1345                         break;
1346                 default:
1347                         if(event.type==compositor->damage_event+XDamageNotify)
1348                                 process_damage_event(compositor, (XDamageNotifyEvent *)&event);
1349                         else if(event.type==compositor->shape_event+ShapeNotify)
1350                                 process_shape_event(compositor, (XShapeEvent *)&event);
1351                         else
1352                                 printf("Event %d\n", event.type);
1353                 }
1354         }
1355 }
1356
1357 void refresh_screen(Compositor *compositor, CompositedScreen *screen)
1358 {
1359         unsigned i;
1360         int use_mask;
1361
1362         for(i=0; i<screen->nwindows; ++i)
1363                 if(screen->windows[i].map_state==IsViewable)
1364                         XDamageSubtract(compositor->display, screen->windows[i].damage, None, None);
1365         glXWaitX();
1366
1367         use_gl(compositor, screen);
1368
1369         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1370         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, screen->framebuffer);
1371
1372         use_mask = -1;
1373         glBindVertexArray(screen->window_vertex_array);
1374
1375         if(screen->root_pixmap)
1376         {
1377                 use_mask = 0;
1378                 glUseProgram(screen->program);
1379                 glBindTexture(GL_TEXTURE_2D, screen->root_texture);
1380                 compositor->glXBindTexImageEXT(compositor->display, screen->root_glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
1381                 glUniform4f(screen->geometry_loc, 0.0f, 0.0f, 1.0f, 1.0f);
1382                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1383                 compositor->glXReleaseTexImageEXT(compositor->display, screen->root_glx_pixmap, GLX_FRONT_LEFT_EXT);
1384         }
1385         else
1386                 glClear(GL_COLOR_BUFFER_BIT);
1387
1388         for(i=0; i<screen->nwindows; ++i)
1389         {
1390                 CompositedWindow *window;
1391
1392                 window = &screen->windows[i];
1393                 if(window->map_state!=IsViewable)
1394                         continue;
1395
1396                 if(window->use_mask!=use_mask)
1397                 {
1398                         use_mask = window->use_mask;
1399                         glUseProgram(use_mask ? screen->masked_program : screen->program);
1400                 }
1401
1402                 if(window->use_mask)
1403                 {
1404                         glActiveTexture(GL_TEXTURE1);
1405                         glBindTexture(GL_TEXTURE_2D, window->mask_texture);
1406                         glActiveTexture(GL_TEXTURE0);
1407                 }
1408
1409                 if(window->recreate_pixmap)
1410                         create_window_pixmap(compositor, screen, window);
1411                 if(window->recreate_mask)
1412                         update_window_mask(compositor, window);
1413
1414                 glBindTexture(GL_TEXTURE_2D, window->texture);
1415                 compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
1416                 glUniform4f((use_mask ? screen->masked_geometry_loc : screen->geometry_loc),
1417                         (float)window->x/screen->width, 1.0f-(float)(window->y+window->height)/screen->height,
1418                         (float)(window->width+2*window->border)/screen->width, (float)(window->height+2*window->border)/screen->height);
1419                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1420                 compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
1421         }
1422
1423         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
1424         glClear(GL_COLOR_BUFFER_BIT);
1425         glBindTexture(GL_TEXTURE_2D, screen->fb_texture);
1426         glEnable(GL_PRIMITIVE_RESTART);
1427         glPrimitiveRestartIndex(0xFFFF);
1428         if(use_mask)
1429                 glUseProgram(screen->program);
1430
1431         for(i=0; i<screen->nmonitors; ++i)
1432         {
1433                 CompositedMonitor *monitor = &screen->monitors[i];
1434                 if(!monitor->enabled)
1435                         continue;
1436
1437                 glUniform4f(screen->geometry_loc,
1438                         (float)monitor->x/screen->width, 1.0f-(float)(monitor->y+monitor->height)/screen->height,
1439                         (float)monitor->width/screen->width, (float)monitor->height/screen->height);
1440
1441                 glBindVertexArray(monitor->vertex_array);
1442                 glDrawElements(GL_TRIANGLE_STRIP, monitor->nelements, GL_UNSIGNED_SHORT, NULL);
1443         }
1444
1445         glBindVertexArray(0);
1446
1447         glXSwapBuffers(compositor->display, screen->glx_window);
1448
1449         screen->dirty = 0;
1450 }
1451
1452 void refresh_all_screens(Compositor *compositor)
1453 {
1454         unsigned i;
1455
1456         for(i=0; i<compositor->nscreens; ++i)
1457         {
1458                 CompositedScreen *screen = &compositor->screens[i];
1459                 if(screen->dirty)
1460                         refresh_screen(compositor, screen);
1461         }
1462
1463         compositor->dirty = 0;
1464 }
1465
1466 void sighandler(int sig)
1467 {
1468         terminate_requested = 1;
1469         (void)sig;
1470 }
1471
1472 int main()
1473 {
1474         Compositor compositor;
1475
1476         signal(SIGINT, &sighandler);
1477         signal(SIGTERM, &sighandler);
1478
1479         if(!initialize_compositor(&compositor))
1480                 return 1;
1481
1482         while(!terminate_requested)
1483         {
1484                 process_events(&compositor);
1485                 if(compositor.dirty && !terminate_requested)
1486                         refresh_all_screens(&compositor);
1487         }
1488
1489         shutdown_compositor(&compositor);
1490
1491         return 0;
1492 }