]> git.sur5r.net Git - i3/i3/blob - i3bar/src/xcb.c
Inset the urgent background of a status block for consistency with workspace buttons.
[i3/i3] / i3bar / src / xcb.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3bar - an xcb-based status- and ws-bar for i3
5  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
6  *
7  * xcb.c: Communicating with X
8  *
9  */
10 #include <xcb/xcb.h>
11 #include <xcb/xkb.h>
12 #include <xcb/xproto.h>
13 #include <xcb/xcb_aux.h>
14
15 #ifdef XCB_COMPAT
16 #include "xcb_compat.h"
17 #endif
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <i3/ipc.h>
25 #include <ev.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <err.h>
29
30 #include <X11/Xlib.h>
31 #include <X11/XKBlib.h>
32 #include <X11/extensions/XKB.h>
33
34 #include "common.h"
35 #include "libi3.h"
36
37 /* We save the Atoms in an easy to access array, indexed by an enum */
38 enum {
39 #define ATOM_DO(name) name,
40 #include "xcb_atoms.def"
41     NUM_ATOMS
42 };
43
44 xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
45 xcb_atom_t atoms[NUM_ATOMS];
46
47 /* Variables, that are the same for all functions at all times */
48 xcb_connection_t *xcb_connection;
49 int screen;
50 xcb_screen_t *root_screen;
51 xcb_window_t xcb_root;
52
53 /* selection window for tray support */
54 static xcb_window_t selwin = XCB_NONE;
55 static xcb_intern_atom_reply_t *tray_reply = NULL;
56
57 /* This is needed for integration with libi3 */
58 xcb_connection_t *conn;
59
60 /* The font we'll use */
61 static i3Font font;
62
63 /* Overall height of the bar (based on font size) */
64 int bar_height;
65
66 /* These are only relevant for XKB, which we only need for grabbing modifiers */
67 int xkb_base;
68 int mod_pressed = 0;
69
70 /* Because the statusline is the same on all outputs, we have
71  * global buffer to render it on */
72 xcb_gcontext_t statusline_ctx;
73 xcb_gcontext_t statusline_clear;
74 xcb_pixmap_t statusline_pm;
75 uint32_t statusline_width;
76
77 /* Event-Watchers, to interact with the user */
78 ev_prepare *xcb_prep;
79 ev_check *xcb_chk;
80 ev_io *xcb_io;
81 ev_io *xkb_io;
82
83 /* The name of current binding mode */
84 static mode binding;
85
86 /* Indicates whether a new binding mode was recently activated */
87 bool activated_mode = false;
88
89 /* The parsed colors */
90 struct xcb_colors_t {
91     uint32_t bar_fg;
92     uint32_t bar_bg;
93     uint32_t sep_fg;
94     uint32_t active_ws_fg;
95     uint32_t active_ws_bg;
96     uint32_t active_ws_border;
97     uint32_t inactive_ws_fg;
98     uint32_t inactive_ws_bg;
99     uint32_t inactive_ws_border;
100     uint32_t urgent_ws_bg;
101     uint32_t urgent_ws_fg;
102     uint32_t urgent_ws_border;
103     uint32_t focus_ws_bg;
104     uint32_t focus_ws_fg;
105     uint32_t focus_ws_border;
106 };
107 struct xcb_colors_t colors;
108
109 /* We define xcb_request_failed as a macro to include the relevant line-number */
110 #define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
111 int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
112     xcb_generic_error_t *err;
113     if ((err = xcb_request_check(xcb_connection, cookie)) != NULL) {
114         fprintf(stderr, "[%s:%d] ERROR: %s. X Error Code: %d\n", __FILE__, line, err_msg, err->error_code);
115         return err->error_code;
116     }
117     return 0;
118 }
119
120 /*
121  * Redraws the statusline to the buffer
122  *
123  */
124 void refresh_statusline(void) {
125     struct status_block *block;
126
127     uint32_t old_statusline_width = statusline_width;
128     statusline_width = 0;
129
130     /* Predict the text width of all blocks (in pixels). */
131     TAILQ_FOREACH(block, &statusline_head, blocks) {
132         if (i3string_get_num_bytes(block->full_text) == 0)
133             continue;
134
135         block->width = predict_text_width(block->full_text);
136
137         /* Compute offset and append for text aligment in min_width. */
138         if (block->min_width <= block->width) {
139             block->x_offset = 0;
140             block->x_append = 0;
141         } else {
142             uint32_t padding_width = block->min_width - block->width;
143             switch (block->align) {
144                 case ALIGN_LEFT:
145                     block->x_append = padding_width;
146                     break;
147                 case ALIGN_RIGHT:
148                     block->x_offset = padding_width;
149                     break;
150                 case ALIGN_CENTER:
151                     block->x_offset = padding_width / logical_px(2);
152                     block->x_append = padding_width / logical_px(2) + padding_width % logical_px(2);
153                     break;
154             }
155         }
156
157         /* If this is not the last block, add some pixels for a separator. */
158         if (TAILQ_NEXT(block, blocks) != NULL)
159             statusline_width += block->sep_block_width;
160
161         statusline_width += block->width + block->x_offset + block->x_append;
162     }
163
164     /* If the statusline is bigger than our screen we need to make sure that
165      * the pixmap provides enough space, so re-allocate if the width grew */
166     if (statusline_width > root_screen->width_in_pixels &&
167         statusline_width > old_statusline_width)
168         realloc_sl_buffer();
169
170     /* Clear the statusline pixmap. */
171     xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height};
172     xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
173
174     /* Draw the text of each block. */
175     uint32_t x = 0;
176     TAILQ_FOREACH(block, &statusline_head, blocks) {
177         if (i3string_get_num_bytes(block->full_text) == 0)
178             continue;
179         uint32_t fg_color;
180
181         /* If this block is urgent, draw it with the defined color and border. */
182         if (block->urgent) {
183             fg_color = colors.urgent_ws_fg;
184
185             uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
186
187             /* Draw the background */
188             uint32_t bg_color = colors.urgent_ws_bg;
189             uint32_t bg_values[] = { bg_color, bg_color };
190             xcb_change_gc(xcb_connection, statusline_ctx, mask, bg_values);
191
192             /* The urgent background “overshoots” by 2 px so that the text that
193              * is printed onto it will not be look so cut off. */
194             xcb_rectangle_t bg_rect = { x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2) };
195             xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect);
196         } else {
197             fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
198         }
199
200         set_font_colors(statusline_ctx, fg_color, colors.bar_bg);
201         draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width);
202         x += block->width + block->sep_block_width + block->x_offset + block->x_append;
203
204         if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) {
205             /* This is not the last block, draw a separator. */
206             uint32_t sep_offset = block->sep_block_width / 2 + block->sep_block_width % 2;
207             uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
208             uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
209             xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
210             xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
211                           statusline_ctx, 2,
212                           (xcb_point_t[]) { { x - sep_offset, logical_px(4) },
213                                             { x - sep_offset, bar_height - logical_px(4) } });
214         }
215     }
216 }
217
218 /*
219  * Hides all bars (unmaps them)
220  *
221  */
222 void hide_bars(void) {
223     if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW && config.hide_on_modifier == M_HIDE)) {
224         return;
225     }
226
227     i3_output *walk;
228     SLIST_FOREACH(walk, outputs, slist) {
229         if (!walk->active) {
230             continue;
231         }
232         xcb_unmap_window(xcb_connection, walk->bar);
233     }
234     stop_child();
235 }
236
237 /*
238  * Unhides all bars (maps them)
239  *
240  */
241 void unhide_bars(void) {
242     if (config.hide_on_modifier != M_HIDE) {
243         return;
244     }
245
246     i3_output *walk;
247     xcb_void_cookie_t cookie;
248     uint32_t mask;
249     uint32_t values[5];
250
251     cont_child();
252
253     SLIST_FOREACH(walk, outputs, slist) {
254         if (walk->bar == XCB_NONE) {
255             continue;
256         }
257         mask = XCB_CONFIG_WINDOW_X |
258                XCB_CONFIG_WINDOW_Y |
259                XCB_CONFIG_WINDOW_WIDTH |
260                XCB_CONFIG_WINDOW_HEIGHT |
261                XCB_CONFIG_WINDOW_STACK_MODE;
262         values[0] = walk->rect.x;
263         if (config.position == POS_TOP)
264             values[1] = walk->rect.y;
265         else
266             values[1] = walk->rect.y + walk->rect.h - bar_height;
267         values[2] = walk->rect.w;
268         values[3] = bar_height;
269         values[4] = XCB_STACK_MODE_ABOVE;
270         DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
271         cookie = xcb_configure_window_checked(xcb_connection,
272                                               walk->bar,
273                                               mask,
274                                               values);
275
276         if (xcb_request_failed(cookie, "Could not reconfigure window")) {
277             exit(EXIT_FAILURE);
278         }
279         xcb_map_window(xcb_connection, walk->bar);
280     }
281 }
282
283 /*
284  * Parse the colors into a format that we can use
285  *
286  */
287 void init_colors(const struct xcb_color_strings_t *new_colors) {
288 #define PARSE_COLOR(name, def)                                                   \
289     do {                                                                         \
290         colors.name = get_colorpixel(new_colors->name ? new_colors->name : def); \
291     } while (0)
292     PARSE_COLOR(bar_fg, "#FFFFFF");
293     PARSE_COLOR(bar_bg, "#000000");
294     PARSE_COLOR(sep_fg, "#666666");
295     PARSE_COLOR(active_ws_fg, "#FFFFFF");
296     PARSE_COLOR(active_ws_bg, "#333333");
297     PARSE_COLOR(active_ws_border, "#333333");
298     PARSE_COLOR(inactive_ws_fg, "#888888");
299     PARSE_COLOR(inactive_ws_bg, "#222222");
300     PARSE_COLOR(inactive_ws_border, "#333333");
301     PARSE_COLOR(urgent_ws_fg, "#FFFFFF");
302     PARSE_COLOR(urgent_ws_bg, "#900000");
303     PARSE_COLOR(urgent_ws_border, "#2f343a");
304     PARSE_COLOR(focus_ws_fg, "#FFFFFF");
305     PARSE_COLOR(focus_ws_bg, "#285577");
306     PARSE_COLOR(focus_ws_border, "#4c7899");
307 #undef PARSE_COLOR
308
309     init_tray_colors();
310     xcb_flush(xcb_connection);
311 }
312
313 /*
314  * Handle a button-press-event (i.e. a mouse click on one of our bars).
315  * We determine, whether the click occured on a ws-button or if the scroll-
316  * wheel was used and change the workspace appropriately
317  *
318  */
319 void handle_button(xcb_button_press_event_t *event) {
320     i3_ws *cur_ws;
321
322     /* Determine, which bar was clicked */
323     i3_output *walk;
324     xcb_window_t bar = event->event;
325     SLIST_FOREACH(walk, outputs, slist) {
326         if (walk->bar == bar) {
327             break;
328         }
329     }
330
331     if (walk == NULL) {
332         DLOG("Unknown Bar klicked!\n");
333         return;
334     }
335
336     /* TODO: Move this to extern get_ws_for_output() */
337     TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
338         if (cur_ws->visible) {
339             break;
340         }
341     }
342
343     if (cur_ws == NULL) {
344         DLOG("No Workspace active?\n");
345         return;
346     }
347
348     int32_t x = event->event_x >= 0 ? event->event_x : 0;
349     int32_t original_x = x;
350
351     DLOG("Got Button %d\n", event->detail);
352
353     if (child_want_click_events()) {
354         /* If the child asked for click events,
355          * check if a status block has been clicked. */
356
357         /* First calculate width of tray area */
358         trayclient *trayclient;
359         int tray_width = 0;
360         TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
361             if (!trayclient->mapped)
362                 continue;
363             tray_width += (font.height + logical_px(2));
364         }
365
366         int block_x = 0, last_block_x;
367         int offset = (walk->rect.w - (statusline_width + tray_width)) - logical_px(10);
368
369         x = original_x - offset;
370         if (x >= 0) {
371             struct status_block *block;
372
373             TAILQ_FOREACH(block, &statusline_head, blocks) {
374                 last_block_x = block_x;
375                 block_x += block->width + block->x_offset + block->x_append;
376
377                 if (x <= block_x && x >= last_block_x) {
378                     send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
379                     return;
380                 }
381             }
382         }
383         x = original_x;
384     }
385
386     switch (event->detail) {
387         case 4:
388             /* Mouse wheel up. We select the previous ws, if any.
389              * If there is no more workspace, don’t even send the workspace
390              * command, otherwise (with workspace auto_back_and_forth) we’d end
391              * up on the wrong workspace. */
392
393             /* If `wheel_up_cmd [COMMAND]` was specified, it should override
394              * the default behavior */
395             if (config.wheel_up_cmd) {
396                 i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_up_cmd);
397                 return;
398             }
399
400             if (cur_ws == TAILQ_FIRST(walk->workspaces))
401                 return;
402
403             cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
404             break;
405         case 5:
406             /* Mouse wheel down. We select the next ws, if any.
407              * If there is no more workspace, don’t even send the workspace
408              * command, otherwise (with workspace auto_back_and_forth) we’d end
409              * up on the wrong workspace. */
410
411             /* if `wheel_down_cmd [COMMAND]` was specified, it should override
412              * the default behavior */
413             if (config.wheel_down_cmd) {
414                 i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_down_cmd);
415                 return;
416             }
417
418             if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
419                 return;
420
421             cur_ws = TAILQ_NEXT(cur_ws, tailq);
422             break;
423         case 1:
424             /* Check if this event regards a workspace button */
425             TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
426                 DLOG("x = %d\n", x);
427                 if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
428                     break;
429                 }
430                 x -= cur_ws->name_width + logical_px(11);
431             }
432
433             /* Otherwise, focus our currently visible workspace if it is not
434              * already focused */
435             if (cur_ws == NULL) {
436                 TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
437                     if (cur_ws->visible && !cur_ws->focused)
438                         break;
439                 }
440             }
441
442             /* if there is nothing to focus, we are done */
443             if (cur_ws == NULL)
444                 return;
445
446             break;
447         default:
448             return;
449     }
450
451     /* To properly handle workspace names with double quotes in them, we need
452      * to escape the double quotes. Unfortunately, that’s rather ugly in C: We
453      * first count the number of double quotes, then we allocate a large enough
454      * buffer, then we copy character by character. */
455     int num_quotes = 0;
456     size_t namelen = 0;
457     const char *utf8_name = cur_ws->canonical_name;
458     for (const char *walk = utf8_name; *walk != '\0'; walk++) {
459         if (*walk == '"')
460             num_quotes++;
461         /* While we’re looping through the name anyway, we can save one
462          * strlen(). */
463         namelen++;
464     }
465
466     const size_t len = namelen + strlen("workspace \"\"") + 1;
467     char *buffer = scalloc(len + num_quotes);
468     strncpy(buffer, "workspace \"", strlen("workspace \""));
469     size_t inpos, outpos;
470     for (inpos = 0, outpos = strlen("workspace \"");
471          inpos < namelen;
472          inpos++, outpos++) {
473         if (utf8_name[inpos] == '"') {
474             buffer[outpos] = '\\';
475             outpos++;
476         }
477         buffer[outpos] = utf8_name[inpos];
478     }
479     buffer[outpos] = '"';
480     i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
481     free(buffer);
482 }
483
484 /*
485  * Adjusts the size of the tray window and alignment of the tray clients by
486  * configuring their respective x coordinates. To be called when mapping or
487  * unmapping a tray client window.
488  *
489  */
490 static void configure_trayclients(void) {
491     trayclient *trayclient;
492     i3_output *output;
493     SLIST_FOREACH(output, outputs, slist) {
494         if (!output->active)
495             continue;
496
497         int clients = 0;
498         TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
499             if (!trayclient->mapped)
500                 continue;
501             clients++;
502
503             DLOG("Configuring tray window %08x to x=%d\n",
504                  trayclient->win, output->rect.w - (clients * (font.height + logical_px(2))));
505             uint32_t x = output->rect.w - (clients * (font.height + logical_px(2)));
506             xcb_configure_window(xcb_connection,
507                                  trayclient->win,
508                                  XCB_CONFIG_WINDOW_X,
509                                  &x);
510         }
511     }
512 }
513
514 /*
515  * Handles ClientMessages (messages sent from another client directly to us).
516  *
517  * At the moment, only the tray window will receive client messages. All
518  * supported client messages currently are _NET_SYSTEM_TRAY_OPCODE.
519  *
520  */
521 static void handle_client_message(xcb_client_message_event_t *event) {
522     if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] &&
523         event->format == 32) {
524         DLOG("_NET_SYSTEM_TRAY_OPCODE received\n");
525         /* event->data.data32[0] is the timestamp */
526         uint32_t op = event->data.data32[1];
527         uint32_t mask;
528         uint32_t values[2];
529         if (op == SYSTEM_TRAY_REQUEST_DOCK) {
530             xcb_window_t client = event->data.data32[2];
531
532             /* Listen for PropertyNotify events to get the most recent value of
533              * the XEMBED_MAPPED atom, also listen for UnmapNotify events */
534             mask = XCB_CW_EVENT_MASK;
535             values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE |
536                         XCB_EVENT_MASK_STRUCTURE_NOTIFY;
537             xcb_change_window_attributes(xcb_connection,
538                                          client,
539                                          mask,
540                                          values);
541
542             /* Request the _XEMBED_INFO property. The XEMBED specification
543              * (which is referred by the tray specification) says this *has* to
544              * be set, but VLC does not set it… */
545             bool map_it = true;
546             int xe_version = 1;
547             xcb_get_property_cookie_t xembedc;
548             xcb_generic_error_t *error;
549             xembedc = xcb_get_property(xcb_connection,
550                                        0,
551                                        client,
552                                        atoms[_XEMBED_INFO],
553                                        XCB_GET_PROPERTY_TYPE_ANY,
554                                        0,
555                                        2 * 32);
556
557             xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection,
558                                                                        xembedc,
559                                                                        &error);
560             if (error != NULL) {
561                 ELOG("Error getting _XEMBED_INFO property: error_code %d\n",
562                      error->error_code);
563                 free(error);
564                 return;
565             }
566             if (xembedr != NULL && xembedr->length != 0) {
567                 DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length);
568                 uint32_t *xembed = xcb_get_property_value(xembedr);
569                 DLOG("xembed version = %d\n", xembed[0]);
570                 DLOG("xembed flags = %d\n", xembed[1]);
571                 map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
572                 xe_version = xembed[0];
573                 if (xe_version > 1)
574                     xe_version = 1;
575                 free(xembedr);
576             } else {
577                 ELOG("Window %08x violates the XEMBED protocol, _XEMBED_INFO not set\n", client);
578             }
579
580             DLOG("X window %08x requested docking\n", client);
581             i3_output *walk, *output = NULL;
582             SLIST_FOREACH(walk, outputs, slist) {
583                 if (!walk->active)
584                     continue;
585                 if (config.tray_output) {
586                     if ((strcasecmp(walk->name, config.tray_output) != 0) &&
587                         (!walk->primary || strcasecmp("primary", config.tray_output) != 0))
588                         continue;
589                 }
590
591                 DLOG("using output %s\n", walk->name);
592                 output = walk;
593                 break;
594             }
595             /* In case of tray_output == primary and there is no primary output
596              * configured, we fall back to the first available output. */
597             if (output == NULL &&
598                 config.tray_output &&
599                 strcasecmp("primary", config.tray_output) == 0) {
600                 SLIST_FOREACH(walk, outputs, slist) {
601                     if (!walk->active)
602                         continue;
603                     DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
604                     output = walk;
605                     break;
606                 }
607             }
608             if (output == NULL) {
609                 ELOG("No output found\n");
610                 return;
611             }
612             xcb_reparent_window(xcb_connection,
613                                 client,
614                                 output->bar,
615                                 output->rect.w - font.height - 2,
616                                 2);
617             /* We reconfigure the window to use a reasonable size. The systray
618              * specification explicitly says:
619              *   Tray icons may be assigned any size by the system tray, and
620              *   should do their best to cope with any size effectively
621              */
622             mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
623             values[0] = font.height;
624             values[1] = font.height;
625             xcb_configure_window(xcb_connection,
626                                  client,
627                                  mask,
628                                  values);
629
630             /* send the XEMBED_EMBEDDED_NOTIFY message */
631             void *event = scalloc(32);
632             xcb_client_message_event_t *ev = event;
633             ev->response_type = XCB_CLIENT_MESSAGE;
634             ev->window = client;
635             ev->type = atoms[_XEMBED];
636             ev->format = 32;
637             ev->data.data32[0] = XCB_CURRENT_TIME;
638             ev->data.data32[1] = atoms[XEMBED_EMBEDDED_NOTIFY];
639             ev->data.data32[2] = output->bar;
640             ev->data.data32[3] = xe_version;
641             xcb_send_event(xcb_connection,
642                            0,
643                            client,
644                            XCB_EVENT_MASK_NO_EVENT,
645                            (char *)ev);
646             free(event);
647
648             /* Put the client inside the save set. Upon termination (whether
649              * killed or normal exit does not matter) of i3bar, these clients
650              * will be correctly reparented to their most closest living
651              * ancestor. Without this, tray icons might die when i3bar
652              * exits/crashes. */
653             xcb_change_save_set(xcb_connection, XCB_SET_MODE_INSERT, client);
654
655             trayclient *tc = smalloc(sizeof(trayclient));
656             tc->win = client;
657             tc->xe_version = xe_version;
658             tc->mapped = false;
659             TAILQ_INSERT_TAIL(output->trayclients, tc, tailq);
660
661             if (map_it) {
662                 DLOG("Mapping dock client\n");
663                 xcb_map_window(xcb_connection, client);
664             } else {
665                 DLOG("Not mapping dock client yet\n");
666             }
667             /* Trigger an update to copy the statusline text to the appropriate
668              * position */
669             configure_trayclients();
670             draw_bars(false);
671         }
672     }
673 }
674
675 /*
676  * Handles DestroyNotify events by removing the tray client from the data
677  * structure. According to the XEmbed protocol, this is one way for a tray
678  * client to finish the protocol. After this event is received, there is no
679  * further interaction with the tray client.
680  *
681  * See: http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
682  *
683  */
684 static void handle_destroy_notify(xcb_destroy_notify_event_t *event) {
685     DLOG("DestroyNotify for window = %08x, event = %08x\n", event->window, event->event);
686
687     i3_output *walk;
688     SLIST_FOREACH(walk, outputs, slist) {
689         if (!walk->active)
690             continue;
691         DLOG("checking output %s\n", walk->name);
692         trayclient *trayclient;
693         TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
694             if (trayclient->win != event->window)
695                 continue;
696
697             DLOG("Removing tray client with window ID %08x\n", event->window);
698             TAILQ_REMOVE(walk->trayclients, trayclient, tailq);
699
700             /* Trigger an update, we now have more space for the statusline */
701             configure_trayclients();
702             draw_bars(false);
703             return;
704         }
705     }
706 }
707
708 /*
709  * Handles MapNotify events. These events happen when a tray client shows its
710  * window. We respond by realigning the tray clients.
711  *
712  */
713 static void handle_map_notify(xcb_map_notify_event_t *event) {
714     DLOG("MapNotify for window = %08x, event = %08x\n", event->window, event->event);
715
716     i3_output *walk;
717     SLIST_FOREACH(walk, outputs, slist) {
718         if (!walk->active)
719             continue;
720         DLOG("checking output %s\n", walk->name);
721         trayclient *trayclient;
722         TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
723             if (trayclient->win != event->window)
724                 continue;
725
726             DLOG("Tray client mapped (window ID %08x). Adjusting tray.\n", event->window);
727             trayclient->mapped = true;
728
729             /* Trigger an update, we now have more space for the statusline */
730             configure_trayclients();
731             draw_bars(false);
732             return;
733         }
734     }
735 }
736 /*
737  * Handles UnmapNotify events. These events happen when a tray client hides its
738  * window. We respond by realigning the tray clients.
739  *
740  */
741 static void handle_unmap_notify(xcb_unmap_notify_event_t *event) {
742     DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event);
743
744     i3_output *walk;
745     SLIST_FOREACH(walk, outputs, slist) {
746         if (!walk->active)
747             continue;
748         DLOG("checking output %s\n", walk->name);
749         trayclient *trayclient;
750         TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
751             if (trayclient->win != event->window)
752                 continue;
753
754             DLOG("Tray client unmapped (window ID %08x). Adjusting tray.\n", event->window);
755             trayclient->mapped = false;
756
757             /* Trigger an update, we now have more space for the statusline */
758             configure_trayclients();
759             draw_bars(false);
760             return;
761         }
762     }
763 }
764
765 /*
766  * Handle PropertyNotify messages. Currently only the _XEMBED_INFO property is
767  * handled, which tells us whether a dock client should be mapped or unmapped.
768  *
769  */
770 static void handle_property_notify(xcb_property_notify_event_t *event) {
771     DLOG("PropertyNotify\n");
772     if (event->atom == atoms[_XEMBED_INFO] &&
773         event->state == XCB_PROPERTY_NEW_VALUE) {
774         DLOG("xembed_info updated\n");
775         trayclient *trayclient = NULL, *walk;
776         i3_output *o_walk;
777         SLIST_FOREACH(o_walk, outputs, slist) {
778             if (!o_walk->active)
779                 continue;
780
781             TAILQ_FOREACH(walk, o_walk->trayclients, tailq) {
782                 if (walk->win != event->window)
783                     continue;
784                 trayclient = walk;
785                 break;
786             }
787
788             if (trayclient)
789                 break;
790         }
791         if (!trayclient) {
792             ELOG("PropertyNotify received for unknown window %08x\n",
793                  event->window);
794             return;
795         }
796         xcb_get_property_cookie_t xembedc;
797         xembedc = xcb_get_property_unchecked(xcb_connection,
798                                              0,
799                                              trayclient->win,
800                                              atoms[_XEMBED_INFO],
801                                              XCB_GET_PROPERTY_TYPE_ANY,
802                                              0,
803                                              2 * 32);
804
805         xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection,
806                                                                    xembedc,
807                                                                    NULL);
808         if (xembedr == NULL || xembedr->length == 0) {
809             DLOG("xembed_info unset\n");
810             return;
811         }
812
813         DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length);
814         uint32_t *xembed = xcb_get_property_value(xembedr);
815         DLOG("xembed version = %d\n", xembed[0]);
816         DLOG("xembed flags = %d\n", xembed[1]);
817         bool map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
818         DLOG("map-state now %d\n", map_it);
819         if (trayclient->mapped && !map_it) {
820             /* need to unmap the window */
821             xcb_unmap_window(xcb_connection, trayclient->win);
822         } else if (!trayclient->mapped && map_it) {
823             /* need to map the window */
824             xcb_map_window(xcb_connection, trayclient->win);
825         }
826         free(xembedr);
827     }
828 }
829
830 /*
831  * Handle ConfigureRequests by denying them and sending the client a
832  * ConfigureNotify with its actual size.
833  *
834  */
835 static void handle_configure_request(xcb_configure_request_event_t *event) {
836     DLOG("ConfigureRequest for window = %08x\n", event->window);
837
838     trayclient *trayclient;
839     i3_output *output;
840     SLIST_FOREACH(output, outputs, slist) {
841         if (!output->active)
842             continue;
843
844         int clients = 0;
845         TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
846             if (!trayclient->mapped)
847                 continue;
848             clients++;
849
850             if (trayclient->win != event->window)
851                 continue;
852
853             xcb_rectangle_t rect;
854             rect.x = output->rect.w - (clients * (font.height + 2));
855             rect.y = 2;
856             rect.width = font.height;
857             rect.height = font.height;
858
859             DLOG("This is a tray window. x = %d\n", rect.x);
860             fake_configure_notify(xcb_connection, rect, event->window, 0);
861             return;
862         }
863     }
864
865     DLOG("WARNING: Could not find corresponding tray window.\n");
866 }
867
868 /*
869  * This function is called immediately before the main loop locks. We flush xcb
870  * then (and only then)
871  *
872  */
873 void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
874     xcb_flush(xcb_connection);
875 }
876
877 /*
878  * This function is called immediately after the main loop locks, so when one
879  * of the watchers registered an event.
880  * We check whether an X-Event arrived and handle it.
881  *
882  */
883 void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
884     xcb_generic_event_t *event;
885
886     if (xcb_connection_has_error(xcb_connection)) {
887         ELOG("X11 connection was closed unexpectedly - maybe your X server terminated / crashed?\n");
888         exit(1);
889     }
890
891     while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
892         int type = (event->response_type & ~0x80);
893
894         if (type == xkb_base && xkb_base > -1) {
895             DLOG("received an xkb event\n");
896
897             xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
898             if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
899                 int modstate = state->mods & config.modifier;
900
901 #define DLOGMOD(modmask, status)                        \
902     do {                                                \
903         switch (modmask) {                              \
904             case ShiftMask:                             \
905                 DLOG("ShiftMask got " #status "!\n");   \
906                 break;                                  \
907             case ControlMask:                           \
908                 DLOG("ControlMask got " #status "!\n"); \
909                 break;                                  \
910             case Mod1Mask:                              \
911                 DLOG("Mod1Mask got " #status "!\n");    \
912                 break;                                  \
913             case Mod2Mask:                              \
914                 DLOG("Mod2Mask got " #status "!\n");    \
915                 break;                                  \
916             case Mod3Mask:                              \
917                 DLOG("Mod3Mask got " #status "!\n");    \
918                 break;                                  \
919             case Mod4Mask:                              \
920                 DLOG("Mod4Mask got " #status "!\n");    \
921                 break;                                  \
922             case Mod5Mask:                              \
923                 DLOG("Mod5Mask got " #status "!\n");    \
924                 break;                                  \
925         }                                               \
926     } while (0)
927
928                 if (modstate != mod_pressed) {
929                     if (modstate == 0) {
930                         DLOGMOD(config.modifier, released);
931                         if (!activated_mode)
932                             hide_bars();
933                     } else {
934                         DLOGMOD(config.modifier, pressed);
935                         activated_mode = false;
936                         unhide_bars();
937                     }
938                     mod_pressed = modstate;
939                 }
940 #undef DLOGMOD
941             }
942
943             free(event);
944             continue;
945         }
946
947         switch (type) {
948             case XCB_EXPOSE:
949                 /* Expose-events happen, when the window needs to be redrawn */
950                 redraw_bars();
951                 break;
952             case XCB_BUTTON_PRESS:
953                 /* Button-press-events are mouse-buttons clicked on one of our bars */
954                 handle_button((xcb_button_press_event_t *)event);
955                 break;
956             case XCB_CLIENT_MESSAGE:
957                 /* Client messages are used for client-to-client communication, for
958                  * example system tray widgets talk to us directly via client messages. */
959                 handle_client_message((xcb_client_message_event_t *)event);
960                 break;
961             case XCB_DESTROY_NOTIFY:
962                 /* DestroyNotify signifies the end of the XEmbed protocol */
963                 handle_destroy_notify((xcb_destroy_notify_event_t *)event);
964                 break;
965             case XCB_UNMAP_NOTIFY:
966                 /* UnmapNotify is received when a tray client hides its window. */
967                 handle_unmap_notify((xcb_unmap_notify_event_t *)event);
968                 break;
969             case XCB_MAP_NOTIFY:
970                 handle_map_notify((xcb_map_notify_event_t *)event);
971                 break;
972             case XCB_PROPERTY_NOTIFY:
973                 /* PropertyNotify */
974                 handle_property_notify((xcb_property_notify_event_t *)event);
975                 break;
976             case XCB_CONFIGURE_REQUEST:
977                 /* ConfigureRequest, sent by a tray child */
978                 handle_configure_request((xcb_configure_request_event_t *)event);
979                 break;
980         }
981         free(event);
982     }
983 }
984
985 /*
986  * Dummy Callback. We only need this, so that the Prepare- and Check-Watchers
987  * are triggered
988  *
989  */
990 void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
991 }
992
993 /*
994  * Early initialization of the connection to X11: Everything which does not
995  * depend on 'config'.
996  *
997  */
998 char *init_xcb_early() {
999     /* FIXME: xcb_connect leaks Memory */
1000     xcb_connection = xcb_connect(NULL, &screen);
1001     if (xcb_connection_has_error(xcb_connection)) {
1002         ELOG("Cannot open display\n");
1003         exit(EXIT_FAILURE);
1004     }
1005     conn = xcb_connection;
1006     DLOG("Connected to xcb\n");
1007
1008 /* We have to request the atoms we need */
1009 #define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
1010 #include "xcb_atoms.def"
1011
1012     root_screen = xcb_aux_get_screen(xcb_connection, screen);
1013     xcb_root = root_screen->root;
1014
1015     /* We draw the statusline to a seperate pixmap, because it looks the same on all bars and
1016      * this way, we can choose to crop it */
1017     uint32_t mask = XCB_GC_FOREGROUND;
1018     uint32_t vals[] = {colors.bar_bg, colors.bar_bg};
1019
1020     statusline_clear = xcb_generate_id(xcb_connection);
1021     xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
1022                                                                statusline_clear,
1023                                                                xcb_root,
1024                                                                mask,
1025                                                                vals);
1026
1027     statusline_ctx = xcb_generate_id(xcb_connection);
1028     xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
1029                                                             statusline_ctx,
1030                                                             xcb_root,
1031                                                             0,
1032                                                             NULL);
1033
1034     statusline_pm = xcb_generate_id(xcb_connection);
1035     xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
1036                                                                root_screen->root_depth,
1037                                                                statusline_pm,
1038                                                                xcb_root,
1039                                                                root_screen->width_in_pixels,
1040                                                                root_screen->height_in_pixels);
1041
1042     /* The various Watchers to communicate with xcb */
1043     xcb_io = smalloc(sizeof(ev_io));
1044     xcb_prep = smalloc(sizeof(ev_prepare));
1045     xcb_chk = smalloc(sizeof(ev_check));
1046
1047     ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
1048     ev_prepare_init(xcb_prep, &xcb_prep_cb);
1049     ev_check_init(xcb_chk, &xcb_chk_cb);
1050
1051     ev_io_start(main_loop, xcb_io);
1052     ev_prepare_start(main_loop, xcb_prep);
1053     ev_check_start(main_loop, xcb_chk);
1054
1055     /* Now we get the atoms and save them in a nice data structure */
1056     get_atoms();
1057
1058     char *path = root_atom_contents("I3_SOCKET_PATH", xcb_connection, screen);
1059
1060     if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
1061         xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
1062         xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
1063         exit(EXIT_FAILURE);
1064     }
1065
1066     return path;
1067 }
1068
1069 /*
1070  * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key-events
1071  * involving that modifier, we sadly have to use xkb which is not yet fully supported
1072  * in xcb.
1073  *
1074  */
1075 void register_xkb_keyevents() {
1076     const xcb_query_extension_reply_t *extreply;
1077     extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
1078     if (!extreply->present) {
1079         ELOG("xkb is not present on this server\n");
1080         exit(EXIT_FAILURE);
1081     }
1082     DLOG("initializing xcb-xkb\n");
1083     xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
1084     xcb_xkb_select_events(conn,
1085                           XCB_XKB_ID_USE_CORE_KBD,
1086                           XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
1087                           0,
1088                           XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
1089                           0xff,
1090                           0xff,
1091                           NULL);
1092     xkb_base = extreply->first_event;
1093 }
1094
1095 /*
1096  * Deregister from xkb keyevents.
1097  *
1098  */
1099 void deregister_xkb_keyevents() {
1100     xcb_xkb_select_events(conn,
1101                           XCB_XKB_ID_USE_CORE_KBD,
1102                           0,
1103                           0,
1104                           0,
1105                           0xff,
1106                           0xff,
1107                           NULL);
1108 }
1109
1110 /*
1111  * Initialization which depends on 'config' being usable. Called after the
1112  * configuration has arrived.
1113  *
1114  */
1115 void init_xcb_late(char *fontname) {
1116     if (fontname == NULL)
1117         fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
1118
1119     /* Load the font */
1120     font = load_font(fontname, true);
1121     set_font(&font);
1122     DLOG("Calculated Font-height: %d\n", font.height);
1123     bar_height = font.height + logical_px(6);
1124
1125     xcb_flush(xcb_connection);
1126
1127     if (config.hide_on_modifier == M_HIDE)
1128         register_xkb_keyevents();
1129 }
1130
1131 /*
1132  * Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the
1133  * selection.
1134  *
1135  */
1136 static void send_tray_clientmessage(void) {
1137     uint8_t buffer[32] = {0};
1138     xcb_client_message_event_t *ev = (xcb_client_message_event_t *)buffer;
1139
1140     ev->response_type = XCB_CLIENT_MESSAGE;
1141     ev->window = xcb_root;
1142     ev->type = atoms[MANAGER];
1143     ev->format = 32;
1144     ev->data.data32[0] = XCB_CURRENT_TIME;
1145     ev->data.data32[1] = tray_reply->atom;
1146     ev->data.data32[2] = selwin;
1147
1148     xcb_send_event(xcb_connection,
1149                    0,
1150                    xcb_root,
1151                    0xFFFFFF,
1152                    (char *)buffer);
1153 }
1154
1155 /*
1156  * Initializes tray support by requesting the appropriate _NET_SYSTEM_TRAY atom
1157  * for the X11 display we are running on, then acquiring the selection for this
1158  * atom. Afterwards, tray clients will send ClientMessages to our window.
1159  *
1160  */
1161 void init_tray(void) {
1162     DLOG("Initializing system tray functionality\n");
1163     /* request the tray manager atom for the X11 display we are running on */
1164     char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
1165     snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
1166     xcb_intern_atom_cookie_t tray_cookie;
1167     if (tray_reply == NULL)
1168         tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
1169
1170     /* tray support: we need a window to own the selection */
1171     selwin = xcb_generate_id(xcb_connection);
1172     uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT;
1173     uint32_t selval[] = {1};
1174     xcb_create_window(xcb_connection,
1175                       root_screen->root_depth,
1176                       selwin,
1177                       xcb_root,
1178                       -1, -1,
1179                       1, 1,
1180                       0,
1181                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
1182                       root_screen->root_visual,
1183                       selmask,
1184                       selval);
1185
1186     uint32_t orientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1187     /* set the atoms */
1188     xcb_change_property(xcb_connection,
1189                         XCB_PROP_MODE_REPLACE,
1190                         selwin,
1191                         atoms[_NET_SYSTEM_TRAY_ORIENTATION],
1192                         XCB_ATOM_CARDINAL,
1193                         32,
1194                         1,
1195                         &orientation);
1196
1197     init_tray_colors();
1198
1199     if (tray_reply == NULL) {
1200         if (!(tray_reply = xcb_intern_atom_reply(xcb_connection, tray_cookie, NULL))) {
1201             ELOG("Could not get atom %s\n", atomname);
1202             exit(EXIT_FAILURE);
1203         }
1204     }
1205
1206     xcb_set_selection_owner(xcb_connection,
1207                             selwin,
1208                             tray_reply->atom,
1209                             XCB_CURRENT_TIME);
1210
1211     /* Verify that we have the selection */
1212     xcb_get_selection_owner_cookie_t selcookie;
1213     xcb_get_selection_owner_reply_t *selreply;
1214
1215     selcookie = xcb_get_selection_owner(xcb_connection, tray_reply->atom);
1216     if (!(selreply = xcb_get_selection_owner_reply(xcb_connection, selcookie, NULL))) {
1217         ELOG("Could not get selection owner for %s\n", atomname);
1218         exit(EXIT_FAILURE);
1219     }
1220
1221     if (selreply->owner != selwin) {
1222         ELOG("Could not set the %s selection. "
1223              "Maybe another tray is already running?\n",
1224              atomname);
1225         /* NOTE that this error is not fatal. We just can’t provide tray
1226          * functionality */
1227         free(selreply);
1228         return;
1229     }
1230
1231     send_tray_clientmessage();
1232 }
1233
1234 /*
1235  * We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
1236  * to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
1237  * they assume a light background.
1238  * See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
1239  *
1240  */
1241 void init_tray_colors(void) {
1242     /* Convert colors.bar_fg (#rrggbb) to 16-bit RGB */
1243     const char *bar_fg = (config.colors.bar_fg ? config.colors.bar_fg : "#FFFFFF");
1244
1245     DLOG("Setting bar_fg = %s as _NET_SYSTEM_TRAY_COLORS\n", bar_fg);
1246
1247     char strgroups[3][3] = {{bar_fg[1], bar_fg[2], '\0'},
1248                             {bar_fg[3], bar_fg[4], '\0'},
1249                             {bar_fg[5], bar_fg[6], '\0'}};
1250     const uint8_t r = strtol(strgroups[0], NULL, 16);
1251     const uint8_t g = strtol(strgroups[1], NULL, 16);
1252     const uint8_t b = strtol(strgroups[2], NULL, 16);
1253
1254     const uint16_t r16 = ((uint16_t)r << 8) | r;
1255     const uint16_t g16 = ((uint16_t)g << 8) | g;
1256     const uint16_t b16 = ((uint16_t)b << 8) | b;
1257
1258     const uint32_t tray_colors[12] = {
1259         r16, g16, b16, /* foreground color */
1260         r16, g16, b16, /* error color */
1261         r16, g16, b16, /* warning color */
1262         r16, g16, b16, /* success color */
1263     };
1264
1265     xcb_change_property(xcb_connection,
1266                         XCB_PROP_MODE_REPLACE,
1267                         selwin,
1268                         atoms[_NET_SYSTEM_TRAY_COLORS],
1269                         XCB_ATOM_CARDINAL,
1270                         32,
1271                         12,
1272                         tray_colors);
1273 }
1274
1275 /*
1276  * Cleanup the xcb-stuff.
1277  * Called once, before the program terminates.
1278  *
1279  */
1280 void clean_xcb(void) {
1281     i3_output *o_walk;
1282     free_workspaces();
1283     SLIST_FOREACH(o_walk, outputs, slist) {
1284         destroy_window(o_walk);
1285         FREE(o_walk->trayclients);
1286         FREE(o_walk->workspaces);
1287         FREE(o_walk->name);
1288     }
1289     FREE_SLIST(outputs, i3_output);
1290     FREE(outputs);
1291
1292     xcb_flush(xcb_connection);
1293     xcb_aux_sync(xcb_connection);
1294     xcb_disconnect(xcb_connection);
1295
1296     ev_check_stop(main_loop, xcb_chk);
1297     ev_prepare_stop(main_loop, xcb_prep);
1298     ev_io_stop(main_loop, xcb_io);
1299
1300     FREE(xcb_chk);
1301     FREE(xcb_prep);
1302     FREE(xcb_io);
1303 }
1304
1305 /*
1306  * Get the earlier requested atoms and save them in the prepared data structure
1307  *
1308  */
1309 void get_atoms(void) {
1310     xcb_intern_atom_reply_t *reply;
1311 #define ATOM_DO(name)                                                        \
1312     reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
1313     if (reply == NULL) {                                                     \
1314         ELOG("Could not get atom %s\n", #name);                              \
1315         exit(EXIT_FAILURE);                                                  \
1316     }                                                                        \
1317     atoms[name] = reply->atom;                                               \
1318     free(reply);
1319
1320 #include "xcb_atoms.def"
1321     DLOG("Got Atoms\n");
1322 }
1323
1324 /*
1325  * Reparents all tray clients of the specified output to the root window. This
1326  * is either used when shutting down, when an output appears (xrandr --output
1327  * VGA1 --off) or when the primary output changes.
1328  *
1329  * Applications using the tray will start the protocol from the beginning again
1330  * afterwards.
1331  *
1332  */
1333 void kick_tray_clients(i3_output *output) {
1334     if (TAILQ_EMPTY(output->trayclients))
1335         return;
1336
1337     trayclient *trayclient;
1338     while (!TAILQ_EMPTY(output->trayclients)) {
1339         trayclient = TAILQ_FIRST(output->trayclients);
1340         /* Unmap, then reparent (to root) the tray client windows */
1341         xcb_unmap_window(xcb_connection, trayclient->win);
1342         xcb_reparent_window(xcb_connection,
1343                             trayclient->win,
1344                             xcb_root,
1345                             0,
1346                             0);
1347
1348         /* We remove the trayclient right here. We might receive an UnmapNotify
1349          * event afterwards, but better safe than sorry. */
1350         TAILQ_REMOVE(output->trayclients, trayclient, tailq);
1351     }
1352
1353     /* Fake a DestroyNotify so that Qt re-adds tray icons.
1354      * We cannot actually destroy the window because then Qt will not restore
1355      * its event mask on the new window. */
1356     uint8_t buffer[32] = {0};
1357     xcb_destroy_notify_event_t *event = (xcb_destroy_notify_event_t *)buffer;
1358
1359     event->response_type = XCB_DESTROY_NOTIFY;
1360     event->event = selwin;
1361     event->window = selwin;
1362
1363     xcb_send_event(conn, false, selwin, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)event);
1364
1365     send_tray_clientmessage();
1366 }
1367
1368 /*
1369  * Destroy the bar of the specified output
1370  *
1371  */
1372 void destroy_window(i3_output *output) {
1373     if (output == NULL) {
1374         return;
1375     }
1376     if (output->bar == XCB_NONE) {
1377         return;
1378     }
1379
1380     kick_tray_clients(output);
1381     xcb_destroy_window(xcb_connection, output->bar);
1382     output->bar = XCB_NONE;
1383 }
1384
1385 /*
1386  * Reallocate the statusline-buffer
1387  *
1388  */
1389 void realloc_sl_buffer(void) {
1390     DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
1391          statusline_width, root_screen->width_in_pixels);
1392     xcb_free_pixmap(xcb_connection, statusline_pm);
1393     statusline_pm = xcb_generate_id(xcb_connection);
1394     xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
1395                                                                root_screen->root_depth,
1396                                                                statusline_pm,
1397                                                                xcb_root,
1398                                                                MAX(root_screen->width_in_pixels, statusline_width),
1399                                                                bar_height);
1400
1401     uint32_t mask = XCB_GC_FOREGROUND;
1402     uint32_t vals[2] = {colors.bar_bg, colors.bar_bg};
1403     xcb_free_gc(xcb_connection, statusline_clear);
1404     statusline_clear = xcb_generate_id(xcb_connection);
1405     xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection,
1406                                                                statusline_clear,
1407                                                                xcb_root,
1408                                                                mask,
1409                                                                vals);
1410
1411     mask |= XCB_GC_BACKGROUND;
1412     vals[0] = colors.bar_fg;
1413     xcb_free_gc(xcb_connection, statusline_ctx);
1414     statusline_ctx = xcb_generate_id(xcb_connection);
1415     xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection,
1416                                                             statusline_ctx,
1417                                                             xcb_root,
1418                                                             mask,
1419                                                             vals);
1420
1421     if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
1422         xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
1423         xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
1424         exit(EXIT_FAILURE);
1425     }
1426 }
1427
1428 /*
1429  * Reconfigure all bars and create new bars for recently activated outputs
1430  *
1431  */
1432 void reconfig_windows(bool redraw_bars) {
1433     uint32_t mask;
1434     uint32_t values[5];
1435     static bool tray_configured = false;
1436
1437     i3_output *walk;
1438     SLIST_FOREACH(walk, outputs, slist) {
1439         if (!walk->active) {
1440             /* If an output is not active, we destroy its bar */
1441             /* FIXME: Maybe we rather want to unmap? */
1442             DLOG("Destroying window for output %s\n", walk->name);
1443             destroy_window(walk);
1444             continue;
1445         }
1446         if (walk->bar == XCB_NONE) {
1447             DLOG("Creating Window for output %s\n", walk->name);
1448
1449             walk->bar = xcb_generate_id(xcb_connection);
1450             walk->buffer = xcb_generate_id(xcb_connection);
1451             mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
1452             /* Black background */
1453             values[0] = colors.bar_bg;
1454             /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar-windows */
1455             values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
1456             /* We enable the following EventMask fields:
1457              * EXPOSURE, to get expose events (we have to re-draw then)
1458              * SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray
1459              *                        child windows use ConfigureWindow
1460              * BUTTON_PRESS, to handle clicks on the workspace buttons
1461              * */
1462             values[2] = XCB_EVENT_MASK_EXPOSURE |
1463                         XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
1464             if (!config.disable_ws) {
1465                 values[2] |= XCB_EVENT_MASK_BUTTON_PRESS;
1466             }
1467             xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
1468                                                                      root_screen->root_depth,
1469                                                                      walk->bar,
1470                                                                      xcb_root,
1471                                                                      walk->rect.x, walk->rect.y + walk->rect.h - bar_height,
1472                                                                      walk->rect.w, bar_height,
1473                                                                      0,
1474                                                                      XCB_WINDOW_CLASS_INPUT_OUTPUT,
1475                                                                      root_screen->root_visual,
1476                                                                      mask,
1477                                                                      values);
1478
1479             /* The double-buffer we use to render stuff off-screen */
1480             xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
1481                                                                     root_screen->root_depth,
1482                                                                     walk->buffer,
1483                                                                     walk->bar,
1484                                                                     walk->rect.w,
1485                                                                     bar_height);
1486
1487             /* Set the WM_CLASS and WM_NAME (we don't need UTF-8) atoms */
1488             xcb_void_cookie_t class_cookie;
1489             class_cookie = xcb_change_property(xcb_connection,
1490                                                XCB_PROP_MODE_REPLACE,
1491                                                walk->bar,
1492                                                XCB_ATOM_WM_CLASS,
1493                                                XCB_ATOM_STRING,
1494                                                8,
1495                                                (strlen("i3bar") + 1) * 2,
1496                                                "i3bar\0i3bar\0");
1497
1498             char *name;
1499             if (asprintf(&name, "i3bar for output %s", walk->name) == -1)
1500                 err(EXIT_FAILURE, "asprintf()");
1501             xcb_void_cookie_t name_cookie;
1502             name_cookie = xcb_change_property(xcb_connection,
1503                                               XCB_PROP_MODE_REPLACE,
1504                                               walk->bar,
1505                                               XCB_ATOM_WM_NAME,
1506                                               XCB_ATOM_STRING,
1507                                               8,
1508                                               strlen(name),
1509                                               name);
1510             free(name);
1511
1512             /* We want dock-windows (for now). When override_redirect is set, i3 is ignoring
1513              * this one */
1514             xcb_void_cookie_t dock_cookie = xcb_change_property(xcb_connection,
1515                                                                 XCB_PROP_MODE_REPLACE,
1516                                                                 walk->bar,
1517                                                                 atoms[_NET_WM_WINDOW_TYPE],
1518                                                                 XCB_ATOM_ATOM,
1519                                                                 32,
1520                                                                 1,
1521                                                                 (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
1522
1523             /* We need to tell i3, where to reserve space for i3bar */
1524             /* left, right, top, bottom, left_start_y, left_end_y,
1525              * right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
1526              * bottom_end_x */
1527             /* A local struct to save the strut_partial property */
1528             struct {
1529                 uint32_t left;
1530                 uint32_t right;
1531                 uint32_t top;
1532                 uint32_t bottom;
1533                 uint32_t left_start_y;
1534                 uint32_t left_end_y;
1535                 uint32_t right_start_y;
1536                 uint32_t right_end_y;
1537                 uint32_t top_start_x;
1538                 uint32_t top_end_x;
1539                 uint32_t bottom_start_x;
1540                 uint32_t bottom_end_x;
1541             } __attribute__((__packed__)) strut_partial;
1542             memset(&strut_partial, 0, sizeof(strut_partial));
1543
1544             switch (config.position) {
1545                 case POS_NONE:
1546                     break;
1547                 case POS_TOP:
1548                     strut_partial.top = bar_height;
1549                     strut_partial.top_start_x = walk->rect.x;
1550                     strut_partial.top_end_x = walk->rect.x + walk->rect.w;
1551                     break;
1552                 case POS_BOT:
1553                     strut_partial.bottom = bar_height;
1554                     strut_partial.bottom_start_x = walk->rect.x;
1555                     strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
1556                     break;
1557             }
1558             xcb_void_cookie_t strut_cookie = xcb_change_property(xcb_connection,
1559                                                                  XCB_PROP_MODE_REPLACE,
1560                                                                  walk->bar,
1561                                                                  atoms[_NET_WM_STRUT_PARTIAL],
1562                                                                  XCB_ATOM_CARDINAL,
1563                                                                  32,
1564                                                                  12,
1565                                                                  &strut_partial);
1566
1567             /* We also want a graphics-context for the bars (it defines the properties
1568              * with which we draw to them) */
1569             walk->bargc = xcb_generate_id(xcb_connection);
1570             xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
1571                                                                 walk->bargc,
1572                                                                 walk->bar,
1573                                                                 0,
1574                                                                 NULL);
1575
1576             /* We finally map the bar (display it on screen), unless the modifier-switch is on */
1577             xcb_void_cookie_t map_cookie;
1578             if (config.hide_on_modifier == M_DOCK) {
1579                 map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
1580             }
1581
1582             if (xcb_request_failed(win_cookie, "Could not create window") ||
1583                 xcb_request_failed(pm_cookie, "Could not create pixmap") ||
1584                 xcb_request_failed(dock_cookie, "Could not set dock mode") ||
1585                 xcb_request_failed(class_cookie, "Could not set WM_CLASS") ||
1586                 xcb_request_failed(name_cookie, "Could not set WM_NAME") ||
1587                 xcb_request_failed(strut_cookie, "Could not set strut") ||
1588                 xcb_request_failed(gc_cookie, "Could not create graphical context") ||
1589                 ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) {
1590                 exit(EXIT_FAILURE);
1591             }
1592
1593             const char *tray_output = (config.tray_output ? config.tray_output : SLIST_FIRST(outputs)->name);
1594             if (!tray_configured && strcasecmp(tray_output, "none") != 0) {
1595                 /* Configuration sanity check: ensure this i3bar instance handles the output on
1596                  * which the tray should appear (e.g. don’t initialize a tray if tray_output ==
1597                  * VGA-1 but output == [HDMI-1]).
1598                  */
1599                 i3_output *output;
1600                 SLIST_FOREACH(output, outputs, slist) {
1601                     if (strcasecmp(output->name, tray_output) == 0 ||
1602                         (strcasecmp(tray_output, "primary") == 0 && output->primary)) {
1603                         init_tray();
1604                         break;
1605                     }
1606                 }
1607                 tray_configured = true;
1608             }
1609         } else {
1610             /* We already have a bar, so we just reconfigure it */
1611             mask = XCB_CONFIG_WINDOW_X |
1612                    XCB_CONFIG_WINDOW_Y |
1613                    XCB_CONFIG_WINDOW_WIDTH |
1614                    XCB_CONFIG_WINDOW_HEIGHT |
1615                    XCB_CONFIG_WINDOW_STACK_MODE;
1616             values[0] = walk->rect.x;
1617             values[1] = walk->rect.y + walk->rect.h - bar_height;
1618             values[2] = walk->rect.w;
1619             values[3] = bar_height;
1620             values[4] = XCB_STACK_MODE_ABOVE;
1621
1622             DLOG("Destroying buffer for output %s\n", walk->name);
1623             xcb_free_pixmap(xcb_connection, walk->buffer);
1624
1625             DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
1626             xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
1627                                                                         walk->bar,
1628                                                                         mask,
1629                                                                         values);
1630
1631             mask = XCB_CW_OVERRIDE_REDIRECT;
1632             values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
1633             DLOG("Changing Window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
1634             xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection,
1635                                                                         walk->bar,
1636                                                                         mask,
1637                                                                         values);
1638
1639             DLOG("Recreating buffer for output %s\n", walk->name);
1640             xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
1641                                                                     root_screen->root_depth,
1642                                                                     walk->buffer,
1643                                                                     walk->bar,
1644                                                                     walk->rect.w,
1645                                                                     bar_height);
1646
1647             xcb_void_cookie_t map_cookie, umap_cookie;
1648             if (redraw_bars) {
1649                 /* Unmap the window, and draw it again when in dock mode */
1650                 umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar);
1651                 if (config.hide_on_modifier == M_DOCK) {
1652                     cont_child();
1653                     map_cookie = xcb_map_window_checked(xcb_connection, walk->bar);
1654                 } else {
1655                     stop_child();
1656                 }
1657
1658                 if (config.hide_on_modifier == M_HIDE) {
1659                     /* Switching to hide mode, register for keyevents */
1660                     register_xkb_keyevents();
1661                 } else {
1662                     /* Switching to dock/invisible mode, deregister from keyevents */
1663                     deregister_xkb_keyevents();
1664                 }
1665             }
1666
1667             if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
1668                 xcb_request_failed(chg_cookie, "Could not change window") ||
1669                 xcb_request_failed(pm_cookie, "Could not create pixmap") ||
1670                 (redraw_bars && (xcb_request_failed(umap_cookie, "Could not unmap window") ||
1671                                  (config.hide_on_modifier == M_DOCK && xcb_request_failed(map_cookie, "Could not map window"))))) {
1672                 exit(EXIT_FAILURE);
1673             }
1674         }
1675     }
1676 }
1677
1678 /*
1679  * Render the bars, with buttons and statusline
1680  *
1681  */
1682 void draw_bars(bool unhide) {
1683     DLOG("Drawing Bars...\n");
1684     int i = 0;
1685
1686     refresh_statusline();
1687
1688     i3_output *outputs_walk;
1689     SLIST_FOREACH(outputs_walk, outputs, slist) {
1690         if (!outputs_walk->active) {
1691             DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
1692             continue;
1693         }
1694         if (outputs_walk->bar == XCB_NONE) {
1695             /* Oh shit, an active output without an own bar. Create it now! */
1696             reconfig_windows(false);
1697         }
1698         /* First things first: clear the backbuffer */
1699         uint32_t color = colors.bar_bg;
1700         xcb_change_gc(xcb_connection,
1701                       outputs_walk->bargc,
1702                       XCB_GC_FOREGROUND,
1703                       &color);
1704         xcb_rectangle_t rect = {0, 0, outputs_walk->rect.w, bar_height};
1705         xcb_poly_fill_rectangle(xcb_connection,
1706                                 outputs_walk->buffer,
1707                                 outputs_walk->bargc,
1708                                 1,
1709                                 &rect);
1710
1711         if (!TAILQ_EMPTY(&statusline_head)) {
1712             DLOG("Printing statusline!\n");
1713
1714             /* Luckily we already prepared a seperate pixmap containing the rendered
1715              * statusline, we just have to copy the relevant parts to the relevant
1716              * position */
1717             trayclient *trayclient;
1718             int traypx = 0;
1719             TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) {
1720                 if (!trayclient->mapped)
1721                     continue;
1722                 /* We assume the tray icons are quadratic (we use the font
1723                  * *height* as *width* of the icons) because we configured them
1724                  * like this. */
1725                 traypx += font.height + logical_px(2);
1726             }
1727             /* Add 2px of padding if there are any tray icons */
1728             if (traypx > 0)
1729                 traypx += logical_px(2);
1730             xcb_copy_area(xcb_connection,
1731                           statusline_pm,
1732                           outputs_walk->buffer,
1733                           outputs_walk->bargc,
1734                           MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + logical_px(4))), 0,
1735                           MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0,
1736                           MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height);
1737         }
1738
1739         if (!config.disable_ws) {
1740             i3_ws *ws_walk;
1741             TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
1742                 DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
1743                      i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
1744                 uint32_t fg_color = colors.inactive_ws_fg;
1745                 uint32_t bg_color = colors.inactive_ws_bg;
1746                 uint32_t border_color = colors.inactive_ws_border;
1747                 if (ws_walk->visible) {
1748                     if (!ws_walk->focused) {
1749                         fg_color = colors.active_ws_fg;
1750                         bg_color = colors.active_ws_bg;
1751                         border_color = colors.active_ws_border;
1752                     } else {
1753                         fg_color = colors.focus_ws_fg;
1754                         bg_color = colors.focus_ws_bg;
1755                         border_color = colors.focus_ws_border;
1756                     }
1757                 }
1758                 if (ws_walk->urgent) {
1759                     DLOG("WS %s is urgent!\n", i3string_as_utf8(ws_walk->name));
1760                     fg_color = colors.urgent_ws_fg;
1761                     bg_color = colors.urgent_ws_bg;
1762                     border_color = colors.urgent_ws_border;
1763                     unhide = true;
1764                 }
1765                 uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
1766                 uint32_t vals_border[] = {border_color, border_color};
1767                 xcb_change_gc(xcb_connection,
1768                               outputs_walk->bargc,
1769                               mask,
1770                               vals_border);
1771                 xcb_rectangle_t rect_border = {i,
1772                                                logical_px(1),
1773                                                ws_walk->name_width + logical_px(10),
1774                                                font.height + logical_px(4)};
1775                 xcb_poly_fill_rectangle(xcb_connection,
1776                                         outputs_walk->buffer,
1777                                         outputs_walk->bargc,
1778                                         1,
1779                                         &rect_border);
1780                 uint32_t vals[] = {bg_color, bg_color};
1781                 xcb_change_gc(xcb_connection,
1782                               outputs_walk->bargc,
1783                               mask,
1784                               vals);
1785                 xcb_rectangle_t rect = {i + logical_px(1),
1786                                         2 * logical_px(1),
1787                                         ws_walk->name_width + logical_px(8),
1788                                         font.height + logical_px(2)};
1789                 xcb_poly_fill_rectangle(xcb_connection,
1790                                         outputs_walk->buffer,
1791                                         outputs_walk->bargc,
1792                                         1,
1793                                         &rect);
1794                 set_font_colors(outputs_walk->bargc, fg_color, bg_color);
1795                 draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc,
1796                           i + logical_px(5), 3 * logical_px(1), ws_walk->name_width);
1797                 i += logical_px(10) + ws_walk->name_width + logical_px(1);
1798             }
1799         }
1800
1801         if (binding.name && !config.disable_binding_mode_indicator) {
1802             uint32_t fg_color = colors.urgent_ws_fg;
1803             uint32_t bg_color = colors.urgent_ws_bg;
1804             uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
1805
1806             uint32_t vals_border[] = {colors.urgent_ws_border, colors.urgent_ws_border};
1807             xcb_change_gc(xcb_connection,
1808                           outputs_walk->bargc,
1809                           mask,
1810                           vals_border);
1811             xcb_rectangle_t rect_border = {i, 1, binding.width + 10, font.height + 4};
1812             xcb_poly_fill_rectangle(xcb_connection,
1813                                     outputs_walk->buffer,
1814                                     outputs_walk->bargc,
1815                                     1,
1816                                     &rect_border);
1817
1818             uint32_t vals[] = {bg_color, bg_color};
1819             xcb_change_gc(xcb_connection,
1820                           outputs_walk->bargc,
1821                           mask,
1822                           vals);
1823             xcb_rectangle_t rect = {i + 1, 2, binding.width + 8, font.height + 2};
1824             xcb_poly_fill_rectangle(xcb_connection,
1825                                     outputs_walk->buffer,
1826                                     outputs_walk->bargc,
1827                                     1,
1828                                     &rect);
1829
1830             set_font_colors(outputs_walk->bargc, fg_color, bg_color);
1831             draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, binding.width);
1832
1833             unhide = true;
1834         }
1835
1836         i = 0;
1837     }
1838
1839     /* Assure the bar is hidden/unhidden according to the specified hidden_state and mode */
1840     if (mod_pressed ||
1841         config.hidden_state == S_SHOW ||
1842         unhide) {
1843         unhide_bars();
1844     } else if (config.hide_on_modifier == M_HIDE) {
1845         hide_bars();
1846     }
1847
1848     redraw_bars();
1849 }
1850
1851 /*
1852  * Redraw the bars, i.e. simply copy the buffer to the barwindow
1853  *
1854  */
1855 void redraw_bars(void) {
1856     i3_output *outputs_walk;
1857     SLIST_FOREACH(outputs_walk, outputs, slist) {
1858         if (!outputs_walk->active) {
1859             continue;
1860         }
1861         xcb_copy_area(xcb_connection,
1862                       outputs_walk->buffer,
1863                       outputs_walk->bar,
1864                       outputs_walk->bargc,
1865                       0, 0,
1866                       0, 0,
1867                       outputs_walk->rect.w,
1868                       outputs_walk->rect.h);
1869         xcb_flush(xcb_connection);
1870     }
1871 }
1872
1873 /*
1874  * Set the current binding mode
1875  *
1876  */
1877 void set_current_mode(struct mode *current) {
1878     I3STRING_FREE(binding.name);
1879     binding = *current;
1880     activated_mode = binding.name != NULL;
1881     return;
1882 }