]> git.sur5r.net Git - i3/i3/blob - i3bar/src/xcb.c
Reconfigure to the correct coordinates
[i3/i3] / i3bar / src / xcb.c
1 #include <xcb/xcb.h>
2 #include <xcb/xproto.h>
3 #include <xcb/xcb_event.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <i3/ipc.h>
8 #include <ev.h>
9
10 #include "common.h"
11
12 #define NUM_ATOMS 3
13
14 enum {
15     #define ATOM_DO(name) name,
16     #include "xcb_atoms.def"
17 };
18
19 xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
20 xcb_atom_t               atoms[NUM_ATOMS];
21
22 xcb_connection_t *xcb_connection;
23 xcb_screen_t     *xcb_screens;
24 xcb_window_t     xcb_root;
25 xcb_font_t       xcb_font;
26
27 ev_prepare *xcb_prep;
28 ev_check   *xcb_chk;
29 ev_io      *xcb_io;
30
31 xcb_event_handlers_t xcb_event_handlers;
32
33 uint32_t get_colorpixel(const char *s) {
34     char strings[3][3] = { { s[0], s[1], '\0'} ,
35                            { s[2], s[3], '\0'} ,
36                            { s[4], s[5], '\0'} };
37     uint8_t r = strtol(strings[0], NULL, 16);
38     uint8_t g = strtol(strings[1], NULL, 16);
39     uint8_t b = strtol(strings[2], NULL, 16);
40     return (r << 16 | g << 8 | b);
41 }
42
43 void handle_button(xcb_button_press_event_t *event) {
44     i3_ws *cur_ws;
45     i3_output *walk;
46     xcb_window_t bar = event->event;
47     SLIST_FOREACH(walk, outputs, slist) {
48         if (walk->bar == bar) {
49             break;
50         }
51     }
52
53     if (walk == NULL) {
54         printf("Unknown Bar klicked!\n");
55         return;
56     }
57
58     TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
59         if (cur_ws->visible) {
60             break;
61         }
62     }
63
64     if (cur_ws == NULL) {
65         printf("No Workspace active?\n");
66         return;
67     }
68
69     int32_t x = event->event_x;
70
71     printf("Got Button %d\n", event->detail);
72
73     switch (event->detail) {
74         case 1:
75             TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
76                 printf("x = %d\n", x);
77                 if (x < cur_ws->name_width + 10) {
78                     break;
79                 }
80                 x -= cur_ws->name_width + 10;
81             }
82             if (cur_ws == NULL) {
83                 return;
84             }
85             break;
86         case 4:
87             if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head)) {
88                 cur_ws = TAILQ_FIRST(walk->workspaces);
89             } else {
90                 cur_ws = TAILQ_NEXT(cur_ws, tailq);
91             }
92             break;
93         case 5:
94             if (cur_ws == TAILQ_FIRST(walk->workspaces)) {
95                 cur_ws = TAILQ_LAST(walk->workspaces, ws_head);
96             } else {
97                 cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
98             }
99             break;
100     }
101
102     char buffer[50];
103     snprintf(buffer, 50, "%d", cur_ws->num);
104     i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
105 }
106
107 void handle_xcb_event(xcb_generic_event_t *event) {
108     switch (event->response_type & ~0x80) {
109         case XCB_EXPOSE:
110             draw_bars();
111             break;
112         case XCB_BUTTON_PRESS:
113             handle_button((xcb_button_press_event_t*) event);
114             break;
115     }
116 }
117
118 void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revenst) {
119     xcb_flush(xcb_connection);
120 }
121
122 void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
123     xcb_generic_event_t *event;
124     if ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
125         handle_xcb_event(event);
126     }
127     FREE(event);
128 }
129
130 void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
131     /* Dummy Callback. We only need this, so that xcb-events trigger
132      * Prepare- and Check-Watchers */
133 }
134
135 int get_string_width(xcb_char2b_t *string, int glyph_len) {
136     xcb_query_text_extents_cookie_t cookie;
137     xcb_query_text_extents_reply_t *reply;
138     xcb_generic_error_t *error;
139     int width;
140
141     cookie = xcb_query_text_extents(xcb_connection, xcb_font, glyph_len, string);
142     if ((reply = xcb_query_text_extents_reply(xcb_connection, cookie, &error)) == NULL) {
143         printf("ERROR: Could not get text extents!");
144         return 7;
145     }
146
147     width = reply->overall_width;
148     free(reply);
149     return width;
150 }
151
152 void init_xcb(char *fontname) {
153     /* FIXME: xcb_connect leaks Memory */
154     xcb_connection = xcb_connect(NULL, NULL);
155     if (xcb_connection_has_error(xcb_connection)) {
156         printf("Cannot open display\n");
157         exit(EXIT_FAILURE);
158     }
159     printf("Connected to xcb\n");
160
161     /* We have to request the atoms we need */
162     #define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
163         #include "xcb_atoms.def"
164
165     xcb_screens = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
166     xcb_root = xcb_screens->root;
167
168     xcb_font = xcb_generate_id(xcb_connection);
169     xcb_open_font(xcb_connection,
170                   xcb_font,
171                   strlen(fontname),
172                   fontname);
173
174     xcb_list_fonts_with_info_cookie_t cookie;
175     cookie = xcb_list_fonts_with_info(xcb_connection,
176                                       1,
177                                       strlen(fontname),
178                                       fontname);
179     xcb_list_fonts_with_info_reply_t *reply;
180     reply = xcb_list_fonts_with_info_reply(xcb_connection,
181                                            cookie,
182                                            NULL);
183     font_height = reply->font_ascent + reply->font_descent;
184     FREE(reply);
185     printf("Calculated Font-height: %d\n", font_height);
186
187     xcb_io = malloc(sizeof(ev_io));
188     xcb_prep = malloc(sizeof(ev_prepare));
189     xcb_chk = malloc(sizeof(ev_check));
190
191     ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
192     ev_prepare_init(xcb_prep, &xcb_prep_cb);
193     ev_check_init(xcb_chk, &xcb_chk_cb);
194
195     ev_io_start(main_loop, xcb_io);
196     ev_prepare_start(main_loop, xcb_prep);
197     ev_check_start(main_loop, xcb_chk);
198
199     /* FIXME: Maybe we can push that further backwards */
200     get_atoms();
201 }
202
203 void clean_xcb() {
204     xcb_disconnect(xcb_connection);
205
206     ev_check_stop(main_loop, xcb_chk);
207     ev_prepare_stop(main_loop, xcb_prep);
208     ev_io_stop(main_loop, xcb_io);
209
210     FREE(xcb_chk);
211     FREE(xcb_prep);
212     FREE(xcb_io);
213 }
214
215 void get_atoms() {
216     xcb_intern_atom_reply_t *reply;
217     #define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
218         atoms[name] = reply->atom; \
219         free(reply);
220
221     #include "xcb_atoms.def"
222     printf("Got Atoms\n");
223 }
224
225 void destroy_window(i3_output *output) {
226     if (output == NULL) {
227         return;
228     }
229     if (output->bar == XCB_NONE) {
230         return;
231     }
232     xcb_destroy_window(xcb_connection, output->bar);
233     output->bar = XCB_NONE;
234 }
235
236 void reconfig_windows() {
237     uint32_t mask;
238     uint32_t values[4];
239
240     i3_output *walk;
241     SLIST_FOREACH(walk, outputs, slist) {
242         if (!walk->active) {
243             printf("Destroying window for output %s\n", walk->name);
244             destroy_window(walk);
245             continue;
246         }
247         if (walk->bar == XCB_NONE) {
248             printf("Creating Window for output %s\n", walk->name);
249
250             walk->bar = xcb_generate_id(xcb_connection);
251             mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
252             values[0] = xcb_screens->black_pixel;
253             values[1] = XCB_EVENT_MASK_EXPOSURE |
254                         XCB_EVENT_MASK_BUTTON_PRESS;
255             xcb_create_window(xcb_connection,
256                               xcb_screens->root_depth,
257                               walk->bar,
258                               xcb_root,
259                               walk->rect.x, walk->rect.y,
260                               walk->rect.w, font_height + 6,
261                               1,
262                               XCB_WINDOW_CLASS_INPUT_OUTPUT,
263                               xcb_screens->root_visual,
264                               mask,
265                               values);
266
267             xcb_change_property(xcb_connection,
268                                 XCB_PROP_MODE_REPLACE,
269                                 walk->bar,
270                                 atoms[_NET_WM_WINDOW_TYPE],
271                                 atoms[ATOM],
272                                 32,
273                                 1,
274                                 (unsigned char*) &atoms[_NET_WM_WINDOW_TYPE_DOCK]);
275
276             walk->bargc = xcb_generate_id(xcb_connection);
277             mask = XCB_GC_FONT;
278             values[0] = xcb_font;
279             xcb_create_gc(xcb_connection,
280                           walk->bargc,
281                           walk->bar,
282                           mask,
283                           values);
284
285             xcb_map_window(xcb_connection, walk->bar);
286         } else {
287             mask = XCB_CONFIG_WINDOW_X |
288                    XCB_CONFIG_WINDOW_Y |
289                    XCB_CONFIG_WINDOW_WIDTH |
290                    XCB_CONFIG_WINDOW_HEIGHT;
291             values[0] = walk->rect.x;
292             values[1] = walk->rect.y + walk->rect.h - font_height - 6;
293             values[2] = walk->rect.w;
294             values[3] = font_height + 6;
295             printf("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
296             xcb_configure_window(xcb_connection,
297                                  walk->bar,
298                                  mask,
299                                  values);
300         }
301     }
302 }
303
304 void draw_bars() {
305     printf("Drawing Bars...\n");
306     int i = 0;
307     i3_output *outputs_walk;
308     SLIST_FOREACH(outputs_walk, outputs, slist) {
309         if (!outputs_walk->active) {
310             printf("Output %s inactive, skipping...\n", outputs_walk->name);
311             continue;
312         }
313         if (outputs_walk->bar == XCB_NONE) {
314             reconfig_windows();
315         }
316         uint32_t color = get_colorpixel("000000");
317         xcb_change_gc(xcb_connection,
318                       outputs_walk->bargc,
319                       XCB_GC_FOREGROUND,
320                       &color);
321         xcb_rectangle_t rect = { 0, 0, outputs_walk->rect.w, font_height + 6 };
322         xcb_poly_fill_rectangle(xcb_connection,
323                                 outputs_walk->bar,
324                                 outputs_walk->bargc,
325                                 1,
326                                 &rect);
327         if (statusline != NULL) {
328             printf("Printing statusline!\n");
329             xcb_change_gc(xcb_connection,
330                           outputs_walk->bargc,
331                           XCB_GC_BACKGROUND,
332                           &color);
333             color = get_colorpixel("FFFFFF");
334             xcb_change_gc(xcb_connection,
335                           outputs_walk->bargc,
336                           XCB_GC_FOREGROUND,
337                           &color);
338
339             int glyph_count;
340             xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count);
341
342             xcb_void_cookie_t cookie;
343             cookie = xcb_image_text_16(xcb_connection,
344                                        glyph_count,
345                                        outputs_walk->bar,
346                                        outputs_walk->bargc,
347                                        outputs_walk->rect.w - get_string_width(text, glyph_count) - 4,
348                                        font_height + 1,
349                                        (xcb_char2b_t*) text);
350
351             xcb_generic_error_t *err = xcb_request_check(xcb_connection, cookie);
352
353             if (err != NULL) {
354                 printf("XCB-Error: %d\n", err->error_code);
355             }
356         }
357         i3_ws *ws_walk;
358         TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
359             printf("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);
360             uint32_t color = get_colorpixel("240000");
361             if (ws_walk->visible) {
362                 color = get_colorpixel("480000");
363             }
364             if (ws_walk->urgent) {
365                 printf("WS %s is urgent!\n", ws_walk->name);
366                 color = get_colorpixel("002400");
367             }
368             xcb_change_gc(xcb_connection,
369                           outputs_walk->bargc,
370                           XCB_GC_FOREGROUND,
371                           &color);
372             xcb_change_gc(xcb_connection,
373                           outputs_walk->bargc,
374                           XCB_GC_BACKGROUND,
375                           &color);
376             xcb_rectangle_t rect = { i + 1, 1, ws_walk->name_width + 8, font_height + 4 };
377             xcb_poly_fill_rectangle(xcb_connection,
378                                     outputs_walk->bar,
379                                     outputs_walk->bargc,
380                                     1,
381                                     &rect);
382             color = get_colorpixel("FFFFFF");
383             xcb_change_gc(xcb_connection,
384                           outputs_walk->bargc,
385                           XCB_GC_FOREGROUND,
386                           &color);
387             xcb_image_text_16(xcb_connection,
388                               ws_walk->name_glyphs,
389                               outputs_walk->bar,
390                               outputs_walk->bargc,
391                               i + 5, font_height + 1,
392                               ws_walk->ucs2_name);
393             i += 10 + ws_walk->name_width;
394         }
395
396         i = 0;
397     }
398 }