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