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