use select to tell when there are events to read
[dana/dcompmgr.git] / display.c
1 #include "display.h"
2 #include "screen.h"
3 #include "gettext.h"
4 #include <stdlib.h>
5 #include <assert.h>
6 #include <string.h>
7 #include <stdio.h>
8
9 #include <xcb/damage.h>
10 #include <xcb/render.h>
11 #include <xcb/xfixes.h>
12 #include <xcb/composite.h>
13
14 typedef struct {
15     xcb_atom_t               *atom;
16     const char               *name;
17     xcb_intern_atom_cookie_t  ck;
18 } d_atom_query_t;
19
20 #define setup_extension(name) \
21     xcb_##name##_query_version_cookie_t ck_##name;
22
23 #define find_extension_xfixes(dpy) &dpy->xfixes;
24 #define find_extension_render(dpy) &dpy->render;
25 #define find_extension_composite(dpy) &dpy->composite;
26 #define find_extension_damage(dpy) &dpy->damage;
27
28 #define version_extension_xfixes \
29   XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MAJOR_VERSION
30 #define version_extension_render \
31   XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MAJOR_VERSION
32 #define version_extension_composite \
33   XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MAJOR_VERSION
34 #define version_extension_damage \
35   XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MAJOR_VERSION
36
37 #define query_extension(dpy, name) \
38 { \
39     const xcb_query_extension_reply_t *rep;          \
40     d_display_ext_t *ext; \
41 \
42     xcb_prefetch_extension_data(dpy->conn, &xcb_##name##_id);  \
43     rep = xcb_get_extension_data(dpy->conn, &xcb_##name##_id); \
44 \
45     ext = find_extension_##name(dpy) \
46     ext->present = rep && rep->present;       \
47     ext->error   = rep && rep->first_error;   \
48     ext->event   = rep && rep->first_event;   \
49     ext->opcode  = rep && rep->major_opcode;  \
50 \
51     if (ext->present) \
52         ck_##name = xcb_##name##_query_version(dpy->conn, \
53                                                version_extension_##name); \
54 }
55
56 #define reply_extension(dpy, name) \
57 { \
58     xcb_##name##_query_version_reply_t *rep; \
59     d_display_ext_t *ext; \
60 \
61     ext = find_extension_##name(dpy) \
62     if (ext->present) { \
63         rep = xcb_##name##_query_version_reply(dpy->conn, ck_##name, NULL); \
64         if (rep) { \
65             ext->major_version = rep->major_version; \
66             ext->minor_version = rep->minor_version; \
67             free(rep); \
68         } \
69         else { \
70             printf("error querying the %s extension's version\n", "##name##");\
71             ext->present = FALSE; \
72         } \
73     } \
74 }
75
76 static void
77 query_atom(d_display_t *dpy, d_atom_query_t *q)
78 {
79     q->ck = xcb_intern_atom(dpy->conn, FALSE, strlen(q->name), q->name);
80 }
81
82 static void
83 reply_atom(d_display_t *dpy, d_atom_query_t *q)
84 {
85     xcb_intern_atom_reply_t *rep;
86     rep = xcb_intern_atom_reply(dpy->conn, q->ck, NULL);
87     if (rep) {
88         *q->atom = rep->atom;
89         free(rep);
90     }
91     else {
92         printf("unable to query atom %s\n", q->name);
93         *q->atom = 0;
94     }
95 }
96
97 static void
98 query_statics(d_display_t *dpy)
99 {
100     d_atom_query_t atoms[] = {
101         { .atom = &dpy->a.atom,
102           .name = "ATOM" },
103         { .atom = &dpy->a.cardinal,
104           .name = "CARDINAL" },
105         { .atom = &dpy->a.utf8_string,
106           .name = "UTF8_STRING" },
107         { .atom = &dpy->a.string,
108           .name = "STRING" },
109         { .atom = &dpy->a.pixmap,
110           .name = "PIXMAP" },
111         { .atom = &dpy->a.net_wm_window_type,
112           .name = "_NET_WM_WINDOW_TYPE" },
113         { .atom = &dpy->a.net_wm_window_type_desktop,
114           .name = "_NET_WM_WINDOW_TYPE_DESKTOP" },
115         { .atom = &dpy->a.net_wm_window_type_dock,
116           .name = "_NET_WM_WINDOW_TYPE_DOCK" },
117         { .atom = &dpy->a.net_wm_window_type_normal,
118           .name = "_NET_WM_WINDOW_TYPE_NORMAL" },
119         { .atom = &dpy->a.net_wm_window_type_dialog,
120           .name = "_NET_WM_WINDOW_TYPE_DIALOG" },
121         { .atom = &dpy->a.net_wm_window_type_toolbar,
122           .name = "_NET_WM_WINDOW_TYPE_TOOLBAR" },
123         { .atom = &dpy->a.net_wm_window_type_menu,
124           .name = "_NET_WM_WINDOW_TYPE_MENU" },
125         { .atom = &dpy->a.net_wm_window_type_utility,
126           .name = "_NET_WM_WINDOW_TYPE_UTILITY" },
127         { .atom = &dpy->a.net_wm_window_type_splash,
128           .name = "_NET_WM_WINDOW_TYPE_SPLAH" },
129         { .atom = &dpy->a.net_wm_window_type_tooltip,
130           .name = "_NET_WM_WINDOW_TYPE_TOOLTIP" },
131         { .atom = &dpy->a.net_wm_window_type_combo,
132           .name = "_NET_WM_WINDOW_TYPE_COMBO" },
133         { .atom = &dpy->a.net_wm_window_type_dropdown_menu,
134           .name = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" },
135         { .atom = &dpy->a.net_wm_window_type_popup_menu,
136           .name = "_NET_WM_WINDOW_TYPE_POPUP_MENU" },
137         { .atom = &dpy->a.net_wm_window_type_notification,
138           .name = "_NET_WM_WINDOW_TYPE_NOTIFICATION" },
139         { .atom = NULL }
140     };
141     int i;
142     setup_extension(xfixes);
143     setup_extension(render);
144     setup_extension(composite);
145     setup_extension(damage);
146
147     query_extension(dpy, xfixes);
148     query_extension(dpy, render);
149     query_extension(dpy, composite);
150     query_extension(dpy, damage);
151
152     for (i = 0; atoms[i].atom != NULL; ++i)
153         query_atom(dpy, &atoms[i]);
154
155     xcb_flush(dpy->conn);
156
157     reply_extension(dpy, xfixes);
158     reply_extension(dpy, render);
159     reply_extension(dpy, composite);
160     reply_extension(dpy, damage);
161
162     for (i = 0; atoms[i].atom != NULL; ++i)
163         reply_atom(dpy, &atoms[i]);
164 }
165
166 d_display_t*
167 display_open(const char *name)
168 {
169     d_display_t *dpy = NULL;
170     xcb_connection_t *conn;
171
172     conn = xcb_connect(name, NULL);
173     if (conn && !xcb_connection_has_error(conn)) {
174         dpy = malloc(sizeof(d_display_t));
175         dpy->conn = conn;
176         dpy->ref = 1;
177         dpy->fd = xcb_get_file_descriptor(conn);
178         dpy->screens = NULL;
179         dpy->nscreens = 0;
180
181         query_statics(dpy);
182     }
183     return dpy;
184 }
185
186 void
187 display_ref(d_display_t *dpy)
188 {
189     ++dpy->ref;
190 }
191
192 void
193 display_unref(d_display_t *dpy)
194 {
195     if (dpy && --dpy->ref == 0) {
196         int i;
197         xcb_disconnect(dpy->conn);
198
199         for (i = 0; i < dpy->nscreens; ++i)
200             screen_free(&dpy->screens[i]);
201         free(dpy->screens);
202         free(dpy);
203     }
204 }
205
206 void
207 display_error(d_display_t *dpy, xcb_generic_error_t *ev)
208 {
209     const int major_opcode = ((xcb_request_error_t*)ev)->major_opcode;
210     const int minor_opcode = ((xcb_request_error_t*)ev)->minor_opcode;
211     const char *name, *req;
212
213     /* XXX check if we should ignore it (ev->full_sequence) */
214
215     (void)dpy;
216
217     name = NULL;
218     switch (ev->error_code) {
219     case XCB_REQUEST:   name = "BadRequest";   break;
220     case XCB_VALUE:     name = "BadValue";     break;
221     case XCB_WINDOW:    name = "BadWindow";    break;
222     case XCB_PIXMAP:    name = "BadPixmap";    break;
223     case XCB_MATCH:     name = "BadMatch";     break;
224     case XCB_DRAWABLE:  name = "BadDrawable";  break;
225     case XCB_G_CONTEXT: name = "BadGC";        break;
226     default: break;
227     }
228     if (!name)
229         switch (ev->error_code - dpy->xfixes.error) {
230         case XCB_XFIXES_BAD_REGION: name = "BadRegion"; break;
231         default: break;
232         }
233     if (!name)
234         switch (ev->error_code - dpy->damage.error) {
235         case XCB_DAMAGE_BAD_DAMAGE: name = "BadDamage"; break;
236         default: break;
237         }
238     if (!name)
239         switch (ev->error_code - dpy->render.error) {
240         case XCB_RENDER_PICT_FORMAT: name = "BadPictFormat"; break;
241         case XCB_RENDER_PICT_OP:     name = "BadPictOp"; break;
242         case XCB_RENDER_PICTURE:     name = "BadPicture"; break;
243         case XCB_RENDER_GLYPH_SET:   name = "BadGlyphSet"; break;
244         case XCB_RENDER_GLYPH:       name = "BadGlyph"; break;
245         default: break;
246         }
247
248     if (major_opcode <= 127)
249         switch (major_opcode) {
250         case 1:  req = "CreateWindow";           break;
251         case 2:  req = "ChangeWindowAttributes"; break;
252         case 3:  req = "GetWindowAttributes";    break;
253         case 14: req = "GetGeometry";            break;
254         case 15: req = "QueryTree";              break;
255         case 18: req = "ChangeProperty";         break;
256         case 20: req = "GetProperty";            break;
257         case 22: req = "SetSelectionOwner";      break;
258         case 23: req = "GetSelectionOwner";      break;
259         case 53: req = "CreatePixmap";           break;
260         case 54: req = "FreePixmap";             break;
261         case 55: req = "CreateGC";               break;
262         case 56: req = "ChangeGC";               break;
263         case 60: req = "FreeGC";                 break;
264         case 72: req = "PutImage";               break;
265         default: break; 
266         }
267
268     if (name && req)
269         printf("XError: %s %s!\n",
270                name, req);
271     else if (name)
272         printf("XError: %s major opcode %d minor opcode %d!\n",
273                name, major_opcode, minor_opcode);
274     else
275         printf("XError: code %d major opcode %d minor opcode %d!\n",
276                ev->error_code, major_opcode, minor_opcode);
277
278     abort();
279 }
280
281 int
282 display_claim_screens(d_display_t *dpy)
283 {
284     static const xcb_setup_t *setup;
285     xcb_screen_iterator_t it;
286     int i;
287     d_screen_t sc;
288
289     setup = xcb_get_setup(dpy->conn);
290
291     i = 0;
292     for (it = xcb_setup_roots_iterator(setup); it.rem; xcb_screen_next(&it)) {
293         sc.super = *it.data;
294         sc.dpy = dpy;
295         sc.num = i++;
296         if (screen_register(dpy, i, &sc)) {
297             ++dpy->nscreens;
298             dpy->screens = realloc(dpy->screens,
299                                    sizeof(d_screen_t)*dpy->nscreens);
300             dpy->screens[dpy->nscreens-1] = sc;
301             printf(_("managing screen %d\n"), sc.num);
302         }
303     }
304     return dpy->nscreens;
305 }
306
307 d_screen_t*
308 display_screen_from_root(d_display_t *dpy, xcb_window_t root)
309 {
310     int i;
311     for (i = 0; i < dpy->nscreens; ++i)
312         if (dpy->screens[i].super.root == root)
313             return &dpy->screens[i];
314     assert(0);
315     return NULL;
316 }
317
318 struct d_screen*
319 display_screen_n(d_display_t *dpy, int n)
320 {
321     assert(n >= 0 && n < dpy->nscreens);
322     return &dpy->screens[n];
323 }