the code is more or less there. have to figure out how to find the fbconfigs right...
[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 <assert.h>
10 #include <stdlib.h>
11
12 #include <xcb/glx.h>
13 //#include <GL/glext.h>
14 //#include <GL/glxext.h>
15 #include <GL/glxtokens.h>
16 #include <GL/gl.h>
17
18 #define MAX_DEPTH 32
19
20 static int plugin_id;
21
22 typedef struct {
23     void (*screen_paint)(d_screen_t *sc);
24     void (*screen_root_pixmap_change)(d_screen_t *sc);
25     void (*window_show)(d_window_t *w);
26     void (*window_zombie_dead)(d_window_t *w);
27     void (*window_resize)(d_window_t *w);
28     void (*window_reshape)(d_window_t *w);
29
30     uint16_t shadowalpha;
31     int xshadowoff;
32     int yshadowoff;
33
34     uint32_t fbconfig[MAX_DEPTH + 1];
35
36     xcb_glx_context_t context;
37     xcb_glx_context_tag_t context_tag;
38 } data_t;
39
40 typedef struct {
41     GLuint texname;
42     xcb_glx_pixmap_t glpixmap;
43 } window_data_t;
44
45 static uint32_t glxrender_visual_info(uint32_t *props, int vis, int numprops,
46                                       uint32_t name);
47 static gboolean glxrender_check_visual(d_screen_t *sc);
48 static gboolean glxrender_find_fb_config(d_screen_t *sc, data_t *d);
49
50 static void glxrender_paint(d_screen_t *sc);
51 static void glxrender_root_pixmap_change(d_screen_t *sc);
52 static void paint_root(d_screen_t *sc, data_t *d);
53 static void paint_window(d_window_t *window, data_t *d, window_data_t *wd,
54                          gboolean opaque, int x, int y, int width,
55                          int height, int bwidth);
56 static void paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
57                          int x, int y, int width, int height, int bwidth);
58 static void glxrender_update_window_pixmap(d_window_t *w, data_t *d,
59                                            window_data_t *wd);
60 static void glxrender_free_window_pixmap(d_window_t *w, window_data_t *wd);
61 static void glxrender_update_root_pixmap(d_screen_t *sc, data_t *d);
62
63 static void glxrender_window_show(d_window_t *window);
64 static void glxrender_window_zombie_dead(d_window_t *window);
65 static void glxrender_window_resize(d_window_t *window);
66 static void glxrender_window_reshape(d_window_t *window);
67
68 void
69 glxrender_init(d_screen_t *sc, int id)
70 {
71     xcb_void_cookie_t ck;
72     xcb_generic_error_t *err;
73     xcb_glx_make_current_cookie_t curck;
74     xcb_glx_make_current_reply_t *currep;
75
76     plugin_id = id;
77
78     data_t *d = malloc(sizeof(data_t));
79     d->screen_paint = sc->screen_paint;
80     d->screen_root_pixmap_change = sc->screen_root_pixmap_change;
81     d->window_show = sc->window_show;
82     d->window_zombie_dead = sc->window_zombie_dead;
83     d->window_resize = sc->window_resize;
84     d->window_reshape = sc->window_reshape;
85     screen_add_plugin_data(sc, plugin_id, d);
86
87     sc->screen_paint = glxrender_paint;
88     sc->screen_root_pixmap_change = glxrender_root_pixmap_change;
89     sc->window_show = glxrender_window_show;
90     sc->window_zombie_dead = glxrender_window_zombie_dead;
91     sc->window_resize = glxrender_window_resize;
92     sc->window_reshape = glxrender_window_reshape;
93
94     d->shadowalpha = 0x3333; /* 20% */
95     d->xshadowoff = 2;
96     d->yshadowoff = 2;
97
98     if (!glxrender_check_visual(sc)) {
99         printf("unable to use the overlay window for GLX\n");
100         exit(1);
101     }
102
103     if (!(glxrender_find_fb_config(sc, d))) {
104         printf("unable to find FB configs\n");
105         exit(1);
106     }
107
108     d->context = xcb_generate_id(sc->dpy->conn);
109     ck = xcb_glx_create_context_checked(sc->dpy->conn, d->context,
110                                         sc->overlay_visual, sc->num,
111                                         XCB_NONE, TRUE);
112     if ((err = xcb_request_check(sc->dpy->conn, ck))) {
113         printf("context creation failed\n");
114         display_error(sc->dpy, err);
115         free(err);
116         exit(1);
117     }
118
119     curck = xcb_glx_make_current(sc->dpy->conn,
120                                  sc->overlay, d->context, XCB_NONE);
121     currep = xcb_glx_make_current_reply(sc->dpy->conn, curck, &err);
122     if (!currep) {
123         if (err) {
124             display_error(sc->dpy, err);
125             free(err);
126         }
127         printf("make current failed\n");
128         exit(1);
129     }
130     d->context_tag = currep->context_tag;
131     free(currep);
132
133     glViewport(0, 0, sc->super.width_in_pixels, sc->super.height_in_pixels);
134     glMatrixMode(GL_PROJECTION);
135     glLoadIdentity();
136     glOrtho(0, sc->super.width_in_pixels, sc->super.height_in_pixels,
137             0.0, -1.0, 100.0);
138     glMatrixMode(GL_MODELVIEW);
139     glLoadIdentity();
140     glClear(GL_COLOR_BUFFER_BIT);
141     glEnable(GL_TEXTURE_2D);
142     xcb_glx_swap_buffers(sc->dpy->conn, d->context_tag, sc->overlay);
143     glClearColor(0.4, 0.4, 0.4, 1.0);
144
145 /*
146   glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);                          
147   glEnable(GL_BLEND);
148 */
149 }
150
151 static uint32_t
152 glxrender_visual_info(uint32_t *props, int vis, int numprops, uint32_t name)
153 {
154     int s;
155
156     assert(numprops > 14);
157
158     /*
159     for (s = vis * numprops; s < vis*numprops + numprops; ++s)
160         printf("%x ", props[s]);
161     printf("\n");
162     */
163
164     s = vis * numprops;
165     switch (name) {
166     case GLX_VISUAL_ID:     return props[s+0];  /* id number */
167     case GLX_X_VISUAL_TYPE: return props[s+1];  /* XCB_CLASS_TRUE_COLOR etc */
168     case GLX_USE_GL:        return props[s+2];  /* boolean */
169     case GLX_RED_SIZE:      return props[s+3];  /* number */
170     case GLX_GREEN_SIZE:    return props[s+4];  /* number */
171     case GLX_BLUE_SIZE:     return props[s+5];  /* number */
172     case GLX_ALPHA_SIZE:    return props[s+6];  /* number */
173     case GLX_DOUBLEBUFFER:  return props[s+11]; /* boolean */
174     case GLX_DEPTH_SIZE:    return props[s+14]; /* number */
175     case GLX_STENCIL_SIZE:  return props[s+15]; /* number */
176     default: assert(0);
177     }
178 }
179
180 static gboolean
181 glxrender_check_visual(d_screen_t *sc)
182 {
183     xcb_glx_get_visual_configs_cookie_t ck;
184     xcb_glx_get_visual_configs_reply_t *rep;
185     gboolean ok = FALSE;
186
187     ck = xcb_glx_get_visual_configs_unchecked(sc->dpy->conn, sc->num);
188     rep = xcb_glx_get_visual_configs_reply(sc->dpy->conn, ck, NULL);
189     if (rep) {
190         uint32_t *props;
191         unsigned int i, nprops;
192
193 /*
194         static int config[] = {
195             GLX_DEPTH_SIZE, 1,
196             GLX_DOUBLEBUFFER,
197             GLX_RGBA,
198             XCB_NONE
199         };
200 */
201
202         props = xcb_glx_get_visual_configs_property_list(rep);
203         nprops = rep->num_properties;
204
205         for (i = 0; i < rep->num_visuals; ++i) {
206             /* look for the overlay's visual */
207             if (glxrender_visual_info(props, i, nprops, GLX_VISUAL_ID) !=
208                 sc->overlay_visual)
209             {
210                 continue;
211             }
212
213             if (!glxrender_visual_info(props, i, nprops, GLX_USE_GL)) {
214                 printf("overlay visual does not support GL\n");
215                 break;
216             }
217
218             if (!glxrender_visual_info(props, i, nprops, GLX_DOUBLEBUFFER)) {
219                 printf("overlay visual is not double buffered\n");
220                 break;
221             }
222
223             ok = TRUE; /* yippa ! */
224         }
225
226         free(rep);
227     }
228     return ok;
229 }
230
231 static uint32_t
232 glxrender_fbconfig_info(uint32_t *props, int con, int numprops, uint32_t name)
233 {
234     int i;
235
236     for (i = 0; i < numprops; ++i) {
237         if (props[i*2 + con*numprops*2] == name)
238             return props[i*2 + con*numprops*2 + 1];
239     }
240     return 0;
241 }
242
243 static gboolean
244 glxrender_find_fb_config(d_screen_t *sc, data_t *d)
245 {
246     xcb_glx_get_visual_configs_cookie_t vck;
247     xcb_glx_get_visual_configs_reply_t *vrep;
248     xcb_glx_get_fb_configs_cookie_t fbck;
249     xcb_glx_get_fb_configs_reply_t *fbrep;
250     xcb_depth_iterator_t depth_it;
251     unsigned int i, nvprops, nfbprops;
252     uint32_t *vprops, *fbprops;
253     uint32_t db, stencil, depthsize;
254
255     vck = xcb_glx_get_visual_configs_unchecked(sc->dpy->conn, sc->num);
256     vrep = xcb_glx_get_visual_configs_reply(sc->dpy->conn, vck, NULL);
257     if (!vrep) return FALSE;
258
259     fbck = xcb_glx_get_fb_configs(sc->dpy->conn, sc->num);
260     fbrep = xcb_glx_get_fb_configs_reply(sc->dpy->conn, fbck, NULL);
261     if (!fbrep) return FALSE;
262
263     vprops = xcb_glx_get_visual_configs_property_list(vrep);
264     nvprops = vrep->num_properties;
265     fbprops = xcb_glx_get_fb_configs_property_list(fbrep);
266     nfbprops = fbrep->num_properties;
267
268     for (i = 0; i <= MAX_DEPTH; ++i)
269         d->fbconfig[i] = 0;
270
271     db = 32767;
272     stencil = 32767;
273     depthsize = 32767;
274     depth_it = xcb_screen_allowed_depths_iterator(&sc->super);
275     for (; depth_it.rem; xcb_depth_next(&depth_it)) {
276         uint32_t vid;
277         int j;
278         unsigned int k;
279         xcb_visualtype_t *visuals;
280         int nvisuals;
281
282         vid = 0;
283         if (depth_it.data->depth > MAX_DEPTH) continue;
284
285         printf("looking for depth %d\n", depth_it.data->depth);
286
287         visuals = xcb_depth_visuals(depth_it.data);
288         nvisuals = xcb_depth_visuals_length(depth_it.data);
289
290         /* pick the nicest visual for the depth */
291         for (j = 0; j < nvisuals; ++j) {
292             uint32_t val;
293
294             /* find the visual's properties */
295             for (k = 0; k < vrep->num_visuals; ++k)
296                 if (glxrender_visual_info(vprops, k, nvprops, GLX_VISUAL_ID)
297                     == visuals[j].visual_id)
298                 {
299                     break;
300                 }
301             if (k == vrep->num_visuals) continue;
302
303             val = glxrender_visual_info(vprops, k, nvprops, GLX_USE_GL);
304             if (!val) continue;
305
306             val = glxrender_visual_info(vprops, k, nvprops, GLX_DOUBLEBUFFER);
307             if (!val > db) continue;
308             db = val;
309
310             val = glxrender_visual_info(vprops, k, nvprops, GLX_STENCIL_SIZE);
311             if (!val > stencil) continue;
312             stencil = val;
313
314             val = glxrender_visual_info(vprops, k, nvprops, GLX_DEPTH_SIZE);
315             if (!val > depthsize) continue;
316             depthsize = val;
317
318             /* use this visual */
319             vid = visuals[j].visual_id;
320         }
321
322         if (!vid) continue;
323
324         printf("found visual for depth %d\n", depth_it.data->depth);
325
326         for (k = 0; k < fbrep->num_FB_configs; ++k) {
327             uint32_t val;
328
329             //printf("root visual 0x%x\n", sc->super.root_visual);
330             //printf("overlay visual 0x%x\n", sc->overlay_visual);
331
332             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
333                                           GLX_VISUAL_ID);
334             //printf("x visual 0x%x\n", val);
335             if (val != vid) continue;
336
337             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
338                                           GLX_DOUBLEBUFFER);
339             //printf("dbl buffer %s\n", val ? "yes" : "no");
340             if (db) continue;
341
342             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
343                                           GLX_DEPTH_SIZE);
344             //printf("depth size %d\n", val);
345
346             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
347                                           GLX_RED_SIZE);
348             //printf("red size %d\n", val);
349             if (!val) continue;
350
351             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
352                                           GLX_GREEN_SIZE);
353             //printf("green size %d\n", val);
354             if (!val) continue;
355
356             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
357                                           GLX_BLUE_SIZE);
358             //printf("blue size %d\n", val);
359             if (!val) continue;
360
361             val = glxrender_fbconfig_info(fbprops, k, nfbprops,
362                                           GLX_ALPHA_SIZE);
363             //printf("alpha size %d\n", val);
364             if (!val) continue;
365
366             val = glxrender_fbconfig_info(fbprops, j, nfbprops,
367                                           GLX_RENDER_TYPE);
368             //printf("rgba bit %s\n", val & GLX_RGBA_BIT ? "yes" : "no");
369             if (!(val & GLX_RGBA_BIT)) continue;
370
371             val = glxrender_fbconfig_info(fbprops, j, nfbprops,
372                                           GLX_CONFIG_CAVEAT);
373             //printf("caveat 0x%x\n", val);
374
375             val = glxrender_fbconfig_info(fbprops, j, nfbprops,
376                                           GLX_BIND_TO_TEXTURE_RGBA_EXT);
377             //printf("bind ext %s\n", val ? "yes" : "no");
378             if (!val) continue;
379
380             d->fbconfig[depth_it.data->depth] =
381                 glxrender_fbconfig_info(fbprops, i, nfbprops, GLX_FBCONFIG_ID);
382             break;
383         }
384     }
385
386     free(vrep);
387     free(fbrep);
388     return TRUE;
389 }
390
391 void
392 glxrender_free(d_screen_t *sc)
393 {
394     data_t *d = screen_find_plugin_data(sc, plugin_id);
395     free(d);
396     screen_remove_plugin_data(sc, plugin_id);
397 }
398
399 int
400 glxrender_next_timeout(struct d_screen *sc, struct timeval *tv)
401 {
402     (void)sc;
403     (void)tv;
404     return FALSE;
405 }
406
407 void
408 glxrender_timeout(struct d_screen *sc, const struct timeval *now)
409 {
410     (void)sc; (void)now;
411 }
412
413 void
414 glxrender_window_free_data(d_window_t *w, window_data_t *wd)
415 {
416     glxrender_free_window_pixmap(w, wd);
417     glDeleteTextures(1, &wd->texname);
418     free(wd);
419 }
420
421 static void
422 glxrender_window_show(d_window_t *w)
423 {
424     data_t *d;
425     window_data_t *wd;
426
427     d = screen_find_plugin_data(w->sc, plugin_id);
428
429     /* pass it on */
430     d->window_show(w);
431
432     wd = window_find_plugin_data(w, plugin_id);
433     if (wd)
434         glxrender_window_free_data(w, wd);
435    
436     wd = malloc(sizeof(window_data_t));
437     glGenTextures(1, &wd->texname);
438
439     window_add_plugin_data(w, plugin_id, wd);
440 }
441
442 static void
443 glxrender_window_zombie_dead(d_window_t *w)
444 {
445     data_t *d;
446     window_data_t *wd;
447
448     d = screen_find_plugin_data(w->sc, plugin_id);
449     wd = window_find_plugin_data(w, plugin_id);
450     if (wd) {
451         glxrender_window_free_data(w, wd);
452         window_remove_plugin_data(w, plugin_id);
453     }
454
455     /* pass it on */
456     d->window_zombie_dead(w);
457 }
458
459 static void
460 glxrender_free_window_pixmap(d_window_t *w, window_data_t *wd)
461 {
462     /* this might cause an error, oh well */
463     if (wd->glpixmap) {
464         glBindTexture(GL_TEXTURE_2D, wd->texname);
465
466         //d->releaseTexImageEXT(obt_display, lw->glpixmap,
467         //                      GLX_FRONT_LEFT_EXT);
468
469         glBindTexture(GL_TEXTURE_2D, 0);
470
471         xcb_glx_destroy_glx_pixmap(w->sc->dpy->conn, wd->glpixmap);
472         wd->glpixmap = XCB_NONE;
473     }
474 }
475
476 static void
477 glxrender_update_window_pixmap(d_window_t *w, data_t *d, window_data_t *wd)
478 {
479     xcb_pixmap_t px;
480     uint8_t depth;
481
482     static const uint32_t attrs[] = {
483         GLX_TEXTURE_FORMAT_EXT,
484         GLX_TEXTURE_FORMAT_RGBA_EXT
485     };
486
487     px = window_get_pixmap(w);
488     depth = window_get_depth(w);
489     if (px && d->fbconfig[depth]) {
490         wd->glpixmap = xcb_generate_id(w->sc->dpy->conn);
491         xcb_glx_create_pixmap(w->sc->dpy->conn, w->sc->num, d->fbconfig[depth],
492                               px, wd->glpixmap, 2, attrs);
493
494         glBindTexture(GL_TEXTURE_2D, wd->texname);
495
496         {
497             unsigned int len = 2 * sizeof(uint32_t);
498             uint32_t data[] = {wd->glpixmap, GLX_FRONT_LEFT_EXT};
499             xcb_void_cookie_t ck;
500             xcb_generic_error_t *err;
501
502             ck = xcb_glx_vendor_private(w->sc->dpy->conn,
503                                         GLX_BIND_TO_TEXTURE_RGBA_EXT,
504                                         d->context_tag,
505                                         len, data);
506             if ((err = xcb_request_check(w->sc->dpy->conn, ck))) {
507                 display_error(w->sc->dpy, err);
508                 free(err);
509             }
510         }
511         //d->bindTexImageEXT(obt_display, lw->glpixmap,
512         //                   GLX_FRONT_LEFT_EXT, NULL);
513
514         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
515         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
516
517         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
518         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
519         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
520
521         glBindTexture(GL_TEXTURE_2D, 0);
522     }
523 }
524
525 static void
526 glxrender_update_root_pixmap(d_screen_t *sc, data_t *d)
527 {
528     xcb_pixmap_t px;
529
530     px = screen_get_root_pixmap(sc);
531     if (px) {
532     }
533 }
534
535 static void
536 glxrender_window_resize(d_window_t *w)
537 {
538     data_t *d;
539     window_data_t *wd;
540
541     d = screen_find_plugin_data(w->sc, plugin_id);
542     wd = window_find_plugin_data(w, plugin_id);
543
544     /* pass it on */
545     d->window_resize(w);
546
547     assert(wd != NULL);
548     glxrender_free_window_pixmap(w, wd);
549 }
550
551 static void
552 glxrender_window_reshape(d_window_t *w)
553 {
554     data_t *d;
555     window_data_t *wd;
556
557     d = screen_find_plugin_data(w->sc, plugin_id);
558     wd = window_find_plugin_data(w, plugin_id);
559
560     /* pass it on */
561     d->window_reshape(w);
562
563     assert(wd != NULL);
564     glxrender_free_window_pixmap(w, wd);
565 }
566
567 static void
568 glxrender_root_pixmap_change(d_screen_t *sc)
569 {
570     data_t *d;
571
572     d = screen_find_plugin_data(sc, plugin_id);
573     int a; /* XXX free things here */
574     //if (d->root_picture) {
575     //    xcb_render_free_picture(sc->dpy->conn, d->root_picture);
576     //    d->root_picture = XCB_NONE;
577     //}
578
579     /* pass it on */
580     d->screen_root_pixmap_change(sc);
581 }
582
583 static void
584 glxrender_paint(d_screen_t *sc)
585 {
586     data_t *d = screen_find_plugin_data(sc, plugin_id);
587     d_list_it_t *it;
588
589     printf("painting\n");
590
591     paint_root(sc, d);
592
593     for (it = list_bottom(sc->stacking); it; it = it->prev) {
594         d_window_t *w = it->data;
595
596         if (!window_is_input_only(w) &&
597             (window_is_mapped(w) || window_is_zombie(w)))
598         {
599             int x, y, width, height, bwidth;
600             gboolean opaque;
601             window_data_t *wd;
602
603             window_get_area(w, &x, &y, &width, &height, &bwidth);
604
605             if (!(x < sc->super.width_in_pixels &&
606                   y < sc->super.height_in_pixels &&
607                   (x + width > 0 || x + width + d->xshadowoff > 0) &&
608                   (y + height > 0 || y + height + d->yshadowoff > 0)))
609             {
610                 continue;
611             }
612
613             opaque = !window_is_argb(w) && window_get_opacity(w) == 0xffff;
614
615             wd = window_find_plugin_data(w, plugin_id);
616
617             paint_shadow(w, d, wd, x, y, width, height, bwidth);
618             paint_window(w, d, wd, opaque, x, y, width, height, bwidth);
619         }
620     }
621
622     xcb_glx_swap_buffers(sc->dpy->conn, d->context_tag, sc->overlay);
623
624     /* call the function we replaced in the chain */
625     d->screen_paint(sc);
626 }
627
628 static void
629 paint_root(d_screen_t *sc, data_t *d)
630 {
631     //if (!d->root_picture)
632     glxrender_update_root_pixmap(sc, d);
633
634     glClear(GL_COLOR_BUFFER_BIT);
635 }
636
637 static void
638 paint_window(d_window_t *w, data_t *d, window_data_t *wd, gboolean opaque,
639              int x, int y, int width, int height, int bwidth)
640 {
641     if (!wd->glpixmap)
642         glxrender_update_window_pixmap(w, d, wd);
643
644     glBegin(GL_QUADS);
645     glColor3f(1.0, 1.0, 1.0);
646     glVertex2i(x, y);
647     glTexCoord2f(1, 0);
648     glVertex2i(x + width + bwidth, y);
649     glTexCoord2f(1, 1);
650     glVertex2i(x + width + bwidth,
651                y + height + bwidth);
652     glTexCoord2f(0, 1);
653     glVertex2i(x, y + height + bwidth);
654     glTexCoord2f(0, 0);
655     glEnd();
656 }
657
658 static void
659 paint_shadow(d_window_t *w, data_t *d, window_data_t *wd,
660              int x, int y, int width, int height, int bwidth)
661 {
662 /*
663     xcb_render_composite(w->sc->dpy->conn,
664                          XCB_RENDER_PICT_OP_OVER,
665                          wd->shadow_picture,
666                          wd->picture,
667                          d->overlay_buffer,
668                          0, 0, 0, 0,
669                          x+d->xshadowoff, y+d->yshadowoff,
670                          width + bwidth*2, height + bwidth *2);
671 */
672 }