]> git.sur5r.net Git - i3/i3lock/blob - xcb.c
Initial commit of the XCB rewrite of i3lock
[i3/i3lock] / xcb.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * © 2010 Michael Stapelberg
5  *
6  * xcb.c: contains all functions which use XCB to talk to X11. Mostly wrappers
7  *        around the rather complicated/ugly parts of the XCB API.
8  *
9  */
10 #include <xcb/xcb.h>
11 #include <xcb/xcb_keysyms.h>
12 #include <xcb/dpms.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdbool.h>
16 #include <assert.h>
17 #include <err.h>
18
19 static uint32_t get_colorpixel(char *hex) {
20     char strgroups[3][3] = {{hex[0], hex[1], '\0'},
21                             {hex[2], hex[3], '\0'},
22                             {hex[4], hex[5], '\0'}};
23     uint32_t rgb16[3] = {(strtol(strgroups[0], NULL, 16)),
24                          (strtol(strgroups[1], NULL, 16)),
25                          (strtol(strgroups[2], NULL, 16))};
26
27     return (rgb16[0] << 16) + (rgb16[1] << 8) + rgb16[2];
28 }       
29
30 int x_event_type(xcb_generic_event_t *event) {
31     /* TODO: comment */
32     assert(event->response_type != 1);
33
34     if (event->response_type == 0) {
35         fprintf(stderr, "error\n");
36         exit(1);
37     }
38     int type = event->response_type;
39     assert(type < 256);
40     /* strip the highest bit (TODO: why?) */
41     type &= 0x7F;
42     assert(type >= 2);
43
44     return type;
45 }
46
47 xcb_visualtype_t *get_root_visual_type(xcb_screen_t *screen) {
48     xcb_visualtype_t *visual_type = NULL;
49     xcb_depth_iterator_t depth_iter;
50     xcb_visualtype_iterator_t visual_iter;
51
52     for (depth_iter = xcb_screen_allowed_depths_iterator(screen);
53          depth_iter.rem;
54          xcb_depth_next(&depth_iter)) {
55
56         for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
57              visual_iter.rem;
58              xcb_visualtype_next(&visual_iter)) {
59             if (screen->root_visual != visual_iter.data->visual_id)
60                 continue;
61
62             visual_type = visual_iter.data;
63             return visual_type;
64         }
65     }
66
67     return NULL;
68 }
69
70 xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color) {
71     uint32_t mask = 0;
72     uint32_t values[3];
73     xcb_window_t win = xcb_generate_id(conn);
74
75     mask |= XCB_CW_BACK_PIXEL;
76     values[0] = get_colorpixel(color);
77
78     mask |= XCB_CW_OVERRIDE_REDIRECT;
79     values[1] = 1;
80
81     mask |= XCB_CW_EVENT_MASK;
82     values[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE;
83
84     xcb_create_window(conn,
85                       24,
86                       win, /* the window id */
87                       scr->root, /* parent == root */
88                       0, 0,
89                       scr->width_in_pixels,
90                       scr->height_in_pixels, /* dimensions */
91                       0, /* border = 0, we draw our own */
92                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
93                       XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
94                       mask,
95                       values);
96
97     /* Map the window (= make it visible) */
98     xcb_map_window(conn, win);
99
100     /* Raise window (put it on top) */
101     values[0] = XCB_STACK_MODE_ABOVE;
102     xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
103
104     return win;
105 }
106
107 /*
108  * Returns the mask for Mode_switch (to be used for looking up keysymbols by
109  * keycode).
110  *
111  */
112 uint32_t get_mod_mask(xcb_connection_t *conn, xcb_key_symbols_t *symbols, uint32_t keycode) {
113     xcb_get_modifier_mapping_reply_t *modmap_r;
114     xcb_keycode_t *modmap, kc;
115     xcb_keycode_t *modeswitchcodes = xcb_key_symbols_get_keycode(symbols, keycode);
116     if (modeswitchcodes == NULL)
117         return 0;
118
119     modmap_r = xcb_get_modifier_mapping_reply(conn, xcb_get_modifier_mapping(conn), NULL);
120     modmap = xcb_get_modifier_mapping_keycodes(modmap_r);
121
122     for (int i = 0; i < 8; i++)
123         for (int j = 0; j < modmap_r->keycodes_per_modifier; j++) {
124             kc = modmap[i * modmap_r->keycodes_per_modifier + j];
125             for (xcb_keycode_t *ktest = modeswitchcodes; *ktest; ktest++) {
126                 if (*ktest != kc)
127                     continue;
128
129                 free(modeswitchcodes);
130                 free(modmap_r);
131                 return (1 << i);
132             }
133         }
134
135     return 0;
136 }
137
138 void dpms_turn_off_screen(xcb_connection_t *conn) {
139     xcb_dpms_enable(conn);
140     xcb_dpms_force_level(conn, XCB_DPMS_DPMS_MODE_OFF);
141     xcb_flush(conn);
142 }
143
144 /*
145  * Repeatedly tries to grab pointer and keyboard (up to 1000 times).
146  *
147  */
148 void grab_pointer_and_keyboard(xcb_connection_t *conn, xcb_screen_t *screen) {
149     xcb_grab_pointer_cookie_t pcookie;
150     xcb_grab_pointer_reply_t *preply;
151
152     xcb_grab_keyboard_cookie_t kcookie;
153     xcb_grab_keyboard_reply_t *kreply;
154
155     int tries = 1000;
156
157     while (tries-- > 0) {
158         pcookie = xcb_grab_pointer(
159             conn,
160             false,               /* get all pointer events specified by the following mask */
161             screen->root,        /* grab the root window */
162             XCB_NONE,            /* which events to let through */
163             XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
164             XCB_GRAB_MODE_ASYNC, /* keyboard mode */
165             XCB_NONE,            /* confine_to = in which window should the cursor stay */
166             XCB_NONE,            /* don’t display a special cursor */
167             XCB_CURRENT_TIME
168         );
169
170         if ((preply = xcb_grab_pointer_reply(conn, pcookie, NULL)) &&
171             preply->status == XCB_GRAB_STATUS_SUCCESS) {
172             free(preply);
173             break;
174         }
175     }
176
177     while (tries-- > 0) {
178         kcookie = xcb_grab_keyboard(
179             conn,
180             true,                /* report events */
181             screen->root,        /* grab the root window */
182             XCB_CURRENT_TIME,
183             XCB_GRAB_MODE_ASYNC, /* process events as normal, do not require sync */
184             XCB_GRAB_MODE_ASYNC
185         );
186
187         if ((kreply = xcb_grab_keyboard_reply(conn, kcookie, NULL)) &&
188             kreply->status == XCB_GRAB_STATUS_SUCCESS) {
189             free(kreply);
190             break;
191         }
192     }
193
194     if (tries == 0)
195         errx(EXIT_FAILURE, "Cannot grab pointer/keyboard");
196 }