diff --git a/doc/bspwm.1 b/doc/bspwm.1 index fdc5be7..f5467c5 100644 --- a/doc/bspwm.1 +++ b/doc/bspwm.1 @@ -444,6 +444,11 @@ floating Is above any tiled window and can be moved/resized freely\&. Although it doesn\(cqt occupy any tiling space, it is still part of the window tree\&. .RE .PP +pseudo_tiled +.RS 4 +Has a libre size while being centered in its tiling space\&. +.RE +.PP fullscreen .RS 4 Fills its monitor rectangle, is above all the other windows and has no borders\&. @@ -534,7 +539,7 @@ Set and change the splitting ratio of (or pull, or push) the edge located in the Rotate the tree holding the edge located in the given direction in relation to the selected window\&. .RE .PP -\fB\-t\fR, \fB\-\-toggle\fR floating|fullscreen|locked|sticky|private[=on|off] +\fB\-t\fR, \fB\-\-toggle\fR floating|fullscreen|pseudo_tiled|locked|sticky|private[=on|off] .RS 4 Set or toggle the given state for the selected window\&. .RE @@ -876,7 +881,7 @@ rule \fIOPTIONS\fR \fBOptions\fR .RS 4 .PP -\fB\-a\fR, \fB\-\-add\fR ||* [\fB\-o\fR|\fB\-\-one\-shot\fR] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)] +\fB\-a\fR, \fB\-\-add\fR ||* [\fB\-o\fR|\fB\-\-one\-shot\fR] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)] .RS 4 Create a new rule\&. .RE diff --git a/doc/bspwm.1.txt b/doc/bspwm.1.txt index e294f60..de513c5 100644 --- a/doc/bspwm.1.txt +++ b/doc/bspwm.1.txt @@ -293,6 +293,9 @@ Window States floating:: Is above any tiled window and can be moved/resized freely. Although it doesn't occupy any tiling space, it is still part of the window tree. +pseudo_tiled:: + Has a libre size while being centered in its tiling space. + fullscreen:: Fills its monitor rectangle, is above all the other windows and has no borders. @@ -346,7 +349,7 @@ Options *-R*, *--rotate* 'DIR' '90|270|180':: Rotate the tree holding the edge located in the given direction in relation to the selected window. -*-t*, *--toggle* floating|fullscreen|locked|sticky|private[=on|off]:: +*-t*, *--toggle* floating|fullscreen|pseudo_tiled|locked|sticky|private[=on|off]:: Set or toggle the given state for the selected window. *-c*, *--close*:: @@ -538,7 +541,7 @@ rule 'OPTIONS' Options ^^^^^^^ -*-a*, *--add* ||* [*-o*|*--one-shot*] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)]:: +*-a*, *--add* ||* [*-o*|*--one-shot*] [desktop=DESKTOP_SEL|monitor=MONITOR_SEL] [(floating|fullscreen|pseudo_tiled|locked|sticky|private|center|lower|follow|manage|focus)=(true|false)]:: Create a new rule. *-r*, *--remove* ^|head|tail|||*...:: diff --git a/events.c b/events.c index 4e8bbf5..2ad619b 100644 --- a/events.c +++ b/events.c @@ -120,6 +120,9 @@ void configure_request(xcb_generic_event_t *evt) evt.override_redirect = false; xcb_send_event(dpy, false, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt); + + if (loc.node->client->pseudo_tiled) + arrange(loc.monitor, loc.desktop); } else { uint16_t mask = 0; uint32_t values[7]; @@ -170,8 +173,12 @@ void configure_request(xcb_generic_event_t *evt) xcb_configure_window(dpy, e->window, mask, values); } - if (is_managed) - translate_client(monitor_from_client(loc.node->client), loc.monitor, loc.node->client); + + if (is_managed) { + monitor_t *m = monitor_from_client(loc.node->client); + if (m != NULL && m != loc.monitor) + transfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus); + } } void destroy_notify(xcb_generic_event_t *evt) diff --git a/helpers.c b/helpers.c index 6e1f312..ccc197a 100644 --- a/helpers.c +++ b/helpers.c @@ -83,3 +83,9 @@ double distance(xcb_point_t a, xcb_point_t b) { return hypot(a.x - b.x, a.y - b.y); } + +void center_rectangle(xcb_rectangle_t *src, xcb_rectangle_t dst) +{ + src->x = dst.x + (dst.width - src->width) / 2; + src->y = dst.y + (dst.height - src->height) / 2; +} diff --git a/helpers.h b/helpers.h index 0dc74c8..81821ad 100644 --- a/helpers.h +++ b/helpers.h @@ -56,9 +56,9 @@ #endif void warn(char *fmt, ...); -__attribute__((noreturn)) void err(char *fmt, ...); bool get_color(char *col, xcb_window_t win, uint32_t *pxl); double distance(xcb_point_t a, xcb_point_t b); +void center_rectangle(xcb_rectangle_t *src, xcb_rectangle_t dst); #endif diff --git a/messages.c b/messages.c index cb43a19..6bd52af 100644 --- a/messages.c +++ b/messages.c @@ -204,6 +204,9 @@ bool cmd_window(char **args, int num) if (streq("fullscreen", key)) { set_fullscreen(trg.node, (a == ALTER_SET ? b : !trg.node->client->fullscreen)); dirty = true; + } else if (streq("pseudo_tiled", key)) { + set_pseudo_tiled(trg.node, (a == ALTER_SET ? b : !trg.node->client->pseudo_tiled)); + dirty = true; } else if (streq("floating", key)) { set_floating(trg.node, (a == ALTER_SET ? b : !trg.node->client->floating)); dirty = true; diff --git a/pointer.c b/pointer.c index 19e9144..6f100d1 100644 --- a/pointer.c +++ b/pointer.c @@ -69,12 +69,12 @@ void grab_pointer(pointer_action_t pac) case ACTION_MOVE: case ACTION_RESIZE_SIDE: case ACTION_RESIZE_CORNER: - if (is_tiled(c)) { - frozen_pointer->rectangle = c->tiled_rectangle; - frozen_pointer->is_tiled = true; - } else if (is_floating(c)) { + if (is_floating(c)) { frozen_pointer->rectangle = c->floating_rectangle; frozen_pointer->is_tiled = false; + } else if (is_tiled(c)) { + frozen_pointer->rectangle = c->tiled_rectangle; + frozen_pointer->is_tiled = true; } else { frozen_pointer->action = ACTION_NONE; return; @@ -254,7 +254,7 @@ void track_pointer(int root_x, int root_y) sr = MIN(1, sr); horizontal_fence->split_ratio = sr; } - arrange(mon, mon->desk); + arrange(m, d); } else { if (pac == ACTION_RESIZE_SIDE) { switch (frozen_pointer->side) { @@ -283,11 +283,6 @@ void track_pointer(int root_x, int root_y) h = rect.height; break; } - width = MAX(1, w); - height = MAX(1, h); - window_move_resize(win, x, y, width, height); - c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height}; - window_draw_border(n, d->focus == n, mon == m); } else if (pac == ACTION_RESIZE_CORNER) { switch (frozen_pointer->corner) { case CORNER_TOP_LEFT: @@ -315,12 +310,11 @@ void track_pointer(int root_x, int root_y) h = rect.height + delta_y; break; } - width = MAX(1, w); - height = MAX(1, h); - window_move_resize(win, x, y, width, height); - c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height}; - window_draw_border(n, d->focus == n, mon == m); } + width = MAX(1, w); + height = MAX(1, h); + window_move_resize(win, x, y, width, height); + c->floating_rectangle = (xcb_rectangle_t) {x, y, width, height}; } break; case ACTION_FOCUS: diff --git a/query.c b/query.c index 71873ac..fd2e538 100644 --- a/query.c +++ b/query.c @@ -90,7 +90,7 @@ void query_tree(desktop_t *d, node_t *n, char *rsp, unsigned int depth) if (is_leaf(n)) { client_t *c = n->client; - snprintf(line, sizeof(line), "%c %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))), (c->floating ? 'f' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-')); + snprintf(line, sizeof(line), "%c %s 0x%X %u %ux%u%+i%+i %c %c%c%c%c%c%c%c%c%c", (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), c->class_name, c->window, c->border_width, c->floating_rectangle.width, c->floating_rectangle.height, c->floating_rectangle.x, c->floating_rectangle.y, (n->split_dir == DIR_UP ? 'U' : (n->split_dir == DIR_RIGHT ? 'R' : (n->split_dir == DIR_DOWN ? 'D' : 'L'))), (c->floating ? 'f' : '-'), (c->pseudo_tiled ? 'd' : '-'), (c->transient ? 't' : '-'), (c->fullscreen ? 'F' : '-'), (c->urgent ? 'u' : '-'), (c->locked ? 'l' : '-'), (c->sticky ? 's' : '-'), (c->private ? 'i' : '-'), (n->split_mode ? 'p' : '-')); } else { snprintf(line, sizeof(line), "%c %c %lf", (n->split_type == TYPE_HORIZONTAL ? 'H' : 'V'), (n->birth_rotation == 90 ? 'a' : (n->birth_rotation == 270 ? 'c' : 'm')), n->split_ratio); } diff --git a/restore.c b/restore.c index 2f7ff07..cc23722 100644 --- a/restore.c +++ b/restore.c @@ -134,9 +134,10 @@ void restore_tree(char *file_path) } else { client_t *c = make_client(XCB_NONE); num_clients++; - char floating, transient, fullscreen, urgent, locked, sticky, private, sd, sm, end = 0; - sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &transient, &fullscreen, &urgent, &locked, &sticky, &private, &sm, &end); + char floating, pseudo_tiled, transient, fullscreen, urgent, locked, sticky, private, sd, sm, end = 0; + sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &pseudo_tiled, &transient, &fullscreen, &urgent, &locked, &sticky, &private, &sm, &end); c->floating = (floating == '-' ? false : true); + c->pseudo_tiled = (pseudo_tiled == '-' ? false : true); c->transient = (transient == '-' ? false : true); c->fullscreen = (fullscreen == '-' ? false : true); c->urgent = (urgent == '-' ? false : true); diff --git a/rule.c b/rule.c index 13d3566..d67ad82 100644 --- a/rule.c +++ b/rule.c @@ -276,6 +276,7 @@ void parse_key_value(char *key, char *value, rule_consequence_t *csq) #define SETCSQ(name) \ else if (streq(#name, key)) \ csq->name = v; + SETCSQ(pseudo_tiled) SETCSQ(fullscreen) SETCSQ(locked) SETCSQ(sticky) diff --git a/tree.c b/tree.c index 514e738..df78dd5 100644 --- a/tree.c +++ b/tree.c @@ -70,17 +70,18 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x xcb_rectangle_t r; if (!n->client->fullscreen) { if (!n->client->floating) { - /* tiled clients */ - if (d->layout == LAYOUT_TILED) + if (n->client->pseudo_tiled) { + /* pseudo-tiled clients */ + r = n->client->floating_rectangle; + center_rectangle(&r, rect); + } else { + /* tiled clients */ r = rect; - else if (d->layout == LAYOUT_MONOCLE) - r = root_rect; - else - return; - int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap); - int bleed = wg + 2 * n->client->border_width; - r.width = (bleed < r.width ? r.width - bleed : 1); - r.height = (bleed < r.height ? r.height - bleed : 1); + int wg = (gapless_monocle && d->layout == LAYOUT_MONOCLE ? 0 : d->window_gap); + int bleed = wg + 2 * n->client->border_width; + r.width = (bleed < r.width ? r.width - bleed : 1); + r.height = (bleed < r.height ? r.height - bleed : 1); + } n->client->tiled_rectangle = r; } else { /* floating clients */ @@ -99,7 +100,7 @@ void apply_layout(monitor_t *m, desktop_t *d, node_t *n, xcb_rectangle_t rect, x xcb_rectangle_t first_rect; xcb_rectangle_t second_rect; - if (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; @@ -360,8 +361,8 @@ client_t *make_client(xcb_window_t win) snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE); c->border_width = BORDER_WIDTH; c->window = win; - c->floating = c->transient = c->fullscreen = c->locked = c->sticky = c->urgent = false; - c->private = c->icccm_focus = false; + c->pseudo_tiled = c->floating = c->transient = c->fullscreen = false; + c->locked = c->sticky = c->urgent = c->private = c->icccm_focus = false; xcb_icccm_get_wm_protocols_reply_t protocols; if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) { if (has_proto(WM_TAKE_FOCUS, &protocols)) diff --git a/types.h b/types.h index 0d3d1ef..6316b6b 100644 --- a/types.h +++ b/types.h @@ -155,6 +155,7 @@ typedef struct { xcb_window_t window; char class_name[SMALEN]; unsigned int border_width; + bool pseudo_tiled; bool floating; bool transient; /* transient window are always floating */ bool fullscreen; @@ -260,6 +261,7 @@ typedef struct { char instance_name[SMALEN]; char desktop_desc[MAXLEN]; char monitor_desc[MAXLEN]; + bool pseudo_tiled; bool floating; bool transient; bool fullscreen; diff --git a/window.c b/window.c index 3d4adfd..c891f9b 100644 --- a/window.c +++ b/window.c @@ -118,6 +118,7 @@ void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd) insert_node(m, d, n, d->focus); disable_floating_atom(c->window); + set_pseudo_tiled(n, csq->pseudo_tiled); set_floating(n, csq->floating); set_locked(m, d, n, csq->locked); set_sticky(m, d, n, csq->sticky); @@ -360,6 +361,16 @@ void set_fullscreen(node_t *n, bool value) stack(n, STACK_ABOVE); } +void set_pseudo_tiled(node_t *n, bool value) +{ + if (n == NULL || n->client->transient || n->client->pseudo_tiled == value) + return; + + PRINTF("pseudo-tiled %X: %s\n", n->client->window, BOOLSTR(value)); + + n->client->pseudo_tiled = value; +} + void set_floating(node_t *n, bool value) { if (n == NULL || n->client->transient || n->client->fullscreen || n->client->floating == value) diff --git a/window.h b/window.h index 78d1983..12bc117 100644 --- a/window.h +++ b/window.h @@ -43,6 +43,7 @@ void adopt_orphans(void); void window_close(node_t *n); void window_kill(monitor_t *m, desktop_t *d, node_t *n); void set_fullscreen(node_t *n, bool value); +void set_pseudo_tiled(node_t *n, bool value); void set_floating(node_t *n, bool value); void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value); void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);