]> git.sur5r.net Git - i3/i3lock/blob - xcb.c
make event loop simpler (Thanks to Jamey Sharp)
[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 xcb_visualtype_t *get_root_visual_type(xcb_screen_t *screen) {
31     xcb_visualtype_t *visual_type = NULL;
32     xcb_depth_iterator_t depth_iter;
33     xcb_visualtype_iterator_t visual_iter;
34
35     for (depth_iter = xcb_screen_allowed_depths_iterator(screen);
36          depth_iter.rem;
37          xcb_depth_next(&depth_iter)) {
38
39         for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
40              visual_iter.rem;
41              xcb_visualtype_next(&visual_iter)) {
42             if (screen->root_visual != visual_iter.data->visual_id)
43                 continue;
44
45             visual_type = visual_iter.data;
46             return visual_type;
47         }
48     }
49
50     return NULL;
51 }
52
53 xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color) {
54     uint32_t mask = 0;
55     uint32_t values[3];
56     xcb_window_t win = xcb_generate_id(conn);
57
58     mask |= XCB_CW_BACK_PIXEL;
59     values[0] = get_colorpixel(color);
60
61     mask |= XCB_CW_OVERRIDE_REDIRECT;
62     values[1] = 1;
63
64     mask |= XCB_CW_EVENT_MASK;
65     values[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE;
66
67     xcb_create_window(conn,
68                       24,
69                       win, /* the window id */
70                       scr->root, /* parent == root */
71                       0, 0,
72                       scr->width_in_pixels,
73                       scr->height_in_pixels, /* dimensions */
74                       0, /* border = 0, we draw our own */
75                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
76                       XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
77                       mask,
78                       values);
79
80     /* Map the window (= make it visible) */
81     xcb_map_window(conn, win);
82
83     /* Raise window (put it on top) */
84     values[0] = XCB_STACK_MODE_ABOVE;
85     xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
86
87     return win;
88 }
89
90 /*
91  * Returns the mask for Mode_switch (to be used for looking up keysymbols by
92  * keycode).
93  *
94  */
95 uint32_t get_mod_mask(xcb_connection_t *conn, xcb_key_symbols_t *symbols, uint32_t keycode) {
96     xcb_get_modifier_mapping_reply_t *modmap_r;
97     xcb_keycode_t *modmap, kc;
98     xcb_keycode_t *modeswitchcodes = xcb_key_symbols_get_keycode(symbols, keycode);
99     if (modeswitchcodes == NULL)
100         return 0;
101
102     modmap_r = xcb_get_modifier_mapping_reply(conn, xcb_get_modifier_mapping(conn), NULL);
103     modmap = xcb_get_modifier_mapping_keycodes(modmap_r);
104
105     for (int i = 0; i < 8; i++)
106         for (int j = 0; j < modmap_r->keycodes_per_modifier; j++) {
107             kc = modmap[i * modmap_r->keycodes_per_modifier + j];
108             for (xcb_keycode_t *ktest = modeswitchcodes; *ktest; ktest++) {
109                 if (*ktest != kc)
110                     continue;
111
112                 free(modeswitchcodes);
113                 free(modmap_r);
114                 return (1 << i);
115             }
116         }
117
118     return 0;
119 }
120
121 void dpms_turn_off_screen(xcb_connection_t *conn) {
122     xcb_dpms_enable(conn);
123     xcb_dpms_force_level(conn, XCB_DPMS_DPMS_MODE_OFF);
124     xcb_flush(conn);
125 }
126
127 /*
128  * Repeatedly tries to grab pointer and keyboard (up to 1000 times).
129  *
130  */
131 void grab_pointer_and_keyboard(xcb_connection_t *conn, xcb_screen_t *screen) {
132     xcb_grab_pointer_cookie_t pcookie;
133     xcb_grab_pointer_reply_t *preply;
134
135     xcb_grab_keyboard_cookie_t kcookie;
136     xcb_grab_keyboard_reply_t *kreply;
137
138     int tries = 1000;
139
140     while (tries-- > 0) {
141         pcookie = xcb_grab_pointer(
142             conn,
143             false,               /* get all pointer events specified by the following mask */
144             screen->root,        /* grab the root window */
145             XCB_NONE,            /* which events to let through */
146             XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
147             XCB_GRAB_MODE_ASYNC, /* keyboard mode */
148             XCB_NONE,            /* confine_to = in which window should the cursor stay */
149             XCB_NONE,            /* don’t display a special cursor */
150             XCB_CURRENT_TIME
151         );
152
153         if ((preply = xcb_grab_pointer_reply(conn, pcookie, NULL)) &&
154             preply->status == XCB_GRAB_STATUS_SUCCESS) {
155             free(preply);
156             break;
157         }
158     }
159
160     while (tries-- > 0) {
161         kcookie = xcb_grab_keyboard(
162             conn,
163             true,                /* report events */
164             screen->root,        /* grab the root window */
165             XCB_CURRENT_TIME,
166             XCB_GRAB_MODE_ASYNC, /* process events as normal, do not require sync */
167             XCB_GRAB_MODE_ASYNC
168         );
169
170         if ((kreply = xcb_grab_keyboard_reply(conn, kcookie, NULL)) &&
171             kreply->status == XCB_GRAB_STATUS_SUCCESS) {
172             free(kreply);
173             break;
174         }
175     }
176
177     if (tries == 0)
178         errx(EXIT_FAILURE, "Cannot grab pointer/keyboard");
179 }