From 00cf7896a4b679ea9c666aed67aaf162efd44f29 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Thu, 31 Jan 2019 20:29:10 +0100 Subject: [PATCH] Handle `single_monocle` more carefully Closes #919. Fixes #730. --- src/desktop.c | 41 ++++++++++++++++++++++-------------- src/desktop.h | 2 +- src/helpers.h | 4 ---- src/messages.c | 24 +++++++++++++++++---- src/query.c | 1 + src/restore.c | 1 + src/subscribe.c | 2 +- src/tree.c | 55 +++++++++++++++++++++++++++++++++++++------------ src/tree.h | 2 +- src/types.h | 1 + src/window.c | 6 +++++- 11 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/desktop.c b/src/desktop.c index 098c97a..c51e3db 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -135,32 +135,41 @@ bool find_any_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t * return false; } -bool set_layout(monitor_t *m, desktop_t *d, layout_t l) +bool set_layout(monitor_t *m, desktop_t *d, layout_t l, bool user) { - layout_t actual_layout = IS_SINGLE_MONOCLE(d) ? LAYOUT_MONOCLE : l; - bool actual_layout_changed = (d->layout != actual_layout); - - if (d->layout == l) { + if ((user && d->user_layout == l) || (!user && d->layout == l)) { return false; } - d->layout = l; + layout_t old_layout = d->layout; - handle_presel_feedbacks(m, d); + if (user) { + d->user_layout = l; + } else { + d->layout = l; + } - if (!actual_layout_changed) { - return true; + if (user && (!single_monocle || tiled_count(d->root, true) > 1)) { + d->layout = l; } - arrange(m, d); + if (d->layout != old_layout) { + handle_presel_feedbacks(m, d); - put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(actual_layout)); + if (user) { + arrange(m, d); + } - if (d == m->desk) { - put_status(SBSC_MASK_REPORT); - } + put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(d->layout)); - return true; + if (d == m->desk) { + put_status(SBSC_MASK_REPORT); + } + + return true; + } else { + return false; + } } void handle_presel_feedbacks(monitor_t *m, desktop_t *d) @@ -255,7 +264,7 @@ desktop_t *make_desktop(const char *name, uint32_t id) } d->prev = d->next = NULL; d->root = d->focus = NULL; - d->layout = LAYOUT_TILED; + d->layout = d->user_layout = LAYOUT_TILED; d->padding = (padding_t) PADDING; d->window_gap = window_gap; d->border_width = border_width; diff --git a/src/desktop.h b/src/desktop.h index dd544a4..132a482 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -31,7 +31,7 @@ void focus_desktop(monitor_t *m, desktop_t *d); bool activate_desktop(monitor_t *m, desktop_t *d); bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t *sel); bool find_any_desktop(coordinates_t *ref, coordinates_t *dst, desktop_select_t *sel); -bool set_layout(monitor_t *m, desktop_t *d, layout_t l); +bool set_layout(monitor_t *m, desktop_t *d, layout_t l, bool user); void handle_presel_feedbacks(monitor_t *m, desktop_t *d); bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d, bool follow); desktop_t *make_desktop(const char *name, uint32_t id); diff --git a/src/helpers.h b/src/helpers.h index 060a494..e51e5d0 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -41,10 +41,6 @@ #define IS_FULLSCREEN(c) (c->state == STATE_FULLSCREEN) #define IS_RECEPTACLE(n) (is_leaf(n) && n->client == NULL) -#define IS_SINGLE_MONOCLE(d) (single_monocle && tiled_count(d->root, true) <= 1) -#define IS_MONOCLE(d) (d->layout == LAYOUT_MONOCLE || IS_SINGLE_MONOCLE(d)) -#define ACTUAL_LAYOUT(d) (IS_MONOCLE(d) ? LAYOUT_MONOCLE : d->layout) - #define BOOL_STR(A) ((A) ? "true" : "false") #define ON_OFF_STR(A) ((A) ? "on" : "off") #define LAYOUT_STR(A) ((A) == LAYOUT_TILED ? "tiled" : "monocle") diff --git a/src/messages.c b/src/messages.c index b08ca04..62eaa85 100644 --- a/src/messages.c +++ b/src/messages.c @@ -745,9 +745,9 @@ void cmd_desktop(char **args, int num, FILE *rsp) layout_t lyt; cycle_dir_t cyc; if (parse_cycle_direction(*args, &cyc)) { - ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2); + ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2, true); } else if (parse_layout(*args, &lyt)) { - ret = set_layout(trg.monitor, trg.desktop, lyt); + ret = set_layout(trg.monitor, trg.desktop, lyt, true); } else { fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args); break; @@ -1637,6 +1637,24 @@ void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp) fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); return; } + } else if (streq("single_monocle", name)) { + bool b; + if (parse_bool(value, &b)) { + if (b == single_monocle) { + fail(rsp, ""); + return; + } + single_monocle = b; + for (monitor_t *m = mon_head; m != NULL; m = m->next) { + for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { + layout_t l = (single_monocle && tiled_count(d->root, true) <= 1) ? LAYOUT_MONOCLE : d->user_layout; + set_layout(m, d, l, false); + } + } + } else { + fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); + return; + } } else if (streq("focus_follows_pointer", name)) { bool b; if (parse_bool(value, &b)) { @@ -1674,8 +1692,6 @@ void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp) SET_BOOL(presel_feedback) SET_BOOL(borderless_monocle) SET_BOOL(gapless_monocle) - SET_BOOL(single_monocle) - put_status(SBSC_MASK_REPORT); SET_BOOL(swallow_first_click) SET_BOOL(pointer_follows_focus) SET_BOOL(pointer_follows_monitor) diff --git a/src/query.c b/src/query.c index 156dbfe..9084e4c 100644 --- a/src/query.c +++ b/src/query.c @@ -101,6 +101,7 @@ void query_desktop(desktop_t *d, FILE *rsp) fprintf(rsp, "\"name\":\"%s\",", d->name); fprintf(rsp, "\"id\":%u,", d->id); fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout)); + fprintf(rsp, "\"userLayout\":\"%s\",", LAYOUT_STR(d->user_layout)); fprintf(rsp, "\"windowGap\":%i,", d->window_gap); fprintf(rsp, "\"borderWidth\":%u,", d->border_width); fprintf(rsp, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0); diff --git a/src/restore.c b/src/restore.c index 46d2dda..79a9159 100644 --- a/src/restore.c +++ b/src/restore.c @@ -314,6 +314,7 @@ desktop_t *restore_desktop(jsmntok_t **t, char *json) snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start); RESTORE_UINT(id, &d->id) RESTORE_ANY(layout, &d->layout, parse_layout) + RESTORE_ANY(userLayout, &d->user_layout, parse_layout) RESTORE_INT(windowGap, &d->window_gap) RESTORE_UINT(borderWidth, &d->border_width) } else if (keyeq("focusedNodeId", *t, json)) { diff --git a/src/subscribe.c b/src/subscribe.c index 54dd0d2..ebf121a 100644 --- a/src/subscribe.c +++ b/src/subscribe.c @@ -101,7 +101,7 @@ int print_report(FILE *stream) fprintf(stream, ":%c%s", c, d->name); } if (m->desk != NULL) { - fprintf(stream, ":L%c", LAYOUT_CHR(ACTUAL_LAYOUT(m->desk))); + fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout)); if (m->desk->focus != NULL) { node_t *n = m->desk->focus; if (n->client != NULL) { diff --git a/src/tree.c b/src/tree.c index fa79a1b..8bdae31 100644 --- a/src/tree.c +++ b/src/tree.c @@ -46,8 +46,6 @@ void arrange(monitor_t *m, desktop_t *d) return; } - layout_t l = ACTUAL_LAYOUT(d); - xcb_rectangle_t rect = m->rectangle; rect.x += m->padding.left + d->padding.left; @@ -55,24 +53,24 @@ void arrange(monitor_t *m, desktop_t *d) rect.width -= m->padding.left + d->padding.left + d->padding.right + m->padding.right; rect.height -= m->padding.top + d->padding.top + d->padding.bottom + m->padding.bottom; - if (l == LAYOUT_MONOCLE) { + if (d->layout == LAYOUT_MONOCLE) { rect.x += monocle_padding.left; rect.y += monocle_padding.top; rect.width -= monocle_padding.left + monocle_padding.right; rect.height -= monocle_padding.top + monocle_padding.bottom; } - if (!gapless_monocle || l != LAYOUT_MONOCLE) { + if (!gapless_monocle || d->layout != LAYOUT_MONOCLE) { rect.x += d->window_gap; rect.y += d->window_gap; rect.width -= d->window_gap; rect.height -= d->window_gap; } - apply_layout(m, d, d->root, l, rect, rect); + apply_layout(m, d, d->root, rect, rect); } -void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect) +void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect) { if (n == NULL) { return; @@ -91,7 +89,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectang } unsigned int bw; - if ((borderless_monocle && l == LAYOUT_MONOCLE && IS_TILED(n->client)) + if ((borderless_monocle && d->layout == LAYOUT_MONOCLE && IS_TILED(n->client)) || n->client->state == STATE_FULLSCREEN) { bw = 0; } else { @@ -103,7 +101,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectang client_state_t s = n->client->state; /* tiled and pseudo-tiled clients */ if (s == STATE_TILED || s == STATE_PSEUDO_TILED) { - int wg = (gapless_monocle && l == LAYOUT_MONOCLE ? 0 : d->window_gap); + int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap); r = rect; int bleed = wg + 2 * bw; r.width = (bleed < r.width ? r.width - bleed : 1); @@ -143,7 +141,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectang xcb_rectangle_t first_rect; xcb_rectangle_t second_rect; - if (l == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) { + if (d->layout == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) { first_rect = second_rect = rect; } else { unsigned int fence; @@ -176,8 +174,8 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectang } } - apply_layout(m, d, n->first_child, l, first_rect, root_rect); - apply_layout(m, d, n->second_child, l, second_rect, root_rect); + apply_layout(m, d, n->first_child, first_rect, root_rect); + apply_layout(m, d, n->second_child, second_rect, root_rect); } } @@ -440,7 +438,9 @@ void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n) node_t *r = make_node(XCB_NONE); insert_node(m, d, r, n); - put_status(SBSC_MASK_REPORT); + if (single_monocle && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) { + set_layout(m, d, d->user_layout, false); + } } bool activate_node(monitor_t *m, desktop_t *d, node_t *n) @@ -1321,6 +1321,10 @@ void remove_node(monitor_t *m, desktop_t *d, node_t *n) } free_node(n); + if (single_monocle && d->layout != LAYOUT_MONOCLE && tiled_count(d->root, true) <= 1) { + set_layout(m, d, LAYOUT_MONOCLE, false); + } + ewmh_update_client_list(false); ewmh_update_client_list(true); @@ -1430,6 +1434,13 @@ bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop show_node(d1, n2); } + if (single_monocle) { + layout_t l1 = tiled_count(d1->root, true) <= 1 ? LAYOUT_MONOCLE : d1->user_layout; + layout_t l2 = tiled_count(d2->root, true) <= 1 ? LAYOUT_MONOCLE : d2->user_layout; + set_layout(m1, d1, l1, false); + set_layout(m2, d2, l2, false); + } + if (n1_held_focus) { if (d1_was_focused) { if (follow) { @@ -1526,6 +1537,14 @@ bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desk draw_border(ns, is_descendant(ns, ds->focus), (ms == mon)); } } else { + if (single_monocle) { + if (ds->layout != LAYOUT_MONOCLE && tiled_count(ds->root, true) <= 1) { + set_layout(ms, ds, LAYOUT_MONOCLE, false); + } + if (dd->layout == LAYOUT_MONOCLE && tiled_count(dd->root, true) > 1) { + set_layout(md, dd, dd->user_layout, false); + } + } if (held_focus) { if (follow) { if (ds_was_focused) { @@ -1725,6 +1744,8 @@ bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s) client_t *c = n->client; + bool was_tiled = IS_TILED(c); + c->last_state = c->state; c->state = s; @@ -1760,6 +1781,14 @@ bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s) put_status(SBSC_MASK_REPORT); } + if (single_monocle && was_tiled != IS_TILED(c)) { + if (was_tiled && d->layout != LAYOUT_MONOCLE && tiled_count(d->root, true) <= 1) { + set_layout(m, d, LAYOUT_MONOCLE, false); + } else if (!was_tiled && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) { + set_layout(m, d, d->user_layout, false); + } + } + return true; } @@ -2054,7 +2083,7 @@ xcb_rectangle_t get_rectangle(monitor_t *m, desktop_t *d, node_t *n) return c->tiled_rectangle; } } else { - int wg = (d == NULL ? 0 : (gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap)); + int wg = (d == NULL ? 0 : (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap)); xcb_rectangle_t rect = n->rectangle; rect.width -= wg; rect.height -= wg; diff --git a/src/tree.h b/src/tree.h index 0334c2e..b571ada 100644 --- a/src/tree.h +++ b/src/tree.h @@ -29,7 +29,7 @@ #define MIN_HEIGHT 32 void arrange(monitor_t *m, desktop_t *d); -void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect); +void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, xcb_rectangle_t root_rect); presel_t *make_presel(void); void set_ratio(node_t *n, double rat); void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir); diff --git a/src/types.h b/src/types.h index a04fc84..2c15a26 100644 --- a/src/types.h +++ b/src/types.h @@ -268,6 +268,7 @@ struct desktop_t { char name[SMALEN]; uint32_t id; layout_t layout; + layout_t user_layout; node_t *root; node_t *focus; desktop_t *prev; diff --git a/src/window.c b/src/window.c index b311cbf..71e4259 100644 --- a/src/window.c +++ b/src/window.c @@ -30,6 +30,7 @@ #include "bspwm.h" #include "ewmh.h" #include "monitor.h" +#include "desktop.h" #include "query.h" #include "rule.h" #include "settings.h" @@ -165,6 +166,9 @@ bool manage_window(xcb_window_t win, rule_consequence_t *csq, int fd) f = insert_node(m, d, n, f); clients_count++; + if (single_monocle && d->layout == LAYOUT_MONOCLE && tiled_count(d->root, true) > 1) { + set_layout(m, d, d->user_layout, false); + } n->vacant = false; @@ -293,7 +297,7 @@ void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n) initialize_presel_feedback(n); } - int gap = gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap; + int gap = gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap; presel_t *p = n->presel; xcb_rectangle_t rect = n->rectangle; rect.x = rect.y = 0; -- 2.44.0