From: nicm Date: Fri, 27 Aug 2021 17:15:57 +0000 (+0000) Subject: Allow control mode clients to set a hard limit on the window width and X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=34ce0afc6eaa9d2ceb5139204be9d39097e4d3d9;p=openbsd Allow control mode clients to set a hard limit on the window width and height, GitHub issue 2594. --- diff --git a/usr.bin/tmux/cmd-refresh-client.c b/usr.bin/tmux/cmd-refresh-client.c index 5520d561931..dada29cc8aa 100644 --- a/usr.bin/tmux/cmd-refresh-client.c +++ b/usr.bin/tmux/cmd-refresh-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-refresh-client.c,v 1.44 2021/08/21 10:28:05 nicm Exp $ */ +/* $OpenBSD: cmd-refresh-client.c,v 1.45 2021/08/27 17:15:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -77,6 +77,58 @@ out: free(copy); } +static enum cmd_retval +cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); + const char *size = args_get(args, 'C'); + u_int w, x, y; + struct client_window *cw; + + if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) { + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { + cmdq_error(item, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + log_debug("%s: client %s window @%u: size %ux%u", __func__, + tc->name, w, x, y); + cw = server_client_add_client_window(tc, w); + cw->sx = x; + cw->sy = y; + tc->flags |= CLIENT_WINDOWSIZECHANGED; + recalculate_sizes_now(1); + return (CMD_RETURN_NORMAL); + } + if (sscanf(size, "@%u:", &w) == 1) { + cw = server_client_get_client_window(tc, w); + if (cw != NULL) { + log_debug("%s: client %s window @%u: no size", __func__, + tc->name, w); + cw->sx = 0; + cw->sy = 0; + recalculate_sizes_now(1); + } + return (CMD_RETURN_NORMAL); + } + + if (sscanf(size, "%u,%u", &x, &y) != 2 && + sscanf(size, "%ux%u", &x, &y) != 2) { + cmdq_error(item, "bad size argument"); + return (CMD_RETURN_ERROR); + } + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { + cmdq_error(item, "size too small or too big"); + return (CMD_RETURN_ERROR); + } + tty_set_size(&tc->tty, x, y, 0, 0); + tc->flags |= CLIENT_SIZECHANGED; + recalculate_sizes_now(1); + return (CMD_RETURN_NORMAL); +} + static void cmd_refresh_client_update_offset(struct client *tc, const char *value) { @@ -117,8 +169,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct tty *tty = &tc->tty; struct window *w; - const char *size, *errstr; - u_int x, y, adjust; + const char *errstr; + u_int adjust; struct args_value *av; if (args_has(args, 'c') || @@ -205,21 +257,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'C')) { if (~tc->flags & CLIENT_CONTROL) goto not_control_client; - size = args_get(args, 'C'); - if (sscanf(size, "%u,%u", &x, &y) != 2 && - sscanf(size, "%ux%u", &x, &y) != 2) { - cmdq_error(item, "bad size argument"); - return (CMD_RETURN_ERROR); - } - if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || - y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { - cmdq_error(item, "size too small or too big"); - return (CMD_RETURN_ERROR); - } - tty_set_size(&tc->tty, x, y, 0, 0); - tc->flags |= CLIENT_SIZECHANGED; - recalculate_sizes_now(1); - return (CMD_RETURN_NORMAL); + return (cmd_refresh_client_control_client_size(self, item)); } if (args_has(args, 'S')) { diff --git a/usr.bin/tmux/cmd-resize-window.c b/usr.bin/tmux/cmd-resize-window.c index ec523fb2e57..980bb35436c 100644 --- a/usr.bin/tmux/cmd-resize-window.c +++ b/usr.bin/tmux/cmd-resize-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-resize-window.c,v 1.8 2021/08/21 10:28:05 nicm Exp $ */ +/* $OpenBSD: cmd-resize-window.c,v 1.9 2021/08/27 17:15:57 nicm Exp $ */ /* * Copyright (c) 2018 Nicholas Marriott @@ -108,7 +108,9 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) } options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL); - resize_window(w, sx, sy, xpixel, ypixel); + w->manual_sx = sx; + w->manual_sy = sy; + recalculate_size(w, 1); return (CMD_RETURN_NORMAL); } diff --git a/usr.bin/tmux/resize.c b/usr.bin/tmux/resize.c index 45779f958d2..99df95eb0da 100644 --- a/usr.bin/tmux/resize.c +++ b/usr.bin/tmux/resize.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resize.c,v 1.46 2021/08/25 10:18:01 nicm Exp $ */ +/* $OpenBSD: resize.c,v 1.47 2021/08/27 17:15:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -87,7 +87,9 @@ ignore_client_size(struct client *c) return (1); } } - if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED)) + if ((c->flags & CLIENT_CONTROL) && + (~c->flags & CLIENT_SIZECHANGED) && + (~c->flags & CLIENT_WINDOWSIZECHANGED)) return (1); return (0); } @@ -113,23 +115,25 @@ clients_calculate_size(int type, int current, struct client *c, int, int, struct session *, struct window *), u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel) { - struct client *loop; - u_int cx, cy, n = 0; - - /* Manual windows do not have their size changed based on a client. */ - if (type == WINDOW_SIZE_MANUAL) { - log_debug("%s: type is manual", __func__); - return (0); - } + struct client *loop; + struct client_window *cw; + u_int cx, cy, n = 0; /* * Start comparing with 0 for largest and UINT_MAX for smallest or * latest. */ - if (type == WINDOW_SIZE_LARGEST) - *sx = *sy = 0; - else - *sx = *sy = UINT_MAX; + if (type == WINDOW_SIZE_LARGEST) { + *sx = 0; + *sy = 0; + } else if (type == WINDOW_SIZE_MANUAL) { + *sx = w->manual_sx; + *sy = w->manual_sy; + log_debug("%s: manual size %ux%u", __func__, *sx, *sy); + } else { + *sx = UINT_MAX; + *sy = UINT_MAX; + } *xpixel = *ypixel = 0; /* @@ -139,14 +143,18 @@ clients_calculate_size(int type, int current, struct client *c, if (type == WINDOW_SIZE_LATEST && w != NULL) n = clients_with_window(w); + /* Skip setting the size if manual */ + if (type == WINDOW_SIZE_MANUAL) + goto skip; + /* Loop over the clients and work out the size. */ TAILQ_FOREACH(loop, &clients, entry) { if (loop != c && ignore_client_size(loop)) { - log_debug("%s: ignoring %s", __func__, loop->name); + log_debug("%s: ignoring %s (1)", __func__, loop->name); continue; } if (loop != c && skip_client(loop, type, current, s, w)) { - log_debug("%s: skipping %s", __func__, loop->name); + log_debug("%s: skipping %s (1)", __func__, loop->name); continue; } @@ -160,9 +168,23 @@ clients_calculate_size(int type, int current, struct client *c, continue; } + /* + * If the client has a per-window size, use this instead if it is + * smaller. + */ + if (w != NULL) + cw = server_client_get_client_window(loop, w->id); + else + cw = NULL; + /* Work out this client's size. */ - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); + if (cw != NULL) { + cx = cw->sx; + cy = cw->sy; + } else { + cx = loop->tty.sx; + cy = loop->tty.sy - status_line_size(loop); + } /* * If it is larger or smaller than the best so far, update the @@ -191,7 +213,44 @@ clients_calculate_size(int type, int current, struct client *c, else log_debug("%s: no calculated size", __func__); +skip: + /* + * Do not allow any size to be larger than the per-client window size + * if one exists. + */ + if (w != NULL) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop != c && ignore_client_size(loop)) + continue; + if (loop != c && skip_client(loop, type, current, s, w)) + continue; + + /* Look up per-window size if any. */ + if (~loop->flags & CLIENT_WINDOWSIZECHANGED) + continue; + cw = server_client_get_client_window(loop, w->id); + if (cw == NULL) + continue; + + /* Clamp the size. */ + log_debug("%s: %s size for @%u is %ux%u", __func__, + loop->name, w->id, cw->sx, cw->sy); + if (cw->sx != 0 && *sx > cw->sx) + *sx = cw->sx; + if (cw->sy != 0 && *sy > cw->sy) + *sy = cw->sy; + } + } + if (*sx != UINT_MAX && *sy != UINT_MAX) + log_debug("%s: calculated size %ux%u", __func__, *sx, *sy); + else + log_debug("%s: no calculated size", __func__); + /* Return whether a suitable size was found. */ + if (type == WINDOW_SIZE_MANUAL) { + log_debug("%s: type is manual", __func__); + return (1); + } if (type == WINDOW_SIZE_LARGEST) { log_debug("%s: type is largest", __func__); return (*sx != 0 && *sy != 0); diff --git a/usr.bin/tmux/server-client.c b/usr.bin/tmux/server-client.c index 9f08166aee7..b65c355043e 100644 --- a/usr.bin/tmux/server-client.c +++ b/usr.bin/tmux/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.384 2021/08/22 13:48:29 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.385 2021/08/27 17:15:57 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -2443,7 +2443,7 @@ server_client_get_flags(struct client *c) } /* Get client window. */ -static struct client_window * +struct client_window * server_client_get_client_window(struct client *c, u_int id) { struct client_window cw = { .window = id }; @@ -2451,6 +2451,21 @@ server_client_get_client_window(struct client *c, u_int id) return (RB_FIND(client_windows, &c->windows, &cw)); } +/* Add client window. */ +struct client_window * +server_client_add_client_window(struct client *c, u_int id) +{ + struct client_window *cw; + + cw = server_client_get_client_window(c, id); + if (cw == NULL) { + cw = xcalloc(1, sizeof *cw); + cw->window = id; + RB_INSERT(client_windows, &c->windows, cw); + } + return cw; +} + /* Get client active pane. */ struct window_pane * server_client_get_pane(struct client *c) @@ -2479,12 +2494,7 @@ server_client_set_pane(struct client *c, struct window_pane *wp) if (s == NULL) return; - cw = server_client_get_client_window(c, s->curw->window->id); - if (cw == NULL) { - cw = xcalloc(1, sizeof *cw); - cw->window = s->curw->window->id; - RB_INSERT(client_windows, &c->windows, cw); - } + cw = server_client_add_client_window(c, s->curw->window->id); cw->pane = wp; log_debug("%s pane now %%%u", c->name, wp->id); } diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index dd1b7553cc6..ebdcb07af5c 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.854 2021/08/13 18:54:54 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.855 2021/08/27 17:15:57 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 13 2021 $ +.Dd $Mdocdate: August 27 2021 $ .Dt TMUX 1 .Os .Sh NAME @@ -1331,7 +1331,7 @@ specified multiple times. .Op Fl cDlLRSU .Op Fl A Ar pane:state .Op Fl B Ar name:what:format -.Op Fl C Ar XxY +.Op Fl C Ar size .Op Fl f Ar flags .Op Fl t Ar target-client .Op Ar adjustment @@ -1375,7 +1375,17 @@ window, changing the current window in the attached session will reset it. .Pp .Fl C -sets the width and height of a control mode client. +sets the width and height of a control mode client or of a window for a +control mode client, +.Ar size +must be one of +.Ql widthxheight +or +.Ql window ID:widthxheight , +for example +.Ql 80x24 +or +.Ql @0:80x24 . .Fl A allows a control mode client to trigger actions on a pane. The argument is a pane ID (with leading diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index a7e266091df..e402d148cd1 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1137 2021/08/25 08:51:55 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1138 2021/08/27 17:15:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -998,6 +998,8 @@ struct window { u_int sx; u_int sy; + u_int manual_sx; + u_int manual_sy; u_int xpixel; u_int ypixel; @@ -1555,6 +1557,10 @@ RB_HEAD(client_files, client_file); struct client_window { u_int window; struct window_pane *pane; + + u_int sx; + u_int sy; + RB_ENTRY(client_window) entry; }; RB_HEAD(client_windows, client_window); @@ -1650,6 +1656,7 @@ struct client { #define CLIENT_ACTIVEPANE 0x80000000ULL #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL +#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -2465,6 +2472,8 @@ void server_client_push_stderr(struct client *); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); +struct client_window *server_client_get_client_window(struct client *, u_int); +struct client_window *server_client_add_client_window(struct client *, u_int); struct window_pane *server_client_get_pane(struct client *); void server_client_set_pane(struct client *, struct window_pane *); void server_client_remove_pane(struct window_pane *); diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c index a83f1302265..4d0c9e2bcc2 100644 --- a/usr.bin/tmux/window.c +++ b/usr.bin/tmux/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.275 2021/08/20 17:36:03 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.276 2021/08/27 17:15:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -318,6 +318,8 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel) w->sx = sx; w->sy = sy; + w->manual_sx = sx; + w->manual_sy = sy; w->xpixel = xpixel; w->ypixel = ypixel;