X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fx.c;h=d312666bda116dd6b956885215af9607fb3a897e;hb=1c4100ce5d8f9a7edc46f80f8a20ca50c6d97f8b;hp=02079a01f89e327ab5118029f722ea294432add1;hpb=4c06e7a573e450329212b28a3b8f4a5e89b326d4;p=i3%2Fi3 diff --git a/src/x.c b/src/x.c index 02079a01..d312666b 100644 --- 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. @@ -12,12 +12,14 @@ */ #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; }