Enable VSync (Fix bug 5296)
[dana/dcompmgr.git] / display.c
1 #include "efence.h"
2
3 #include "display.h"
4 #include "screen.h"
5 #include "list.h"
6 #include "gettext.h"
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <string.h>
10 #include <stdio.h>
11
12 #include <xcb/damage.h>
13 #include <xcb/render.h>
14 #include <xcb/xfixes.h>
15 #include <xcb/composite.h>
16 #include <xcb/glx.h>
17
18 typedef struct {
19     xcb_atom_t               *atom;
20     const char               *name;
21     xcb_intern_atom_cookie_t  ck;
22 } d_atom_query_t;
23
24 #define setup_extension(name) \
25     xcb_##name##_query_version_cookie_t ck_##name;
26
27 #define find_extension_xfixes(dpy) &dpy->xfixes;
28 #define find_extension_render(dpy) &dpy->render;
29 #define find_extension_composite(dpy) &dpy->composite;
30 #define find_extension_damage(dpy) &dpy->damage;
31 #define find_extension_shape(dpy) &dpy->shape;
32 #define find_extension_glx(dpy) &dpy->glx;
33
34 #define version_extension_xfixes \
35   XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MAJOR_VERSION
36 #define version_extension_render \
37   XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MAJOR_VERSION
38 #define version_extension_composite \
39   XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MAJOR_VERSION
40 #define version_extension_damage \
41   XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MAJOR_VERSION
42 #define version_extension_glx \
43   XCB_GLX_MAJOR_VERSION, XCB_GLX_MAJOR_VERSION
44
45 #define query_extension(dpy, name) \
46 { \
47     const xcb_query_extension_reply_t *rep;          \
48     d_display_ext_t *ext; \
49 \
50     xcb_prefetch_extension_data(dpy->conn, &xcb_##name##_id);  \
51     rep = xcb_get_extension_data(dpy->conn, &xcb_##name##_id); \
52 \
53     ext = find_extension_##name(dpy) \
54     ext->present = rep && rep->present;          \
55     ext->error   = rep ? rep->first_error : 0;   \
56     ext->event   = rep ? rep->first_event : 0;   \
57     ext->opcode  = rep ? rep->major_opcode : 0;  \
58 }
59
60 #define query_extension_version(dpy, name) \
61 { \
62     d_display_ext_t *ext; \
63 \
64     ext = find_extension_##name(dpy) \
65 \
66     if (ext->present) \
67         ck_##name = xcb_##name##_query_version(dpy->conn, \
68                                                version_extension_##name); \
69 }
70
71 #define query_extension_version_no_client_version(dpy, name) \
72 { \
73     d_display_ext_t *ext; \
74 \
75     ext = find_extension_##name(dpy) \
76 \
77     if (ext->present) \
78         ck_##name = xcb_##name##_query_version(dpy->conn); \
79 }
80
81 #define reply_extension(dpy, name) \
82 { \
83     xcb_##name##_query_version_reply_t *rep; \
84     d_display_ext_t *ext; \
85 \
86     ext = find_extension_##name(dpy) \
87     if (ext->present) { \
88         rep = xcb_##name##_query_version_reply(dpy->conn, ck_##name, NULL); \
89         if (rep) { \
90             ext->major_version = rep->major_version; \
91             ext->minor_version = rep->minor_version; \
92             free(rep); \
93         } \
94         else { \
95             printf("error querying the %s extension's version\n", "##name##");\
96             ext->present = FALSE; \
97         } \
98     } \
99 }
100
101 static void
102 query_atom(d_display_t *dpy, d_atom_query_t *q)
103 {
104     q->ck = xcb_intern_atom(dpy->conn, FALSE, strlen(q->name), q->name);
105 }
106
107 static void
108 reply_atom(d_display_t *dpy, d_atom_query_t *q)
109 {
110     xcb_intern_atom_reply_t *rep;
111     rep = xcb_intern_atom_reply(dpy->conn, q->ck, NULL);
112     if (rep) {
113         *q->atom = rep->atom;
114         free(rep);
115     }
116     else {
117         printf("unable to query atom %s\n", q->name);
118         *q->atom = 0;
119     }
120 }
121
122 static void
123 query_statics(d_display_t *dpy)
124 {
125     d_atom_query_t atoms[] = {
126         { .atom = &dpy->a.utf8_string,
127           .name = "UTF8_STRING" },
128         { .atom = &dpy->a.esetroot_pmap_id,
129           .name = "ESETROOT_PMAP_ID" },
130         { .atom = &dpy->a.xrootpmap_id,
131           .name = "_XROOTPMAP_ID" },
132         { .atom = &dpy->a.xsetroot_id,
133           .name = "_XSETROOT_ID" },
134         { .atom = &dpy->a.net_wm_window_opacity,
135           .name = "_NET_WM_WINDOW_OPACITY" },
136         { .atom = &dpy->a.net_wm_window_type,
137           .name = "_NET_WM_WINDOW_TYPE" },
138         { .atom = &dpy->a.net_wm_window_type_desktop,
139           .name = "_NET_WM_WINDOW_TYPE_DESKTOP" },
140         { .atom = &dpy->a.net_wm_window_type_dock,
141           .name = "_NET_WM_WINDOW_TYPE_DOCK" },
142         { .atom = &dpy->a.net_wm_window_type_normal,
143           .name = "_NET_WM_WINDOW_TYPE_NORMAL" },
144         { .atom = &dpy->a.net_wm_window_type_dialog,
145           .name = "_NET_WM_WINDOW_TYPE_DIALOG" },
146         { .atom = &dpy->a.net_wm_window_type_toolbar,
147           .name = "_NET_WM_WINDOW_TYPE_TOOLBAR" },
148         { .atom = &dpy->a.net_wm_window_type_menu,
149           .name = "_NET_WM_WINDOW_TYPE_MENU" },
150         { .atom = &dpy->a.net_wm_window_type_utility,
151           .name = "_NET_WM_WINDOW_TYPE_UTILITY" },
152         { .atom = &dpy->a.net_wm_window_type_splash,
153           .name = "_NET_WM_WINDOW_TYPE_SPLAH" },
154         { .atom = &dpy->a.net_wm_window_type_tooltip,
155           .name = "_NET_WM_WINDOW_TYPE_TOOLTIP" },
156         { .atom = &dpy->a.net_wm_window_type_combo,
157           .name = "_NET_WM_WINDOW_TYPE_COMBO" },
158         { .atom = &dpy->a.net_wm_window_type_dropdown_menu,
159           .name = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" },
160         { .atom = &dpy->a.net_wm_window_type_popup_menu,
161           .name = "_NET_WM_WINDOW_TYPE_POPUP_MENU" },
162         { .atom = &dpy->a.net_wm_window_type_notification,
163           .name = "_NET_WM_WINDOW_TYPE_NOTIFICATION" },
164         { .atom = NULL }
165     };
166     int i;
167     setup_extension(xfixes);
168     setup_extension(render);
169     setup_extension(composite);
170     setup_extension(damage);
171     setup_extension(shape);
172     setup_extension(glx);
173
174     query_extension(dpy, xfixes);
175     query_extension(dpy, render);
176     query_extension(dpy, composite);
177     query_extension(dpy, damage);
178     query_extension(dpy, shape);
179     query_extension(dpy, glx);
180
181     query_extension_version(dpy, xfixes);
182     query_extension_version(dpy, render);
183     query_extension_version(dpy, composite);
184     query_extension_version(dpy, damage);
185     query_extension_version_no_client_version(dpy, shape);
186     query_extension_version(dpy, glx);
187
188     for (i = 0; atoms[i].atom != NULL; ++i)
189         query_atom(dpy, &atoms[i]);
190
191     xcb_flush(dpy->conn);
192
193     reply_extension(dpy, xfixes);
194     reply_extension(dpy, render);
195     reply_extension(dpy, composite);
196     reply_extension(dpy, damage);
197     reply_extension(dpy, shape);
198     reply_extension(dpy, glx);
199
200     for (i = 0; atoms[i].atom != NULL; ++i)
201         reply_atom(dpy, &atoms[i]);
202
203     /* hard-coded ones, you can't request  these */
204     dpy->a.atom = 4;
205     dpy->a.cardinal = 6;
206     dpy->a.pixmap = 20;
207     dpy->a.string = 31;
208     dpy->a.window = 33;
209     dpy->a.wm_transient_for = 68;
210 }
211
212 d_display_t*
213 display_open(const char *name)
214 {
215     d_display_t *dpy = NULL;
216     xcb_connection_t *conn = NULL;
217     Display *xlib_dpy = NULL;
218
219     xlib_dpy = XOpenDisplay(name);
220     if (xlib_dpy) {
221         conn = XGetXCBConnection(xlib_dpy);
222
223         XSetEventQueueOwner(xlib_dpy, XCBOwnsEventQueue);
224     }
225
226     //conn = xcb_connect(name, NULL);
227     if (conn && !xcb_connection_has_error(conn)) {
228         dpy = malloc(sizeof(d_display_t));
229         dpy->conn = conn;
230         dpy->xlib_dpy = xlib_dpy;
231         dpy->ref = 1;
232         dpy->fd = xcb_get_file_descriptor(conn);
233         dpy->screens = list_new();
234
235         query_statics(dpy);
236     }
237     return dpy;
238 }
239
240 void
241 display_ref(d_display_t *dpy)
242 {
243     ++dpy->ref;
244 }
245
246 void
247 display_unref(d_display_t *dpy)
248 {
249     if (dpy && --dpy->ref == 0) {
250         d_list_it_t *it;
251
252         for (it = list_top(dpy->screens); it; it = it->next)
253             screen_unref(it->data);
254         list_unref(dpy->screens);
255
256         //xcb_disconnect(dpy->conn);
257         XCloseDisplay(dpy->xlib_dpy);
258         free(dpy);
259     }
260 }
261
262 void
263 display_error(d_display_t *dpy, xcb_generic_error_t *ev)
264 {
265     const int major_opcode = ((xcb_request_error_t*)ev)->major_opcode;
266     const int minor_opcode = ((xcb_request_error_t*)ev)->minor_opcode;
267     const char *name, *req;
268
269     /* XXX check if we should ignore it (ev->full_sequence) */
270
271     (void)dpy;
272
273     name = NULL;
274     switch (ev->error_code) {
275     case XCB_REQUEST:   name = "BadRequest";   break;
276     case XCB_VALUE:     name = "BadValue";     break;
277     case XCB_WINDOW:    name = "BadWindow";    break;
278     case XCB_PIXMAP:    name = "BadPixmap";    break;
279     case XCB_MATCH:     name = "BadMatch";     break;
280     case XCB_DRAWABLE:  name = "BadDrawable";  break;
281     case XCB_G_CONTEXT: name = "BadGC";        break;
282     case XCB_ID_CHOICE: name = "BadID";        break;
283     default: break;
284     }
285     if (!name)
286         switch (ev->error_code - dpy->xfixes.error) {
287         case XCB_XFIXES_BAD_REGION: name = "BadRegion"; break;
288         default: break;
289         }
290     if (!name)
291         switch (ev->error_code - dpy->damage.error) {
292         case XCB_DAMAGE_BAD_DAMAGE: name = "BadDamage"; break;
293         default: break;
294         }
295     if (!name)
296         switch (ev->error_code - dpy->render.error) {
297         case XCB_RENDER_PICT_FORMAT: name = "BadPictFormat"; break;
298         case XCB_RENDER_PICT_OP:     name = "BadPictOp"; break;
299         case XCB_RENDER_PICTURE:     name = "BadPicture"; break;
300         case XCB_RENDER_GLYPH_SET:   name = "BadGlyphSet"; break;
301         case XCB_RENDER_GLYPH:       name = "BadGlyph"; break;
302         default: break;
303         }
304
305     req = NULL;
306     if (major_opcode <= 127)
307         switch (major_opcode) {
308         case 1:  req = "CreateWindow";           break;
309         case 2:  req = "ChangeWindowAttributes"; break;
310         case 3:  req = "GetWindowAttributes";    break;
311         case 14: req = "GetGeometry";            break;
312         case 15: req = "QueryTree";              break;
313         case 18: req = "ChangeProperty";         break;
314         case 20: req = "GetProperty";            break;
315         case 22: req = "SetSelectionOwner";      break;
316         case 23: req = "GetSelectionOwner";      break;
317         case 53: req = "CreatePixmap";           break;
318         case 54: req = "FreePixmap";             break;
319         case 55: req = "CreateGC";               break;
320         case 56: req = "ChangeGC";               break;
321         case 60: req = "FreeGC";                 break;
322         case 72: req = "PutImage";               break;
323         default: break; 
324         }
325     else if (major_opcode == dpy->damage.opcode)
326         switch (minor_opcode)
327         {
328         case 0: req = "DamageQueryVersion"; break;
329         case 1: req = "DamageCreate";       break;
330         case 2: req = "DamageDestroy";      break;
331         case 3: req = "DamageSubtract";     break;
332         default: break;
333         }
334     else if (major_opcode == dpy->render.opcode)
335         switch (minor_opcode)
336         {
337         case 0:  req = "RenderQueryVersion";             break;
338         case 1:  req = "RenderQueryPictFormats";         break;
339         case 2:  req = "RenderQueryPictIndexValues";     break;
340         case 4:  req = "RenderCreatePicture";            break;
341         case 5:  req = "RenderChangePicture";            break;
342         case 6:  req = "RenderSetPictureClipRectangles"; break;
343         case 7:  req = "RenderFreePicture";              break;
344         case 8:  req = "RenderComposite";                break;
345         case 10: req = "RenderCompositeTrapezoids";      break;
346         case 11: req = "RenderCompositeTriangles";       break;
347         case 12: req = "RenderCompositeTriStrip";        break;
348         case 13: req = "RenderCompositeTriFan";          break;
349         case 17: req = "RenderCreateGlyphSet";           break;
350         case 18: req = "RenderReferenceGlyphSet";        break;
351         case 19: req = "RenderFreeGlyphSet";             break;
352         case 20: req = "RenderAddGlyphs";                break;
353         case 22: req = "RenderFreeGlyphs";               break;
354         case 23: req = "RenderCompositeGlyphs8";         break;
355         case 24: req = "RenderCompositeGlyphs16";        break;
356         case 25: req = "RenderCompositeGlyphs32";        break;
357         case 26: req = "RenderFillRectangles";           break;
358         case 27: req = "RenderCreateCursor";             break;
359         case 28: req = "RenderSetPictureTransform";      break;
360         case 29: req = "RenderQueryFilters";             break;
361         case 30: req = "RenderSetPictureFilter";         break;
362         case 31: req = "RenderCreateAnimCursor";         break;
363         case 32: req = "RenderAddTraps";                 break;
364         case 33: req = "RenderCreateSolidFill";          break;
365         case 34: req = "RenderLinearGradient";           break;
366         case 35: req = "RenderRadialGradient";           break;
367         case 36: req = "RenderConicalGradient";          break;
368         default: break;
369         }
370     else if (major_opcode == dpy->composite.opcode)
371         switch (minor_opcode)
372         {
373         case 0: req = "CompositeQueryVersion";               break;
374         case 1: req = "CompositeRedirectWindow";             break;
375         case 2: req = "CompositeRedirectSubwindows";         break;
376         case 3: req = "CompositeUnredirectWindow";           break;
377         case 4: req = "CompositeUnredirectSubwindows";       break;
378         case 5: req = "CompositeCreateRegionFromBorderClip"; break;
379         case 6: req = "CompositeNameWindowPixmap";           break;
380         case 7: req = "CompositeGetOverlayWindow";           break;
381         case 8: req = "CompositeReleaseOverlayWindow";       break;
382         default: break;
383         }
384     else if (major_opcode == dpy->shape.opcode)
385         switch (minor_opcode)
386         {
387         case 0: req = "ShapeQueryVersion";  break;
388         case 1: req = "ShapeRectangles";    break;
389         case 2: req = "ShapeMask";          break;
390         case 3: req = "ShapeCombine";       break;
391         case 4: req = "ShapeOffset";        break;
392         case 5: req = "ShapeQueryExtents";  break;
393         case 6: req = "ShapeSelectInput";   break;
394         case 7: req = "ShapeInputSelected"; break;
395         case 8: req = "ShapeGetRectangles"; break;
396         default: break;
397         }
398     else if (major_opcode == dpy->xfixes.opcode)
399         switch (minor_opcode)
400         {
401         case 0: req = "XFixesQueryVersion";            break;
402         case 1: req = "XFixesChangeSaveSet";           break;
403         case 2: req = "XFixesSelectSelectionInput";    break;
404         case 3: req = "XFixesSelectCursorInput";       break;
405         case 4: req = "XFixesGetCursorImage";          break;
406         case 5: req = "XFixesCreateRegion";            break;
407         case 6: req = "XFixesCreateRegionFromBitmap";  break;
408         case 7: req = "XFixesCreateRegionFromWindow";  break;
409         case 8: req = "XFixesCreateRegionFromGC";      break;
410         case 9: req = "XFixesCreateRegionFromPicture"; break;
411         case 10: req = "XFixesDestroyRegion";          break;
412         case 11: req = "XFixesSetRegion";              break;
413         case 12: req = "XFixesCopyRegion";             break;
414         case 13: req = "XFixesUnionRegion";            break;
415         case 14: req = "XFixesIntersectRegion";        break;
416         case 15: req = "XFixesSubtractRegion";         break;
417         case 16: req = "XFixesInvertRegion";           break;
418         case 17: req = "XFixesTranslanteRegion";       break;
419         case 18: req = "XFixesRegionExtents";          break;
420         case 19: req = "XFixesFetchRegion";            break;
421         case 20: req = "XFixesGCClipRegion";           break;
422         case 21: req = "XFixesSetWindowShapeRegion";   break;
423         case 22: req = "XFixesSetPictureClipRegion";   break;
424         default: break;
425         }
426     else if (major_opcode == dpy->glx.opcode)
427         switch (minor_opcode)
428         {
429         case 1: req = "glXRender";                  break;
430         case 2: req = "glXRenderLarge";             break;
431         case 3: req = "glxCreateContext";           break;
432         case 4: req = "glxDestroyContext";          break;
433         case 5: req = "glxMakeCurrent";             break;
434         case 6: req = "glXIsDirect";                break;
435         case 7: req = "glXQueryVersion";            break;
436         case 8: req = "glXWaitGL";                  break;
437         case 9: req = "glXWaitX";                   break;
438         case 10: req = "glXCopyContext";            break;
439         case 11: req = "glXSwapBuffers";            break;
440         case 12: req = "glXUseXFont";               break;
441         case 13: req = "glXCreateGLXPixmap";        break;
442         case 14: req = "glXGetVisualConfigs";       break;
443         case 15: req = "glXDestroyGLXPixmap";       break;
444         case 16: req = "glXVendorPrivate";          break;
445         case 17: req = "glXVendorPrivateWithReply"; break;
446         case 18: req = "glXQueryExtensionsString";  break;
447         case 19: req = "glXQueryServerString";      break;
448         case 20: req = "glXClientInfo";             break;
449         case 21: req = "glXGetFBConfigs";           break;
450         case 22: req = "glXCreatePixmap";           break;
451         case 23: req = "glXDestroyPixmap";          break;
452         case 24: req = "glXCreateNewContext";       break;
453         case 25: req = "glXQueryContext";           break;
454         case 26: req = "glXMakeContextCurrent";     break;
455         default: break;
456         }
457
458     if (name && req)
459         printf("XError: %s %s\n",
460                name, req);
461     else if (name)
462         printf("XError: %s major opcode %d minor opcode %d\n",
463                name, major_opcode, minor_opcode);
464     else
465         printf("XError: error code %d major opcode %d minor opcode %d\n",
466                ev->error_code, major_opcode, minor_opcode);
467
468     //abort();
469 }
470
471 int
472 display_claim_screens(d_display_t *dpy)
473 {
474     static const xcb_setup_t *setup;
475     xcb_screen_iterator_t it;
476     int i;
477     d_screen_t *sc;
478
479     setup = xcb_get_setup(dpy->conn);
480
481     i = 0;
482     for (it = xcb_setup_roots_iterator(setup); it.rem; xcb_screen_next(&it)) {
483         sc = screen_new(dpy, i++, it.data);
484         if (screen_register(sc)) {
485             list_append(dpy->screens, sc);
486             printf(_("managing screen %d\n"), sc->num);
487         }
488         else
489             screen_unref(sc);
490     }
491     return list_length(dpy->screens);
492 }
493
494 d_screen_t*
495 display_screen_from_root(d_display_t *dpy, xcb_window_t root)
496 {
497     d_list_it_t *it;
498
499     for (it = list_top(dpy->screens); it; it = it->next) {
500         d_screen_t *sc = it->data;
501         if (sc->super->root == root)
502             return sc;
503     }
504     return NULL;
505 }