]> git.sur5r.net Git - i3/i3/blobdiff - src/x.c
Use 32-bit visuals for i3bar when possible and allow RGBA colors.
[i3/i3] / src / x.c
diff --git a/src/x.c b/src/x.c
index 02079a01f89e327ab5118029f722ea294432add1..d312666bda116dd6b956885215af9607fb3a897e 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * x.c: Interface to X11, transfers our in-memory state to X11 (see also
  *      render.c). Basically a big state machine.
  */
 #include "all.h"
 
+xcb_window_t ewmh_window;
+
 /* Stores the X11 window ID of the currently focused window */
 xcb_window_t focused_id = XCB_NONE;
 
-/* Because 'focused_id' might be reset to force input focus (after click to
- * raise), we separately keep track of the X11 window ID to be able to always
- * tell whether the focused window actually changed. */
+/* Because 'focused_id' might be reset to force input focus, we separately keep
+ * track of the X11 window ID to be able to always tell whether the focused
+ * window actually changed. */
 static xcb_window_t last_focused = XCB_NONE;
 
 /* Stores coordinates to warp mouse pointer to if set */
@@ -36,6 +38,7 @@ typedef struct con_state {
     bool mapped;
     bool unmap_now;
     bool child_mapped;
+    bool is_hidden;
 
     /** The con for which this state is. */
     Con *con;
@@ -142,11 +145,19 @@ void x_con_init(Con *con, uint16_t depth) {
 
     Rect dims = {-15, -15, 10, 10};
     con->frame = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
+    xcb_change_property(conn,
+                        XCB_PROP_MODE_REPLACE,
+                        con->frame,
+                        XCB_ATOM_WM_CLASS,
+                        XCB_ATOM_STRING,
+                        8,
+                        (strlen("i3-frame") + 1) * 2,
+                        "i3-frame\0i3-frame\0");
 
     if (win_colormap != XCB_NONE)
         xcb_free_colormap(conn, win_colormap);
 
-    struct con_state *state = scalloc(sizeof(struct con_state));
+    struct con_state *state = scalloc(1, sizeof(struct con_state));
     state->id = con->frame;
     state->mapped = false;
     state->initial = true;
@@ -285,7 +296,7 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
     /* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
      * In order to properly initialize these bytes, we allocate 32 bytes even
      * though we only need less for an xcb_configure_notify_event_t */
-    void *event = scalloc(32);
+    void *event = scalloc(32, 1);
     xcb_client_message_event_t *ev = event;
 
     ev->response_type = XCB_CLIENT_MESSAGE;
@@ -336,7 +347,7 @@ void x_draw_decoration(Con *con) {
         return;
 
     /* 1: build deco_params and compare with cache */
-    struct deco_render_params *p = scalloc(sizeof(struct deco_render_params));
+    struct deco_render_params *p = scalloc(1, sizeof(struct deco_render_params));
 
     /* find out which colors to use */
     if (con->urgent)
@@ -352,8 +363,8 @@ void x_draw_decoration(Con *con) {
 
     Rect *r = &(con->rect);
     Rect *w = &(con->window_rect);
-    p->con_rect = (struct width_height) {r->width, r->height};
-    p->con_window_rect = (struct width_height) {w->width, w->height};
+    p->con_rect = (struct width_height){r->width, r->height};
+    p->con_window_rect = (struct width_height){w->width, w->height};
     p->con_deco_rect = con->deco_rect;
     p->background = config.client.background;
     p->con_is_leaf = con_is_leaf(con);
@@ -363,6 +374,7 @@ void x_draw_decoration(Con *con) {
         (con->window == NULL || !con->window->name_x_changed) &&
         !parent->pixmap_recreated &&
         !con->pixmap_recreated &&
+        !con->mark_changed &&
         memcmp(p, con->deco_render_params, sizeof(struct deco_render_params)) == 0) {
         free(p);
         goto copy_pixmaps;
@@ -381,6 +393,7 @@ void x_draw_decoration(Con *con) {
 
     parent->pixmap_recreated = false;
     con->pixmap_recreated = false;
+    con->mark_changed = false;
 
     /* 2: draw the client.background, but only for the parts around the client_rect */
     if (con->window != NULL) {
@@ -403,7 +416,7 @@ void x_draw_decoration(Con *con) {
                 );
 #endif
 
-        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {config.client.background});
+        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){config.client.background});
         xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background);
     }
 
@@ -424,22 +437,22 @@ void x_draw_decoration(Con *con) {
          * (left, bottom and right part). We don’t just fill the whole
          * rectangle because some childs are not freely resizable and we want
          * their background color to "shine through". */
-        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
+        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
         if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
             xcb_rectangle_t leftline = {0, 0, br.x, r->height};
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
         }
         if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
-            xcb_rectangle_t rightline = {r->width + br.width + br.x, 0, r->width, r->height};
+            xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height};
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
         }
         if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
-            xcb_rectangle_t bottomline = {0, r->height + br.height + br.y, r->width, r->height};
+            xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)};
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
         }
         /* 1pixel border needs an additional line at the top */
         if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
-            xcb_rectangle_t topline = {br.x, 0, con->rect.width + br.width + br.x, br.y};
+            xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y};
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
         }
 
@@ -450,13 +463,13 @@ void x_draw_decoration(Con *con) {
         if (TAILQ_NEXT(con, nodes) == NULL &&
             TAILQ_PREV(con, nodes_head, nodes) == NULL &&
             con->parent->type != CT_FLOATING_CON) {
-            xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
+            xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->indicator});
             if (p->parent_layout == L_SPLITH)
-                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
-                                                                              {r->width + br.width + br.x, br.y, r->width, r->height + br.height}});
+                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
+                                                                              {r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}});
             else if (p->parent_layout == L_SPLITV)
-                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
-                                                                              {br.x, r->height + br.height + br.y, r->width - (2 * br.x), r->height}});
+                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
+                                                                              {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
         }
     }
 
@@ -466,20 +479,20 @@ void x_draw_decoration(Con *con) {
         goto copy_pixmaps;
 
     /* 4: paint the bar */
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
+    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
     xcb_rectangle_t drect = {con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height};
     xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect);
 
     /* 5: draw two unconnected horizontal lines in border color */
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
+    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
     Rect *dr = &(con->deco_rect);
-    int deco_diff_l = 2;
-    int deco_diff_r = 2;
-    if (parent->layout == L_TABBED) {
-        if (TAILQ_PREV(con, nodes_head, nodes) != NULL)
-            deco_diff_l = 0;
-        if (TAILQ_NEXT(con, nodes) != NULL)
-            deco_diff_r = 0;
+    adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
+    int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
+    int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width;
+    if (parent->layout == L_TABBED ||
+        (parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
+        deco_diff_l = 0;
+        deco_diff_r = 0;
     }
     xcb_segment_t segments[] = {
         {dr->x, dr->y,
@@ -531,10 +544,28 @@ void x_draw_decoration(Con *con) {
     //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult);
     int indent_px = (indent_level * 5) * indent_mult;
 
-    draw_text(win->name,
-              parent->pixmap, parent->pm_gc,
-              con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y,
-              con->deco_rect.width - 2 - indent_px);
+    int mark_width = 0;
+    if (config.show_marks && con->mark != NULL && (con->mark)[0] != '_') {
+        char *formatted_mark;
+        sasprintf(&formatted_mark, "[%s]", con->mark);
+        i3String *mark = i3string_from_utf8(formatted_mark);
+        FREE(formatted_mark);
+        mark_width = predict_text_width(mark);
+
+        draw_text(mark, parent->pixmap, parent->pm_gc, NULL,
+                  con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2),
+                  con->deco_rect.y + text_offset_y, mark_width);
+
+        I3STRING_FREE(mark);
+    }
+
+    i3String *title = win->title_format == NULL ? win->name : window_parse_title_format(win);
+    draw_text(title,
+              parent->pixmap, parent->pm_gc, NULL,
+              con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y,
+              con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2));
+    if (win->title_format != NULL)
+        I3STRING_FREE(title);
 
 after_title:
     /* Since we don’t clip the text at all, it might in some cases be painted
@@ -545,12 +576,12 @@ after_title:
     /* Draw a 1px separator line before and after every tab, so that tabs can
      * be easily distinguished. */
     if (parent->layout == L_TABBED) {
-        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
+        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
     } else {
-        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
+        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
     }
     xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, parent->pixmap, parent->pm_gc, 6,
-                  (xcb_point_t[]) {
+                  (xcb_point_t[]){
                       {dr->x + dr->width, dr->y},
                       {dr->x + dr->width, dr->y + dr->height},
                       {dr->x + dr->width - 1, dr->y},
@@ -559,7 +590,7 @@ after_title:
                       {dr->x, dr->y},
                   });
 
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
+    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
     xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments);
 
 copy_pixmaps:
@@ -594,6 +625,31 @@ void x_deco_recurse(Con *con) {
         x_draw_decoration(con);
 }
 
+/*
+ * Sets or removes the _NET_WM_STATE_HIDDEN property on con if necessary.
+ *
+ */
+static void set_hidden_state(Con *con) {
+    if (con->window == NULL) {
+        return;
+    }
+
+    con_state *state = state_for_frame(con->frame);
+    bool should_be_hidden = con_is_hidden(con);
+    if (should_be_hidden == state->is_hidden)
+        return;
+
+    if (should_be_hidden) {
+        DLOG("setting _NET_WM_STATE_HIDDEN for con = %p\n", con);
+        xcb_add_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_HIDDEN);
+    } else {
+        DLOG("removing _NET_WM_STATE_HIDDEN for con = %p\n", con);
+        xcb_remove_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_HIDDEN);
+    }
+
+    state->is_hidden = should_be_hidden;
+}
+
 /*
  * This function pushes the properties of each node of the layout tree to
  * X11 if they have changed (like the map state, position of the window, …).
@@ -659,10 +715,18 @@ void x_push_node(Con *con) {
              con, con->window->id, con->ignore_unmap);
     }
 
+    /* The pixmap of a borderless leaf container will not be used except
+     * for the titlebar in a stack or tabs (issue #1013). */
+    bool is_pixmap_needed = (con->border_style != BS_NONE ||
+                             !con_is_leaf(con) ||
+                             con->parent->layout == L_STACKED ||
+                             con->parent->layout == L_TABBED);
+
     bool fake_notify = false;
-    /* Set new position if rect changed (and if height > 0) */
-    if (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 &&
-        rect.height > 0) {
+    /* Set new position if rect changed (and if height > 0) or if the pixmap
+     * needs to be recreated */
+    if ((is_pixmap_needed && con->pixmap == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 &&
+                                                          rect.height > 0)) {
         /* We first create the new pixmap, then render to it, set it as the
          * background and only afterwards change the window size. This reduces
          * flickering. */
@@ -673,13 +737,6 @@ void x_push_node(Con *con) {
          * (height == 0) or when it is not needed. */
         bool has_rect_changed = (state->rect.width != rect.width || state->rect.height != rect.height);
 
-        /* The pixmap of a borderless leaf container will not be used except
-         * for the titlebar in a stack or tabs (issue #1013). */
-        bool is_pixmap_needed = (con->border_style != BS_NONE ||
-                                 !con_is_leaf(con) ||
-                                 con->parent->layout == L_STACKED ||
-                                 con->parent->layout == L_TABBED);
-
         /* Check if the container has an unneeded pixmap left over from
          * previously having a border or titlebar. */
         if (!is_pixmap_needed && con->pixmap != XCB_NONE) {
@@ -687,7 +744,7 @@ void x_push_node(Con *con) {
             con->pixmap = XCB_NONE;
         }
 
-        if (has_rect_changed && is_pixmap_needed) {
+        if (is_pixmap_needed && (has_rect_changed || con->pixmap == XCB_NONE)) {
             if (con->pixmap == 0) {
                 con->pixmap = xcb_generate_id(conn);
                 con->pm_gc = xcb_generate_id(conn);
@@ -796,6 +853,8 @@ void x_push_node(Con *con) {
         fake_absolute_configure_notify(con);
     }
 
+    set_hidden_state(con);
+
     /* Handle all children and floating windows of this node. We recurse
      * in focus order to display the focused client in a stack first when
      * switching workspaces (reduces flickering). */
@@ -974,9 +1033,9 @@ void x_push_changes(Con *con) {
             Output *target = get_output_containing(mid_x, mid_y);
             if (current != target) {
                 /* Ignore MotionNotify events generated by warping */
-                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
+                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
                 xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, mid_x, mid_y);
-                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {ROOT_EVENT_MASK});
+                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
             }
         }
         warp_to = NULL;
@@ -1022,7 +1081,7 @@ void x_push_changes(Con *con) {
                     values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE);
                     xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
                 }
-                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp);
+                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
                 if (focused->window != NULL) {
                     values[0] = CHILD_EVENT_MASK;
                     xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
@@ -1039,10 +1098,12 @@ void x_push_changes(Con *con) {
     }
 
     if (focused_id == XCB_NONE) {
-        DLOG("Still no window focused, better set focus to the root window\n");
-        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, last_timestamp);
+        /* If we still have no window to focus, we focus the EWMH window instead. We use this rather than the
+         * root window in order to avoid an X11 fallback mechanism causing a ghosting effect (see #1378). */
+        DLOG("Still no window focused, better set focus to the EWMH support window (%d)\n", ewmh_window);
+        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, XCB_CURRENT_TIME);
         ewmh_update_active_window(XCB_WINDOW_NONE);
-        focused_id = root;
+        focused_id = ewmh_window;
     }
 
     xcb_flush(conn);
@@ -1140,8 +1201,7 @@ void x_set_i3_atoms(void) {
  *
  */
 void x_set_warp_to(Rect *rect) {
-    if (!config.disable_focus_follows_mouse &&
-        config.mouse_warping != POINTER_WARPING_NONE)
+    if (config.mouse_warping != POINTER_WARPING_NONE)
         warp_to = rect;
 }