]> git.sur5r.net Git - i3/i3/blobdiff - i3bar/src/xcb.c
Fix i3bar when using multiple displays (Thanks brimstone)
[i3/i3] / i3bar / src / xcb.c
index 471209c7c98d3d3a75890eb594a5d38d063e0c14..afcfaa397420791d68f21928a3f87646ccd4b0ea 100644 (file)
@@ -2,17 +2,15 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
+ * © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
  *
- * © 2010-2011 Axel Wagner and contributors
- *
- * See file LICNSE for license information
- *
- * src/xcb.c: Communicating with X
+ * xcb.c: Communicating with X
  *
  */
 #include <xcb/xcb.h>
 #include <xcb/xproto.h>
 #include <xcb/xcb_atom.h>
+#include <xcb/xcb_aux.h>
 
 #ifdef XCB_COMPAT
 #include "xcb_compat.h"
 #include "common.h"
 #include "libi3.h"
 
-#if defined(__APPLE__)
-
-/*
- * Taken from FreeBSD
- * Returns a pointer to a new string which is a duplicate of the
- * string, but only copies at most n characters.
- *
- */
-char *strndup(const char *str, size_t n) {
-    size_t len;
-    char *copy;
-
-    for (len = 0; len < n && str[len]; len++)
-        continue;
-
-    if ((copy = malloc(len + 1)) == NULL)
-        return (NULL);
-    memcpy(copy, str, len);
-    copy[len] = '\0';
-    return (copy);
-}
-
-#endif
-
 /* We save the Atoms in an easy to access array, indexed by an enum */
 enum {
     #define ATOM_DO(name) name,
@@ -195,21 +169,6 @@ void draw_text(xcb_drawable_t drawable, xcb_gcontext_t ctx, int16_t x, int16_t y
     }
 }
 
-/*
- * Converts a colorstring to a colorpixel as expected from xcb_change_gc.
- * s is assumed to be in the format "rrggbb"
- *
- */
-uint32_t get_colorpixel(const char *s) {
-    char strings[3][3] = { { s[0], s[1], '\0'} ,
-                           { s[2], s[3], '\0'} ,
-                           { s[4], s[5], '\0'} };
-    uint8_t r = strtol(strings[0], NULL, 16);
-    uint8_t g = strtol(strings[1], NULL, 16);
-    uint8_t b = strtol(strings[2], NULL, 16);
-    return (r << 16 | g << 8 | b);
-}
-
 /*
  * Redraws the statusline to the buffer
  *
@@ -282,7 +241,9 @@ void unhide_bars() {
                XCB_CONFIG_WINDOW_HEIGHT |
                XCB_CONFIG_WINDOW_STACK_MODE;
         values[0] = walk->rect.x;
-        values[1] = walk->rect.y + walk->rect.h - font_height - 6;
+        if (config.position == POS_TOP)
+            values[1] = walk->rect.y;
+        else values[1] = walk->rect.y + walk->rect.h - font_height - 6;
         values[2] = walk->rect.w;
         values[3] = font_height + 6;
         values[4] = XCB_STACK_MODE_ABOVE;
@@ -308,16 +269,16 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
     do { \
         colors.name = get_colorpixel(new_colors->name ? new_colors->name : def); \
     } while  (0)
-    PARSE_COLOR(bar_fg, "FFFFFF");
-    PARSE_COLOR(bar_bg, "000000");
-    PARSE_COLOR(active_ws_fg, "FFFFFF");
-    PARSE_COLOR(active_ws_bg, "480000");
-    PARSE_COLOR(inactive_ws_fg, "FFFFFF");
-    PARSE_COLOR(inactive_ws_bg, "240000");
-    PARSE_COLOR(urgent_ws_fg, "FFFFFF");
-    PARSE_COLOR(urgent_ws_bg, "002400");
-    PARSE_COLOR(focus_ws_fg, "FFFFFF");
-    PARSE_COLOR(focus_ws_bg, "480000");
+    PARSE_COLOR(bar_fg, "#FFFFFF");
+    PARSE_COLOR(bar_bg, "#000000");
+    PARSE_COLOR(active_ws_fg, "#FFFFFF");
+    PARSE_COLOR(active_ws_bg, "#333333");
+    PARSE_COLOR(inactive_ws_fg, "#888888");
+    PARSE_COLOR(inactive_ws_bg, "#222222");
+    PARSE_COLOR(urgent_ws_fg, "#FFFFFF");
+    PARSE_COLOR(urgent_ws_bg, "#900000");
+    PARSE_COLOR(focus_ws_fg, "#FFFFFF");
+    PARSE_COLOR(focus_ws_bg, "#285577");
 #undef PARSE_COLOR
 }
 
@@ -413,6 +374,8 @@ static void configure_trayclients() {
 
         int clients = 0;
         TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
+            if (!trayclient->mapped)
+                continue;
             clients++;
 
             DLOG("Configuring tray window %08x to x=%d\n",
@@ -490,6 +453,9 @@ static void handle_client_message(xcb_client_message_event_t* event) {
             SLIST_FOREACH(walk, outputs, slist) {
                 if (!walk->active)
                     continue;
+                if (config.tray_output &&
+                    strcasecmp(walk->name, config.tray_output) != 0)
+                    continue;
                 DLOG("using output %s\n", walk->name);
                 output = walk;
             }
@@ -516,7 +482,7 @@ static void handle_client_message(xcb_client_message_event_t* event) {
                                  values);
 
             /* send the XEMBED_EMBEDDED_NOTIFY message */
-            void *event = calloc(32, 1);
+            void *event = scalloc(32);
             xcb_client_message_event_t *ev = event;
             ev->response_type = XCB_CLIENT_MESSAGE;
             ev->window = client;
@@ -533,13 +499,20 @@ static void handle_client_message(xcb_client_message_event_t* event) {
                            (char*)ev);
             free(event);
 
+            /* Put the client inside the save set. Upon termination (whether
+             * killed or normal exit does not matter) of i3bar, these clients
+             * will be correctly reparented to their most closest living
+             * ancestor. Without this, tray icons might die when i3bar
+             * exits/crashes. */
+            xcb_change_save_set(xcb_connection, XCB_SET_MODE_INSERT, client);
+
             if (map_it) {
                 DLOG("Mapping dock client\n");
                 xcb_map_window(xcb_connection, client);
             } else {
                 DLOG("Not mapping dock client yet\n");
             }
-            trayclient *tc = malloc(sizeof(trayclient));
+            trayclient *tc = smalloc(sizeof(trayclient));
             tc->win = client;
             tc->mapped = map_it;
             tc->xe_version = xe_version;
@@ -625,8 +598,10 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
         xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection,
                                                                    xembedc,
                                                                    NULL);
-        if (xembedr == NULL || xembedr->length == 0)
+        if (xembedr == NULL || xembedr->length == 0) {
+            DLOG("xembed_info unset\n");
             return;
+        }
 
         DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length);
         uint32_t *xembed = xcb_get_property_value(xembedr);
@@ -638,11 +613,13 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
             /* need to unmap the window */
             xcb_unmap_window(xcb_connection, trayclient->win);
             trayclient->mapped = map_it;
+            configure_trayclients();
             draw_bars();
         } else if (!trayclient->mapped && map_it) {
             /* need to map the window */
             xcb_map_window(xcb_connection, trayclient->win);
             trayclient->mapped = map_it;
+            configure_trayclients();
             draw_bars();
         }
         free(xembedr);
@@ -665,6 +642,8 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
 
         int clients = 0;
         TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
+            if (!trayclient->mapped)
+                continue;
             clients++;
 
             if (trayclient->win != event->window)
@@ -708,38 +687,37 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
         exit(1);
     }
 
-    while ((event = xcb_poll_for_event(xcb_connection)) == NULL) {
-        return;
-    }
-
-    switch (event->response_type & ~0x80) {
-        case XCB_EXPOSE:
-            /* Expose-events happen, when the window needs to be redrawn */
-            redraw_bars();
-            break;
-        case XCB_BUTTON_PRESS:
-            /* Button-press-events are mouse-buttons clicked on one of our bars */
-            handle_button((xcb_button_press_event_t*) event);
-            break;
-        case XCB_CLIENT_MESSAGE:
-            /* Client messages are used for client-to-client communication, for
-             * example system tray widgets talk to us directly via client messages. */
-            handle_client_message((xcb_client_message_event_t*) event);
-            break;
-        case XCB_UNMAP_NOTIFY:
-            /* UnmapNotifies are received when a tray window unmaps itself */
-            handle_unmap_notify((xcb_unmap_notify_event_t*) event);
-            break;
-        case XCB_PROPERTY_NOTIFY:
-            /* PropertyNotify */
-            handle_property_notify((xcb_property_notify_event_t*) event);
-            break;
-        case XCB_CONFIGURE_REQUEST:
-            /* ConfigureRequest, sent by a tray child */
-            handle_configure_request((xcb_configure_request_event_t*) event);
-            break;
+    while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
+        switch (event->response_type & ~0x80) {
+            case XCB_EXPOSE:
+                /* Expose-events happen, when the window needs to be redrawn */
+                redraw_bars();
+                break;
+            case XCB_BUTTON_PRESS:
+                /* Button-press-events are mouse-buttons clicked on one of our bars */
+                handle_button((xcb_button_press_event_t*) event);
+                break;
+            case XCB_CLIENT_MESSAGE:
+                /* Client messages are used for client-to-client communication, for
+                 * example system tray widgets talk to us directly via client messages. */
+                handle_client_message((xcb_client_message_event_t*) event);
+                break;
+            case XCB_UNMAP_NOTIFY:
+            case XCB_DESTROY_NOTIFY:
+                /* UnmapNotifies are received when a tray window unmaps itself */
+                handle_unmap_notify((xcb_unmap_notify_event_t*) event);
+                break;
+            case XCB_PROPERTY_NOTIFY:
+                /* PropertyNotify */
+                handle_property_notify((xcb_property_notify_event_t*) event);
+                break;
+            case XCB_CONFIGURE_REQUEST:
+                /* ConfigureRequest, sent by a tray child */
+                handle_configure_request((xcb_configure_request_event_t*) event);
+                break;
+        }
+        free(event);
     }
-    FREE(event);
 }
 
 /*
@@ -807,7 +785,7 @@ char *init_xcb_early() {
     #define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
     #include "xcb_atoms.def"
 
-    xcb_screen = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)).data;
+    xcb_screen = xcb_aux_get_screen(xcb_connection, screen);
     xcb_root = xcb_screen->root;
 
     /* We draw the statusline to a seperate pixmap, because it looks the same on all bars and
@@ -841,9 +819,9 @@ char *init_xcb_early() {
 
 
     /* The various Watchers to communicate with xcb */
-    xcb_io = malloc(sizeof(ev_io));
-    xcb_prep = malloc(sizeof(ev_prepare));
-    xcb_chk = malloc(sizeof(ev_check));
+    xcb_io = smalloc(sizeof(ev_io));
+    xcb_prep = smalloc(sizeof(ev_prepare));
+    xcb_chk = smalloc(sizeof(ev_check));
 
     ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
     ev_prepare_init(xcb_prep, &xcb_prep_cb);
@@ -893,10 +871,8 @@ char *init_xcb_early() {
  */
 void init_xcb_late(char *fontname) {
     if (fontname == NULL) {
-        /* This is a very restrictive default. More sensefull would be something like
-         * "-misc-*-*-*-*--*-*-*-*-*-*-*-*". But since that produces very ugly results
-         * on my machine, let's stick with this until we have a configfile */
-        fontname = "-misc-fixed-medium-r-semicondensed--12-110-75-75-c-60-iso10646-1";
+        /* XXX: font fallback to 'misc' like i3 does it would be good. */
+        fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
     }
 
     /* We load and allocate the font */
@@ -956,7 +932,7 @@ void init_xcb_late(char *fontname) {
             exit(EXIT_FAILURE);
         }
 
-        xkb_io = malloc(sizeof(ev_io));
+        xkb_io = smalloc(sizeof(ev_io));
         ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
         ev_io_start(main_loop, xkb_io);
         XFlush(xkb_dpy);
@@ -989,6 +965,7 @@ void init_xcb_late(char *fontname) {
  *
  */
 void init_tray() {
+    DLOG("Initializing system tray functionality\n");
     /* request the tray manager atom for the X11 display we are running on */
     char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
     snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
@@ -1053,7 +1030,7 @@ void init_tray() {
     }
 
     /* Inform clients waiting for a new _NET_SYSTEM_TRAY that we are here */
-    void *event = calloc(32, 1);
+    void *event = scalloc(32);
     xcb_client_message_event_t *ev = event;
     ev->response_type = XCB_CLIENT_MESSAGE;
     ev->window = xcb_root;
@@ -1195,6 +1172,7 @@ void realloc_sl_buffer() {
 void reconfig_windows() {
     uint32_t mask;
     uint32_t values[5];
+    static bool tray_configured = false;
 
     i3_output *walk;
     SLIST_FOREACH(walk, outputs, slist) {
@@ -1208,9 +1186,6 @@ void reconfig_windows() {
         if (walk->bar == XCB_NONE) {
             DLOG("Creating Window for output %s\n", walk->name);
 
-            /* TODO: only call init_tray() if the tray is configured for this output */
-            init_tray();
-
             walk->bar = xcb_generate_id(xcb_connection);
             walk->buffer = xcb_generate_id(xcb_connection);
             mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
@@ -1304,15 +1279,15 @@ void reconfig_windows() {
                 uint32_t bottom_start_x;
                 uint32_t bottom_end_x;
             } __attribute__((__packed__)) strut_partial = {0,};
-            switch (config.dockpos) {
-                case DOCKPOS_NONE:
+            switch (config.position) {
+                case POS_NONE:
                     break;
-                case DOCKPOS_TOP:
+                case POS_TOP:
                     strut_partial.top = font_height + 6;
                     strut_partial.top_start_x = walk->rect.x;
                     strut_partial.top_end_x = walk->rect.x + walk->rect.w;
                     break;
-                case DOCKPOS_BOT:
+                case POS_BOT:
                     strut_partial.bottom = font_height + 6;
                     strut_partial.bottom_start_x = walk->rect.x;
                     strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
@@ -1354,6 +1329,13 @@ void reconfig_windows() {
                 (!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) {
                 exit(EXIT_FAILURE);
             }
+
+            if (!tray_configured &&
+                (!config.tray_output ||
+                 strcasecmp("none", config.tray_output) != 0)) {
+                init_tray();
+                tray_configured = true;
+            }
         } else {
             /* We already have a bar, so we just reconfigure it */
             mask = XCB_CONFIG_WINDOW_X |