draw leaf window decorations on ->frame instead of ->parent->frame

related to https://github.com/i3/i3/issues/3724
fixes https://github.com/i3/i3/issues/1966
This commit is contained in:
Michael Stapelberg 2022-11-03 23:11:32 +01:00 committed by Michael Stapelberg
parent 6e6af01b7a
commit d26ddcbfe5
14 changed files with 186 additions and 131 deletions

View File

@ -564,9 +564,7 @@ split container.
==== Default layout ==== Default layout
In default layout, containers are placed horizontally or vertically next to In default layout, containers are placed horizontally or vertically next to
each other (depending on the +con->orientation+). If a child is a leaf node (as each other (depending on the +con->orientation+).
opposed to a split container) and has border style "normal", appropriate space
will be reserved for its window decoration.
==== Stacked layout ==== Stacked layout

View File

@ -410,6 +410,14 @@ deco_rect (map)::
The coordinates of the *window decoration* inside its container. These The coordinates of the *window decoration* inside its container. These
coordinates are relative to the container and do not include the actual coordinates are relative to the container and do not include the actual
client window. client window.
actual_deco_rect (map)::
See +deco_rect+. i3 v4.22 changed the way title bars are rendered. Before
i3 v4.22, the deco_rect was always relative to the parent coordinates.
Starting with i3 v4.22, this remains true for tabbed/stacked containers
(actual_deco_rect is identical to deco_rect), but for normal-border leaf
containers within vertical/horizontal split containers, actual_deco_rect
is relative to the container itself. For more background, see
https://github.com/i3/i3/issues/1966
geometry (map):: geometry (map)::
The original geometry the window specified when i3 mapped it. Used when The original geometry the window specified when i3 mapped it. Used when
switching a window to floating mode, for example. switching a window to floating mode, for example.

View File

@ -1501,23 +1501,6 @@ Tip: You can find an
https://github.com/Airblader/i3/wiki/Example-Configuration[example https://github.com/Airblader/i3/wiki/Example-Configuration[example
configuration] that uses modes to illustrate various gap configurations. configuration] that uses modes to illustrate various gap configurations.
[[gaps_artifacts]]
==== ⚠ Known issue with gaps: graphical artifacts (black rectangles)
The way i3 renders window title bars results in graphical artifacts (black
rectangles behind windows) when enabling gaps. In some circumstances, running a
compositor such as `picom` works around the artifacts.
Another workaround is to disable window title bars entirely:
------------------------
# You can also use any non-zero value if you'd like to have a border
default_border pixel 0
------------------------
See https://github.com/i3/i3/issues/3724[Issue #3724] for more details and
updates on this issue.
== Configuring i3bar == Configuring i3bar
The bar at the bottom of your monitor is drawn by a separate process called The bar at the bottom of your monitor is drawn by a separate process called

View File

@ -420,6 +420,14 @@ Con *con_descend_tiling_focused(Con *con);
*/ */
Con *con_descend_direction(Con *con, direction_t direction); Con *con_descend_direction(Con *con, direction_t direction);
/**
* Returns whether the window decoration (title bar) should be drawn into the
* X11 frame window of this container (default) or into the X11 frame window of
* the parent container (for stacked/tabbed containers).
*
*/
bool con_draw_decoration_into_frame(Con *con);
/** /**
* Returns a "relative" Rect which contains the amount of pixels that need to * Returns a "relative" Rect which contains the amount of pixels that need to
* be added to the original Rect to get the final position (obviously the * be added to the original Rect to get the final position (obviously the

View File

@ -404,13 +404,20 @@ void handle_button_press(xcb_button_press_event_t *event) {
} }
/* Check if the click was on the decoration of a child */ /* Check if the click was on the decoration of a child */
Con *child; if (con->window != NULL) {
TAILQ_FOREACH_REVERSE (child, &(con->nodes_head), nodes_head, nodes) { if (rect_contains(con->deco_rect, event->event_x, event->event_y)) {
if (!rect_contains(child->deco_rect, event->event_x, event->event_y)) route_click(con, event, mod_pressed, CLICK_DECORATION);
continue; return;
}
} else {
Con *child;
TAILQ_FOREACH_REVERSE (child, &(con->nodes_head), nodes_head, nodes) {
if (!rect_contains(child->deco_rect, event->event_x, event->event_y))
continue;
route_click(child, event, mod_pressed, CLICK_DECORATION); route_click(child, event, mod_pressed, CLICK_DECORATION);
return; return;
}
} }
if (event->child != XCB_NONE) { if (event->child != XCB_NONE) {

View File

@ -1692,6 +1692,20 @@ static bool has_outer_gaps(gaps_t gaps) {
gaps.left > 0; gaps.left > 0;
} }
/*
* Returns whether the window decoration (title bar) should be drawn into the
* X11 frame window of this container (default) or into the X11 frame window of
* the parent container (for stacked/tabbed containers).
*
*/
bool con_draw_decoration_into_frame(Con *con) {
return con_is_leaf(con) &&
con->border_style == BS_NORMAL &&
(con->parent == NULL ||
(con->parent->layout != L_TABBED &&
con->parent->layout != L_STACKED));
}
/* /*
* Returns a "relative" Rect which contains the amount of pixels that need to * Returns a "relative" Rect which contains the amount of pixels that need to
* be added to the original Rect to get the final position (obviously the * be added to the original Rect to get the final position (obviously the
@ -1724,7 +1738,16 @@ Rect con_border_style_rect(Con *con) {
if (border_style == BS_NONE) if (border_style == BS_NONE)
return (Rect){0, 0, 0, 0}; return (Rect){0, 0, 0, 0};
if (border_style == BS_NORMAL) { if (border_style == BS_NORMAL) {
const int deco_height = render_deco_height();
result = (Rect){border_width, 0, -(2 * border_width), -(border_width)}; result = (Rect){border_width, 0, -(2 * border_width), -(border_width)};
if (con_draw_decoration_into_frame(con)) {
result = (Rect){
.x = border_width /* left */,
.y = deco_height,
.width = -(border_width /* left */ + border_width /* right */),
.height = -(border_width /* bottom */ + deco_height),
};
}
} else { } else {
result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)}; result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
} }
@ -1828,23 +1851,17 @@ void con_set_border_style(Con *con, border_style_t border_style, int border_widt
* pixels. For the parent, we do the same also for the decoration. */ * pixels. For the parent, we do the same also for the decoration. */
Con *parent = con->parent; Con *parent = con->parent;
Rect bsr = con_border_style_rect(con); Rect bsr = con_border_style_rect(con);
int deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
con->rect = rect_add(con->rect, bsr); con->rect = rect_add(con->rect, bsr);
parent->rect = rect_add(parent->rect, bsr); parent->rect = rect_add(parent->rect, bsr);
parent->rect.y += deco_height;
parent->rect.height -= deco_height;
/* Change the border style, get new border/decoration values. */ /* Change the border style, get new border/decoration values. */
con->border_style = border_style; con->border_style = border_style;
con->current_border_width = border_width; con->current_border_width = border_width;
bsr = con_border_style_rect(con); bsr = con_border_style_rect(con);
deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
con->rect = rect_sub(con->rect, bsr); con->rect = rect_sub(con->rect, bsr);
parent->rect = rect_sub(parent->rect, bsr); parent->rect = rect_sub(parent->rect, bsr);
parent->rect.y -= deco_height;
parent->rect.height += deco_height;
} }
/* /*

View File

@ -79,16 +79,22 @@ void floating_check_size(Con *floating_con, bool prefer_height) {
Rect floating_sane_max_dimensions; Rect floating_sane_max_dimensions;
Con *focused_con = con_descend_focused(floating_con); Con *focused_con = con_descend_focused(floating_con);
DLOG("deco_rect.height = %d\n", focused_con->deco_rect.height);
Rect border_rect = con_border_style_rect(focused_con); Rect border_rect = con_border_style_rect(focused_con);
/* We have to do the opposite calculations that render_con() do /* We have to do the opposite calculations that render_con() do
* to get the exact size we want. */ * to get the exact size we want. */
border_rect.width = -border_rect.width; border_rect.width = -border_rect.width;
border_rect.width += 2 * focused_con->border_width;
border_rect.height = -border_rect.height; border_rect.height = -border_rect.height;
/* undo x11 border */
border_rect.width += 2 * focused_con->border_width;
border_rect.height += 2 * focused_con->border_width; border_rect.height += 2 * focused_con->border_width;
if (con_border_style(focused_con) == BS_NORMAL) {
border_rect.height += render_deco_height(); DLOG("floating_check_size, want min width %d, min height %d, border extra: w=%d, h=%d\n",
} floating_sane_min_width,
floating_sane_min_height,
border_rect.width,
border_rect.height);
i3Window *window = focused_con->window; i3Window *window = focused_con->window;
if (window != NULL) { if (window != NULL) {
@ -319,9 +325,6 @@ bool floating_enable(Con *con, bool automatic) {
x_set_name(nc, name); x_set_name(nc, name);
free(name); free(name);
/* find the height for the decorations */
int deco_height = render_deco_height();
DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height); DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height);
DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height); DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height);
nc->rect = con->geometry; nc->rect = con->geometry;
@ -352,15 +355,10 @@ bool floating_enable(Con *con, bool automatic) {
} }
/* Add pixels for the decoration. */ /* Add pixels for the decoration. */
Rect border_style_rect = con_border_style_rect(con); Rect bsr = con_border_style_rect(con);
nc->rect.height -= border_style_rect.height; nc->rect.height -= bsr.height;
nc->rect.width -= border_style_rect.width; nc->rect.width -= bsr.width;
/* Add some more pixels for the title bar */
if (con_border_style(con) == BS_NORMAL) {
nc->rect.height += deco_height;
}
/* Honor the X11 border */ /* Honor the X11 border */
nc->rect.height += con->border_width * 2; nc->rect.height += con->border_width * 2;
@ -406,14 +404,6 @@ bool floating_enable(Con *con, bool automatic) {
DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height); DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height);
/* 5: Subtract the deco_height in order to make the floating window appear
* at precisely the position it specified in its original geometry (which
* is what applications might remember). */
deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
nc->rect.y -= deco_height;
DLOG("Corrected y = %d (deco_height = %d)\n", nc->rect.y, deco_height);
/* render the cons to get initial window_rect correct */ /* render the cons to get initial window_rect correct */
render_con(nc); render_con(nc);

View File

@ -214,18 +214,30 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
return; return;
/* see over which rect the user is */ /* see over which rect the user is */
Con *current; if (con->window != NULL) {
TAILQ_FOREACH_REVERSE (current, &(con->nodes_head), nodes_head, nodes) { if (rect_contains(con->deco_rect, event->event_x, event->event_y)) {
if (!rect_contains(current->deco_rect, event->event_x, event->event_y)) /* We found the rect, lets see if this window is focused */
continue; if (TAILQ_FIRST(&(con->parent->focus_head)) == con)
return;
/* We found the rect, lets see if this window is focused */ con_focus(con);
if (TAILQ_FIRST(&(con->focus_head)) == current) x_push_changes(croot);
return; return;
}
} else {
Con *current;
TAILQ_FOREACH_REVERSE (current, &(con->nodes_head), nodes_head, nodes) {
if (!rect_contains(current->deco_rect, event->event_x, event->event_y))
continue;
con_focus(current); /* We found the rect, lets see if this window is focused */
x_push_changes(croot); if (TAILQ_FIRST(&(con->focus_head)) == current)
return; return;
con_focus(current);
x_push_changes(croot);
return;
}
} }
} }
@ -318,15 +330,9 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
Con *fullscreen = con_get_fullscreen_covering_ws(workspace); Con *fullscreen = con_get_fullscreen_covering_ws(workspace);
if (fullscreen != con && con_is_floating(con) && con_is_leaf(con)) { if (fullscreen != con && con_is_floating(con) && con_is_leaf(con)) {
/* find the height for the decorations */
int deco_height = con->deco_rect.height;
/* we actually need to apply the size/position changes to the *parent* /* we actually need to apply the size/position changes to the *parent*
* container */ * container */
Rect bsr = con_border_style_rect(con); Rect bsr = con_border_style_rect(con);
if (con->border_style == BS_NORMAL) {
bsr.y += deco_height;
bsr.height -= deco_height;
}
Con *floatingcon = con->parent; Con *floatingcon = con->parent;
Rect newrect = floatingcon->rect; Rect newrect = floatingcon->rect;

View File

@ -503,7 +503,15 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
y(integer, con->current_border_width); y(integer, con->current_border_width);
dump_rect(gen, "rect", con->rect); dump_rect(gen, "rect", con->rect);
dump_rect(gen, "deco_rect", con->deco_rect); if (con_draw_decoration_into_frame(con)) {
Rect simulated_deco_rect = con->deco_rect;
simulated_deco_rect.x = con->rect.x - con->parent->rect.x;
simulated_deco_rect.y = con->rect.y - con->parent->rect.y;
dump_rect(gen, "deco_rect", simulated_deco_rect);
dump_rect(gen, "actual_deco_rect", con->deco_rect);
} else {
dump_rect(gen, "deco_rect", con->deco_rect);
}
dump_rect(gen, "window_rect", con->window_rect); dump_rect(gen, "window_rect", con->window_rect);
dump_rect(gen, "geometry", con->geometry); dump_rect(gen, "geometry", con->geometry);

View File

@ -23,6 +23,7 @@ static Con *to_focus;
static bool parsing_gaps; static bool parsing_gaps;
static bool parsing_swallows; static bool parsing_swallows;
static bool parsing_rect; static bool parsing_rect;
static bool parsing_actual_deco_rect;
static bool parsing_deco_rect; static bool parsing_deco_rect;
static bool parsing_window_rect; static bool parsing_window_rect;
static bool parsing_geometry; static bool parsing_geometry;
@ -61,7 +62,12 @@ static int json_start_map(void *ctx) {
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches); TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
swallow_is_empty = true; swallow_is_empty = true;
} else { } else {
if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry && !parsing_gaps) { if (!parsing_rect &&
!parsing_actual_deco_rect &&
!parsing_deco_rect &&
!parsing_window_rect &&
!parsing_geometry &&
!parsing_gaps) {
if (last_key && strcasecmp(last_key, "floating_nodes") == 0) { if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
DLOG("New floating_node\n"); DLOG("New floating_node\n");
Con *ws = con_get_workspace(json_node); Con *ws = con_get_workspace(json_node);
@ -85,7 +91,13 @@ static int json_start_map(void *ctx) {
static int json_end_map(void *ctx) { static int json_end_map(void *ctx) {
LOG("end of map\n"); LOG("end of map\n");
if (!parsing_swallows && !parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry && !parsing_gaps) { if (!parsing_swallows &&
!parsing_rect &&
!parsing_actual_deco_rect &&
!parsing_deco_rect &&
!parsing_window_rect &&
!parsing_geometry &&
!parsing_gaps) {
/* Set a few default values to simplify manually crafted layout files. */ /* Set a few default values to simplify manually crafted layout files. */
if (json_node->layout == L_DEFAULT) { if (json_node->layout == L_DEFAULT) {
DLOG("Setting layout = L_SPLITH\n"); DLOG("Setting layout = L_SPLITH\n");
@ -195,6 +207,7 @@ static int json_end_map(void *ctx) {
parsing_gaps = false; parsing_gaps = false;
parsing_rect = false; parsing_rect = false;
parsing_actual_deco_rect = false;
parsing_deco_rect = false; parsing_deco_rect = false;
parsing_window_rect = false; parsing_window_rect = false;
parsing_geometry = false; parsing_geometry = false;
@ -253,6 +266,9 @@ static int json_key(void *ctx, const unsigned char *val, size_t len) {
if (strcasecmp(last_key, "rect") == 0) if (strcasecmp(last_key, "rect") == 0)
parsing_rect = true; parsing_rect = true;
if (strcasecmp(last_key, "actual_deco_rect") == 0)
parsing_actual_deco_rect = true;
if (strcasecmp(last_key, "deco_rect") == 0) if (strcasecmp(last_key, "deco_rect") == 0)
parsing_deco_rect = true; parsing_deco_rect = true;
@ -674,6 +690,7 @@ void tree_append_json(Con *con, const char *buf, const size_t len, char **errorm
incomplete = 0; incomplete = 0;
parsing_swallows = false; parsing_swallows = false;
parsing_rect = false; parsing_rect = false;
parsing_actual_deco_rect = false;
parsing_deco_rect = false; parsing_deco_rect = false;
parsing_window_rect = false; parsing_window_rect = false;
parsing_geometry = false; parsing_geometry = false;

View File

@ -69,9 +69,6 @@ void render_con(Con *con) {
con->rect = rect_add(con->rect, inset); con->rect = rect_add(con->rect, inset);
} }
inset.height = 0; inset.height = 0;
if (con->deco_rect.width != 0 && con->deco_rect.height != 0) {
con->deco_rect = rect_add(con->deco_rect, inset);
}
params.x = con->rect.x; params.x = con->rect.x;
params.y = con->rect.y; params.y = con->rect.y;
@ -84,17 +81,27 @@ void render_con(Con *con) {
if (con->window) { if (con->window) {
/* depending on the border style, the rect of the child window /* depending on the border style, the rect of the child window
* needs to be smaller */ * needs to be smaller */
Rect *inset = &(con->window_rect); Rect inset = (Rect){
*inset = (Rect){0, 0, con->rect.width, con->rect.height}; .x = 0,
.y = 0,
.width = con->rect.width,
.height = con->rect.height,
};
if (con->fullscreen_mode == CF_NONE) { if (con->fullscreen_mode == CF_NONE) {
*inset = rect_add(*inset, con_border_style_rect(con)); DLOG("deco_rect.height = %d\n", con->deco_rect.height);
Rect bsr = con_border_style_rect(con);
DLOG("bsr at %dx%d with size %dx%d\n",
bsr.x, bsr.y, bsr.width, bsr.height);
inset = rect_add(inset, bsr);
} }
/* Obey x11 border */ /* Obey x11 border */
inset->width -= (2 * con->border_width); inset.width -= (2 * con->border_width);
inset->height -= (2 * con->border_width); inset.height -= (2 * con->border_width);
*inset = rect_sanitize_dimensions(*inset); inset = rect_sanitize_dimensions(inset);
con->window_rect = inset;
/* NB: We used to respect resize increment size hints for tiling /* NB: We used to respect resize increment size hints for tiling
* windows up until commit 0db93d9 here. However, since all terminal * windows up until commit 0db93d9 here. However, since all terminal
@ -102,7 +109,8 @@ void render_con(Con *con) {
* can (by providing their fake-transparency or background color), this * can (by providing their fake-transparency or background color), this
* code was removed. See also https://bugs.i3wm.org/540 */ * code was removed. See also https://bugs.i3wm.org/540 */
DLOG("child will be at %dx%d with size %dx%d\n", inset->x, inset->y, inset->width, inset->height); DLOG("child will be at %dx%d with size %dx%d\n",
inset.x, inset.y, inset.width, inset.height);
} }
/* Check for fullscreen nodes */ /* Check for fullscreen nodes */
@ -159,6 +167,18 @@ void render_con(Con *con) {
child->rect.x, child->rect.y, child->rect.width, child->rect.height); child->rect.x, child->rect.y, child->rect.width, child->rect.height);
x_raise_con(child); x_raise_con(child);
render_con(child); render_con(child);
/* render_con_split() sets the deco_rect width based on the rect
* width, but the render_con() call updates the rect width by
* applying gaps, so we need to update deco_rect. */
if (con->layout == L_SPLITH || con->layout == L_SPLITV) {
if (con_is_leaf(child)) {
if (child->border_style == BS_NORMAL) {
child->deco_rect.width = child->rect.width;
}
}
}
i++; i++;
} }
@ -381,11 +401,8 @@ static void render_con_split(Con *con, Con *child, render_params *p, int i) {
if (con_is_leaf(child)) { if (con_is_leaf(child)) {
if (child->border_style == BS_NORMAL) { if (child->border_style == BS_NORMAL) {
/* TODO: make a function for relative coords? */ /* TODO: make a function for relative coords? */
child->deco_rect.x = child->rect.x - con->rect.x; child->deco_rect.x = 0;
child->deco_rect.y = child->rect.y - con->rect.y; child->deco_rect.y = 0;
child->rect.y += p->deco_height;
child->rect.height -= p->deco_height;
child->deco_rect.width = child->rect.width; child->deco_rect.width = child->rect.width;
child->deco_rect.height = p->deco_height; child->deco_rect.height = p->deco_height;

View File

@ -10,23 +10,6 @@
#include "all.h" #include "all.h"
static xcb_window_t create_drop_indicator(Rect rect); static xcb_window_t create_drop_indicator(Rect rect);
/*
* Includes decoration (container title) to the container's rect. This way we
* can find the correct drop target if the mouse is on a container's
* decoration.
*
*/
static Rect con_rect_plus_deco_height(Con *con) {
Rect rect = con->rect;
rect.height += con->deco_rect.height;
if (rect.y < con->deco_rect.height) {
rect.y = 0;
} else {
rect.y -= con->deco_rect.height;
}
return rect;
}
static bool is_tiling_drop_target(Con *con) { static bool is_tiling_drop_target(Con *con) {
if (!con_has_managed_window(con) || if (!con_has_managed_window(con) ||
con_is_floating(con) || con_is_floating(con) ||
@ -90,7 +73,7 @@ bool has_drop_targets(void) {
static Con *find_drop_target(uint32_t x, uint32_t y) { static Con *find_drop_target(uint32_t x, uint32_t y) {
Con *con; Con *con;
TAILQ_FOREACH (con, &all_cons, all_cons) { TAILQ_FOREACH (con, &all_cons, all_cons) {
Rect rect = con_rect_plus_deco_height(con); Rect rect = con->rect;
if (!rect_contains(rect, x, y) || if (!rect_contains(rect, x, y) ||
!is_tiling_drop_target(con)) { !is_tiling_drop_target(con)) {
continue; continue;
@ -186,7 +169,7 @@ DRAGGING_CB(drag_callback) {
return; return;
} }
Rect rect = con_rect_plus_deco_height(target); Rect rect = target->rect;
direction_t direction = 0; direction_t direction = 0;
drop_type_t drop_type = DT_CENTER; drop_type_t drop_type = DT_CENTER;

59
src/x.c
View File

@ -355,29 +355,27 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
free(event); free(event);
} }
static void x_draw_title_border(Con *con, struct deco_render_params *p) { static void x_draw_title_border(Con *con, struct deco_render_params *p, surface_t *dest_surface) {
assert(con->parent != NULL);
Rect *dr = &(con->deco_rect); Rect *dr = &(con->deco_rect);
/* Left */ /* Left */
draw_util_rectangle(&(con->parent->frame_buffer), p->color->border, draw_util_rectangle(dest_surface, p->color->border,
dr->x, dr->y, 1, dr->height); dr->x, dr->y, 1, dr->height);
/* Right */ /* Right */
draw_util_rectangle(&(con->parent->frame_buffer), p->color->border, draw_util_rectangle(dest_surface, p->color->border,
dr->x + dr->width - 1, dr->y, 1, dr->height); dr->x + dr->width - 1, dr->y, 1, dr->height);
/* Top */ /* Top */
draw_util_rectangle(&(con->parent->frame_buffer), p->color->border, draw_util_rectangle(dest_surface, p->color->border,
dr->x, dr->y, dr->width, 1); dr->x, dr->y, dr->width, 1);
/* Bottom */ /* Bottom */
draw_util_rectangle(&(con->parent->frame_buffer), p->color->border, draw_util_rectangle(dest_surface, p->color->border,
dr->x, dr->y + dr->height - 1, dr->width, 1); dr->x, dr->y + dr->height - 1, dr->width, 1);
} }
static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p) { static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p, surface_t *dest_surface) {
assert(con->parent != NULL); assert(con->parent != NULL);
Rect *dr = &(con->deco_rect); Rect *dr = &(con->deco_rect);
@ -389,7 +387,7 @@ static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p
/* We actually only redraw the far right two pixels as that is the /* We actually only redraw the far right two pixels as that is the
* distance we keep from the edge (not the entire border width). * distance we keep from the edge (not the entire border width).
* Redrawing the entire border would cause text to be cut off. */ * Redrawing the entire border would cause text to be cut off. */
draw_util_rectangle(&(con->parent->frame_buffer), p->color->background, draw_util_rectangle(dest_surface, p->color->background,
dr->x + dr->width - 2 * logical_px(1), dr->x + dr->width - 2 * logical_px(1),
dr->y, dr->y,
2 * logical_px(1), 2 * logical_px(1),
@ -397,7 +395,7 @@ static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p
} }
/* Redraw the border. */ /* Redraw the border. */
x_draw_title_border(con, p); x_draw_title_border(con, p, dest_surface);
} }
/* /*
@ -593,16 +591,24 @@ void x_draw_decoration(Con *con) {
} }
} }
surface_t *dest_surface = &(parent->frame_buffer);
if (con_draw_decoration_into_frame(con)) {
DLOG("using con->frame_buffer (for con->name=%s) as dest_surface\n", con->name);
dest_surface = &(con->frame_buffer);
} else {
DLOG("sticking to parent->frame_buffer = %p\n", dest_surface);
}
DLOG("dest_surface %p is %d x %d (id=0x%08x)\n", dest_surface, dest_surface->width, dest_surface->height, dest_surface->id);
/* If the parent hasn't been set up yet, skip the decoration rendering /* If the parent hasn't been set up yet, skip the decoration rendering
* for now. */ * for now. */
if (parent->frame_buffer.id == XCB_NONE) if (dest_surface->id == XCB_NONE)
goto copy_pixmaps; goto copy_pixmaps;
/* For the first child, we clear the parent pixmap to ensure there's no /* For the first child, we clear the parent pixmap to ensure there's no
* garbage left on there. This is important to avoid tearing when using * garbage left on there. This is important to avoid tearing when using
* transparency. */ * transparency. */
if (con == TAILQ_FIRST(&(con->parent->nodes_head))) { if (con == TAILQ_FIRST(&(con->parent->nodes_head))) {
draw_util_clear_surface(&(con->parent->frame_buffer), COLOR_TRANSPARENT);
FREE(con->parent->deco_render_params); FREE(con->parent->deco_render_params);
} }
@ -612,11 +618,13 @@ void x_draw_decoration(Con *con) {
goto copy_pixmaps; goto copy_pixmaps;
/* 4: paint the bar */ /* 4: paint the bar */
draw_util_rectangle(&(parent->frame_buffer), p->color->background, DLOG("con->deco_rect = (x=%d, y=%d, w=%d, h=%d)\n",
con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height);
draw_util_rectangle(dest_surface, p->color->background,
con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height); con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height);
/* 5: draw title border */ /* 5: draw title border */
x_draw_title_border(con, p); x_draw_title_border(con, p, dest_surface);
/* 6: draw the icon and title */ /* 6: draw the icon and title */
int text_offset_y = (con->deco_rect.height - config.font.height) / 2; int text_offset_y = (con->deco_rect.height - config.font.height) / 2;
@ -651,7 +659,7 @@ void x_draw_decoration(Con *con) {
? title_padding ? title_padding
: deco_width - mark_width - title_padding; : deco_width - mark_width - title_padding;
draw_util_text(mark, &(parent->frame_buffer), draw_util_text(mark, dest_surface,
p->color->text, p->color->background, p->color->text, p->color->background,
con->deco_rect.x + mark_offset_x, con->deco_rect.x + mark_offset_x,
con->deco_rect.y + text_offset_y, mark_width); con->deco_rect.y + text_offset_y, mark_width);
@ -725,7 +733,7 @@ void x_draw_decoration(Con *con) {
break; break;
} }
draw_util_text(title, &(parent->frame_buffer), draw_util_text(title, dest_surface,
p->color->text, p->color->background, p->color->text, p->color->background,
con->deco_rect.x + title_offset_x, con->deco_rect.x + title_offset_x,
con->deco_rect.y + text_offset_y, con->deco_rect.y + text_offset_y,
@ -733,7 +741,7 @@ void x_draw_decoration(Con *con) {
if (has_icon) { if (has_icon) {
draw_util_image( draw_util_image(
win->icon, win->icon,
&(parent->frame_buffer), dest_surface,
con->deco_rect.x + icon_offset_x, con->deco_rect.x + icon_offset_x,
con->deco_rect.y + logical_px(1), con->deco_rect.y + logical_px(1),
icon_size, icon_size,
@ -744,7 +752,7 @@ void x_draw_decoration(Con *con) {
I3STRING_FREE(title); I3STRING_FREE(title);
} }
x_draw_decoration_after_title(con, p); x_draw_decoration_after_title(con, p, dest_surface);
copy_pixmaps: copy_pixmaps:
draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height); draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
} }
@ -891,7 +899,7 @@ void x_push_node(Con *con) {
FREE(state->name); FREE(state->name);
} }
if (con->window == NULL) { if (con->window == NULL && (con->layout == L_STACKED || con->layout == L_TABBED)) {
/* Calculate the height of all window decorations which will be drawn on to /* Calculate the height of all window decorations which will be drawn on to
* this frame. */ * this frame. */
uint32_t max_y = 0, max_height = 0; uint32_t max_y = 0, max_height = 0;
@ -905,6 +913,9 @@ void x_push_node(Con *con) {
rect.height = max_y + max_height; rect.height = max_y + max_height;
if (rect.height == 0) if (rect.height == 0)
con->mapped = false; con->mapped = false;
} else if (con->window == NULL) {
/* not a stacked or tabbed split container */
con->mapped = false;
} }
bool need_reshape = false; bool need_reshape = false;
@ -949,10 +960,11 @@ void x_push_node(Con *con) {
/* The pixmap of a borderless leaf container will not be used except /* The pixmap of a borderless leaf container will not be used except
* for the titlebar in a stack or tabs (issue #1013). */ * for the titlebar in a stack or tabs (issue #1013). */
bool is_pixmap_needed = (con->border_style != BS_NONE || bool is_pixmap_needed = ((con_is_leaf(con) && con->border_style != BS_NONE) ||
!con_is_leaf(con) || con->layout == L_STACKED ||
con->parent->layout == L_STACKED || con->layout == L_TABBED);
con->parent->layout == L_TABBED); DLOG("Con %p (layout %d), is_pixmap_needed = %s, rect.height = %d\n",
con, con->layout, is_pixmap_needed ? "yes" : "no", con->rect.height);
/* The root con and output cons will never require a pixmap. In particular for the /* The root con and output cons will never require a pixmap. In particular for the
* __i3 output, this will likely not work anyway because it might be ridiculously * __i3 output, this will likely not work anyway because it might be ridiculously
@ -999,6 +1011,7 @@ void x_push_node(Con *con) {
int width = MAX((int32_t)rect.width, 1); int width = MAX((int32_t)rect.width, 1);
int height = MAX((int32_t)rect.height, 1); int height = MAX((int32_t)rect.height, 1);
DLOG("creating %d x %d pixmap for con %p (con->frame_buffer.id = (pixmap_t)0x%08x) (con->frame.id (drawable_t)0x%08x)\n", width, height, con, con->frame_buffer.id, con->frame.id);
xcb_create_pixmap(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height); xcb_create_pixmap(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height);
draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id, draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id,
get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height); get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height);

View File

@ -50,7 +50,7 @@ $target = get_focused($ws);
$A = $cons[0]; $A = $cons[0];
$C = $cons[1]->{nodes}[1]; $C = $cons[1]->{nodes}[1];
$y = $C->{rect}->{y} - 0.5 * $C->{deco_rect}->{height}; $y = $C->{rect}->{y} + 0.5 * $C->{deco_rect}->{height};
# make sure that B is the focus head of its parent # make sure that B is the focus head of its parent
cmd '[id="' . $B->{id} . '"] focus'; cmd '[id="' . $B->{id} . '"] focus';