use translate to place windows, and draw them based on 0,0
[dana/dcompmgr.git] / glxrender.c
1 #include "efence.h"
2
3 #include "render.h"
4 #include "screen.h"
5 #include "window.h"
6 #include "display.h"
7 #include "list.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <stdlib.h>
12
13 #include <GL/gl.h>
14 #include <GL/glx.h>
15 #include <GL/glxtokens.h>
16 #include <GL/glext.h>
17 #include <GL/glxext.h>
18
19 #define MAX_DEPTH 32
20
21 static int plugin_id;
22
23 typedef void (*BindEXTFunc)(Display *, GLXDrawable, int, const int *);
24 typedef void (*ReleaseEXTFunc)(Display *, GLXDrawable, int);
25
26 typedef struct {
27     void (*screen_paint)(d_screen_t *sc);
28     void (*screen_root_pixmap_change)(d_screen_t *sc);
29     void (*window_show)(d_window_t *w);
30     void (*window_zombie_dead)(d_window_t *w);
31     void (*window_resize)(d_window_t *w);
32     void (*window_reshape)(d_window_t *w);
33
34     float shadowalpha;
35     int xshadowoff;
36     int yshadowoff;
37
38     GLXFBConfig fbconfig[MAX_DEPTH + 1];
39
40     GLXContext context;
41     GLuint root_texname;
42     GLXPixmap root_glpixmap;
43
44     BindEXTFunc bind_func;
45     ReleaseEXTFunc release_func;
46 } data_t;
47
48 typedef struct {
49     GLuint texname;
50     GLXPixmap glpixmap;
51 } window_data_t;
52
53 static gboolean glxrender_find_fb_config(d_screen_t *sc, data_t *d);
54
55 static void glxrender_paint(d_screen_t *sc);
56 static void glxrender_root_pixmap_change(d_screen_t *sc);
57 static void paint_root(d_screen_t *sc, data_t *d);
58 static void paint_window(d_window_t *window, data_t *d, window_data_t *wd,
59                          gboolean opaque, int x, int y, int width,
60                          int height, int bwidth);
61 static void paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
62                          int x, int y, int width, int height, int bwidth);
63 static void glxrender_update_window_pixmap(d_window_t *w, data_t *d,
64                                            window_data_t *wd);
65 static void glxrender_free_window_pixmap(d_window_t *w, data_t *d,
66                                          window_data_t *wd);
67 static void glxrender_free_root_pixmap(d_screen_t *sc, data_t *d);
68 static void glxrender_update_root_pixmap(d_screen_t *sc, data_t *d);
69
70 static void glxrender_window_show(d_window_t *window);
71 static void glxrender_window_zombie_dead(d_window_t *window);
72 static void glxrender_window_resize(d_window_t *window);
73 static void glxrender_window_reshape(d_window_t *window);
74
75 void
76 glxrender_init(d_screen_t *sc, int id)
77 {
78     static int context_visual_config[] = {
79         GLX_DEPTH_SIZE, 1,
80         GLX_DOUBLEBUFFER,
81         GLX_RGBA,
82         XCB_NONE
83     };
84     XVisualInfo *vi;
85     data_t *d;
86
87     plugin_id = id;
88
89     d = malloc(sizeof(data_t));
90     d->screen_paint = sc->screen_paint;
91     d->screen_root_pixmap_change = sc->screen_root_pixmap_change;
92     d->window_show = sc->window_show;
93     d->window_zombie_dead = sc->window_zombie_dead;
94     d->window_resize = sc->window_resize;
95     d->window_reshape = sc->window_reshape;
96     screen_add_plugin_data(sc, plugin_id, d);
97
98     sc->screen_paint = glxrender_paint;
99     sc->screen_root_pixmap_change = glxrender_root_pixmap_change;
100     sc->window_show = glxrender_window_show;
101     sc->window_zombie_dead = glxrender_window_zombie_dead;
102     sc->window_resize = glxrender_window_resize;
103     sc->window_reshape = glxrender_window_reshape;
104
105     d->shadowalpha = 0.2f; /* 20% */
106     d->xshadowoff = 2;
107     d->yshadowoff = 2;
108
109     vi = glXChooseVisual(sc->dpy->xlib_dpy, sc->num, context_visual_config);
110     if (!vi) {
111         printf("unable to find a valid double buffered GL context to use\n");
112         exit(1);
113     }
114
115     d->context = glXCreateContext(sc->dpy->xlib_dpy, vi, NULL, GL_TRUE);
116     glXMakeCurrent(sc->dpy->xlib_dpy, sc->overlay, d->context);
117
118
119     if (!(glxrender_find_fb_config(sc, d))) {
120         printf("unable to find FB configs\n");
121         exit(1);
122     }
123
124     glViewport(0, 0, sc->super->width_in_pixels, sc->super->height_in_pixels);
125     glMatrixMode(GL_PROJECTION);
126     glLoadIdentity();
127     glOrtho(0, sc->super->width_in_pixels, sc->super->height_in_pixels, 0,
128             -100.0, 100.0);
129     glMatrixMode(GL_MODELVIEW);
130     glLoadIdentity();
131     glEnable(GL_TEXTURE_2D);
132
133     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
134     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
135     glDisable(GL_BLEND);
136
137     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
138     glClear(GL_COLOR_BUFFER_BIT);
139     glXSwapBuffers(sc->dpy->xlib_dpy, sc->overlay);
140
141     d->bind_func = (BindEXTFunc)
142         glXGetProcAddress((const guchar*)"glXBindTexImageEXT");
143     d->release_func = (ReleaseEXTFunc)
144         glXGetProcAddress((const guchar*)"glXReleaseTexImageEXT");
145
146     glGenTextures(1, &d->root_texname);
147     d->root_glpixmap = XCB_NONE;
148 }
149
150 static gboolean
151 glxrender_find_fb_config(d_screen_t *sc, data_t *d)
152 {
153     static const int drawable_tfp_attrs[] = {
154         GLX_CONFIG_CAVEAT, GLX_NONE,
155         GLX_DOUBLEBUFFER, FALSE,
156         GLX_DEPTH_SIZE, 0,
157         GLX_RED_SIZE, 1,
158         GLX_GREEN_SIZE, 1,
159         GLX_BLUE_SIZE, 1,
160         GLX_ALPHA_SIZE, 1,
161         GLX_RENDER_TYPE, GLX_RGBA_BIT,
162         GLX_BIND_TO_TEXTURE_RGBA_EXT, TRUE, /* For TextureFromPixmap */
163         XCB_NONE
164     };
165     int db, stencil, depth, numfb, i;
166     GLXFBConfig *fbcons;
167     XVisualInfo tvis, *visinfo;
168
169     fbcons = glXChooseFBConfig(sc->dpy->xlib_dpy, sc->num,
170                                drawable_tfp_attrs, &numfb);
171     if (!fbcons) return FALSE;
172
173     for (i = 0; i <= MAX_DEPTH; i++) {
174         int j, count, value;
175         VisualID vid;
176
177         vid = 0;
178         d->fbconfig[i] = 0;
179         db = 32767;
180         stencil = 32767;
181         depth = 32767;
182
183         printf("looking for depth %d\n", i);
184
185         tvis.depth = i;
186         visinfo = XGetVisualInfo(sc->dpy->xlib_dpy, VisualDepthMask,
187                                  &tvis, &count);
188         /* pick the nicest visual for the depth */
189         for (j = 0; j < count; j++) {
190             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_USE_GL, &value);
191             if (!value)
192                 continue;
193
194             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_DOUBLEBUFFER,
195                          &value);
196             if (value > db)
197                 continue;
198             db = value;
199
200             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_STENCIL_SIZE,
201                          &value);
202             if (value > stencil)
203                 continue;
204             stencil = value;
205
206             glXGetConfig(sc->dpy->xlib_dpy, &visinfo[j], GLX_DEPTH_SIZE,
207                          &value);
208             if (value > depth)
209                 continue;
210             depth = value;
211
212             /* use this visual */
213             vid = visinfo[j].visualid;
214         }
215
216         if (!vid) continue;
217
218         /* look for an fbconfig for this visual */
219         for (j = 0; j < numfb; ++j) {
220             glXGetFBConfigAttrib(sc->dpy->xlib_dpy, fbcons[j],
221                                  GLX_VISUAL_ID, &value);
222             if ((unsigned)value == vid) {
223                 d->fbconfig[i] = fbcons[j];
224
225                 printf("found visual 0x%x fbconfig 0x%x for depth %d\n",
226                        (uint32_t)vid, (uint32_t)fbcons[j], i);
227                 break;
228             }
229         }
230     }
231
232     XFree(fbcons);
233     return TRUE;
234 }
235
236 void
237 glxrender_free(d_screen_t *sc)
238 {
239     data_t *d = screen_find_plugin_data(sc, plugin_id);
240     glxrender_free_root_pixmap(sc, d);
241     free(d);
242     screen_remove_plugin_data(sc, plugin_id);
243 }
244
245 int
246 glxrender_next_timeout(struct d_screen *sc, struct timeval *tv)
247 {
248     (void)sc;
249     (void)tv;
250     return FALSE;
251 }
252
253 void
254 glxrender_timeout(struct d_screen *sc, const struct timeval *now)
255 {
256     (void)sc; (void)now;
257 }
258
259 void
260 glxrender_window_free_data(d_window_t *w, data_t *d, window_data_t *wd)
261 {
262     glxrender_free_window_pixmap(w, d, wd);
263     glDeleteTextures(1, &wd->texname);
264     free(wd);
265 }
266
267 static void
268 glxrender_window_show(d_window_t *w)
269 {
270     data_t *d;
271     window_data_t *wd;
272
273     d = screen_find_plugin_data(w->sc, plugin_id);
274
275     /* pass it on */
276     d->window_show(w);
277
278     wd = window_find_plugin_data(w, plugin_id);
279     if (wd)
280         glxrender_window_free_data(w, d, wd);
281    
282     wd = malloc(sizeof(window_data_t));
283     glGenTextures(1, &wd->texname);
284     wd->glpixmap = XCB_NONE;
285
286     window_add_plugin_data(w, plugin_id, wd);
287 }
288
289 static void
290 glxrender_window_zombie_dead(d_window_t *w)
291 {
292     data_t *d;
293     window_data_t *wd;
294
295     d = screen_find_plugin_data(w->sc, plugin_id);
296     wd = window_find_plugin_data(w, plugin_id);
297     if (wd) {
298         glxrender_window_free_data(w, d, wd);
299         window_remove_plugin_data(w, plugin_id);
300     }
301
302     /* pass it on */
303     d->window_zombie_dead(w);
304 }
305
306 static void
307 glxrender_free_window_pixmap(d_window_t *w, data_t *d, window_data_t *wd)
308 {
309     /* this might cause an error, oh well */
310     if (wd->glpixmap) {
311         glBindTexture(GL_TEXTURE_2D, wd->texname);
312         d->release_func(w->sc->dpy->xlib_dpy,
313                         wd->glpixmap, GLX_FRONT_LEFT_EXT);
314         glBindTexture(GL_TEXTURE_2D, 0);
315
316         //xcb_glx_destroy_pixmap(w->sc->dpy->conn, wd->glpixmap);
317         glXDestroyPixmap(w->sc->dpy->xlib_dpy, wd->glpixmap);
318         wd->glpixmap = XCB_NONE;
319     }
320 }
321
322 static void
323 glxrender_free_root_pixmap(d_screen_t *sc, data_t *d)
324 {
325     /* this might cause an error, oh well */
326     if (d->root_glpixmap) {
327         glBindTexture(GL_TEXTURE_2D, d->root_texname);
328         d->release_func(sc->dpy->xlib_dpy,
329                         d->root_glpixmap, GLX_FRONT_LEFT_EXT);
330         glBindTexture(GL_TEXTURE_2D, 0);
331
332         //xcb_glx_destroy_pixmap(w->sc->dpy->conn, wd->glpixmap);
333         glXDestroyPixmap(sc->dpy->xlib_dpy, d->root_glpixmap);
334         d->root_glpixmap = XCB_NONE;
335     }
336 }
337
338 static void
339 glxrender_update_window_pixmap(d_window_t *w, data_t *d, window_data_t *wd)
340 {
341     xcb_pixmap_t px;
342     uint8_t depth;
343
344     static int attrs[] = {
345         GLX_TEXTURE_FORMAT_EXT,
346         XCB_NONE,
347         XCB_NONE
348     };
349
350     px = window_get_pixmap(w);
351     depth = window_get_depth(w);
352
353     if (!px) return;
354
355     if (!d->fbconfig[depth]) {
356         printf("no GL visual for depth %d\n", depth);
357         return;
358     }
359
360     if (window_is_argb(w))
361         attrs[1] = GLX_TEXTURE_FORMAT_RGBA_EXT;
362     else
363         attrs[1] = GLX_TEXTURE_FORMAT_RGB_EXT;
364
365     wd->glpixmap = glXCreatePixmap(w->sc->dpy->xlib_dpy,
366                                    d->fbconfig[depth],
367                                    px, attrs);
368
369
370     glBindTexture(GL_TEXTURE_2D, wd->texname);
371     d->bind_func(w->sc->dpy->xlib_dpy,
372                  wd->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
373
374     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
375     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
376
377     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
378     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
379
380     glBindTexture(GL_TEXTURE_2D, 0);
381 }
382
383 static void
384 glxrender_update_root_pixmap(d_screen_t *sc, data_t *d)
385 {
386     xcb_pixmap_t px;
387     static int attrs[] = {
388         GLX_TEXTURE_FORMAT_EXT,
389         GLX_TEXTURE_FORMAT_RGB_EXT,
390         XCB_NONE
391     };
392
393     px = screen_get_root_pixmap(sc);
394     if (!px) return;
395
396     if (!d->fbconfig[sc->super->root_depth]) {
397         printf("no GL visual for depth %d\n", sc->super->root_depth);
398         return;
399     }
400
401     d->root_glpixmap = glXCreatePixmap(sc->dpy->xlib_dpy,
402                                        d->fbconfig[sc->super->root_depth],
403                                        px, attrs);
404
405
406     glBindTexture(GL_TEXTURE_2D, d->root_texname);
407     d->bind_func(sc->dpy->xlib_dpy,
408                  d->root_glpixmap, GLX_FRONT_LEFT_EXT, NULL);
409
410     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
411     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
412
413     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
414     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
415
416     glBindTexture(GL_TEXTURE_2D, 0);
417 }
418
419 static void
420 glxrender_window_resize(d_window_t *w)
421 {
422     data_t *d;
423     window_data_t *wd;
424
425     d = screen_find_plugin_data(w->sc, plugin_id);
426     wd = window_find_plugin_data(w, plugin_id);
427
428     /* pass it on */
429     d->window_resize(w);
430
431     assert(wd != NULL);
432     glxrender_free_window_pixmap(w, d, wd);
433 }
434
435 static void
436 glxrender_window_reshape(d_window_t *w)
437 {
438     data_t *d;
439     window_data_t *wd;
440
441     d = screen_find_plugin_data(w->sc, plugin_id);
442     wd = window_find_plugin_data(w, plugin_id);
443
444     /* pass it on */
445     d->window_reshape(w);
446
447     assert(wd != NULL);
448     glxrender_free_window_pixmap(w, d, wd);
449 }
450
451 static void
452 glxrender_root_pixmap_change(d_screen_t *sc)
453 {
454     data_t *d;
455
456     d = screen_find_plugin_data(sc, plugin_id);
457     glxrender_free_root_pixmap(sc, d);
458
459     /* pass it on */
460     d->screen_root_pixmap_change(sc);
461 }
462
463 static void
464 glxrender_paint(d_screen_t *sc)
465 {
466     data_t *d = screen_find_plugin_data(sc, plugin_id);
467     d_list_it_t *it;
468
469     //printf("painting\n");
470
471     paint_root(sc, d);
472
473     for (it = list_bottom(sc->stacking); it; it = it->prev) {
474         d_window_t *w = it->data;
475
476         if (!window_is_input_only(w) &&
477             (window_is_mapped(w) || window_is_zombie(w)))
478         {
479             int x, y, width, height, bwidth;
480             gboolean opaque;
481             window_data_t *wd;
482
483             window_get_area(w, &x, &y, &width, &height, &bwidth);
484
485             if (!(x < sc->super->width_in_pixels &&
486                   y < sc->super->height_in_pixels &&
487                   (x + width > 0 || x + width + d->xshadowoff > 0) &&
488                   (y + height > 0 || y + height + d->yshadowoff > 0)))
489             {
490                 continue;
491             }
492
493             opaque = !window_is_argb(w) && window_get_opacity(w) == 0xffff;
494
495             wd = window_find_plugin_data(w, plugin_id);
496
497             glPushMatrix();
498             glTranslatef(x + d->xshadowoff, y + d->yshadowoff, 0.0f);
499
500             paint_shadow(w, d, wd, 0, 0, width, height, bwidth);
501             glTranslatef(-d->xshadowoff, -d->yshadowoff, 0.0f);
502             paint_window(w, d, wd, opaque, 0, 0, width, height, bwidth);
503
504             glPopMatrix();
505         }
506     }
507
508     //xcb_glx_swap_buffers(sc->dpy->conn, d->context_tag, sc->overlay);
509     glXSwapBuffers(sc->dpy->xlib_dpy, sc->overlay);
510
511     /* call the function we replaced in the chain */
512     d->screen_paint(sc);
513 }
514
515 static void
516 paint_root(d_screen_t *sc, data_t *d)
517 {
518     if (!d->root_glpixmap)
519         glxrender_update_root_pixmap(sc, d);
520
521     glClear(GL_COLOR_BUFFER_BIT);
522
523     glBindTexture(GL_TEXTURE_2D, d->root_texname);
524     glBegin(GL_QUADS);
525     glVertex2i(0, 0);
526     glTexCoord2f(1, 0);
527     glVertex2i(sc->super->width_in_pixels, 0);
528     glTexCoord2f(1, 1);
529     glVertex2i(sc->super->width_in_pixels, sc->super->height_in_pixels);
530     glTexCoord2f(0, 1);
531     glVertex2i(0, sc->super->height_in_pixels);
532     glTexCoord2f(0, 0);
533     glEnd();
534
535     glBindTexture(GL_TEXTURE_2D, 0);
536 }
537
538 static void
539 paint_window(d_window_t *w, data_t *d, window_data_t *wd, gboolean opaque,
540              int x, int y, int width, int height, int bwidth)
541 {
542     uint16_t o = window_get_opacity(w);
543
544     if (!wd->glpixmap)
545         glxrender_update_window_pixmap(w, d, wd);
546
547     glBindTexture(GL_TEXTURE_2D, wd->texname);
548
549     if (!opaque) {
550         glEnable(GL_BLEND);
551
552         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
553         glColor4us(o, o, o, o);
554     }
555
556     glBegin(GL_QUADS);
557     glTexCoord2f(0, 0);
558     glVertex2i(x, y);
559     glTexCoord2f(1, 0);
560     glVertex2i(x + width + bwidth, y);
561     glTexCoord2f(1, 1);
562     glVertex2i(x + width + bwidth,
563                y + height + bwidth);
564     glTexCoord2f(0, 1);
565     glVertex2i(x, y + height + bwidth);
566     glEnd();
567
568     glBindTexture(GL_TEXTURE_2D, 0);
569
570     if (!opaque) {
571         glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
572         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
573         glDisable(GL_BLEND);
574     }
575 }
576
577 static void
578 paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
579              int x, int y, int width, int height, int bwidth)
580 {
581     float alpha = d->shadowalpha;
582
583     alpha *= window_get_opacity(w);
584     alpha /= 0xffff;
585
586     if (alpha < 0.01) return;
587
588     glEnable(GL_BLEND);
589     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
590     /* black shadow */
591     glColor4f(0.0f, 0.0f, 0.0f, alpha);
592
593     if (window_is_argb(w)) {
594         /* shape the shadow to the window's alpha levels */
595         glBindTexture(GL_TEXTURE_2D, wd->texname);
596
597         glBegin(GL_QUADS);
598         glTexCoord2f(0, 0);
599         glVertex2i(x, y);
600         glTexCoord2f(1, 0);
601         glVertex2i(x + width + bwidth, y);
602         glTexCoord2f(1, 1);
603         glVertex2i(x + width + bwidth,
604                    y + height + bwidth);
605         glTexCoord2f(0, 1);
606         glVertex2i(x, y + height + bwidth);
607         glEnd();
608
609         glBindTexture(GL_TEXTURE_2D, 0);
610     }
611     else
612         /* draw a solid rectangle */
613         glRecti(x, y,
614                 x + width + bwidth - 1, y + height + bwidth - 1);
615
616     glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
617     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
618     glDisable(GL_BLEND);
619 }