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