Allow control mode clients to set a hard limit on the window width and
authornicm <nicm@openbsd.org>
Fri, 27 Aug 2021 17:15:57 +0000 (17:15 +0000)
committernicm <nicm@openbsd.org>
Fri, 27 Aug 2021 17:15:57 +0000 (17:15 +0000)
height, GitHub issue 2594.

usr.bin/tmux/cmd-refresh-client.c
usr.bin/tmux/cmd-resize-window.c
usr.bin/tmux/resize.c
usr.bin/tmux/server-client.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h
usr.bin/tmux/window.c

index 5520d56..dada29c 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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')) {
index ec523fb..980bb35 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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);
 }
index 45779f9..99df95e 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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);
index 9f08166..b65c355 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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);
 }
index dd1b755..ebdcb07 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
 .\"
@@ -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
index a7e2660..e402d14 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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 *);
index a83f130..4d0c9e2 100644 (file)
@@ -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 <nicholas.marriott@gmail.com>
@@ -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;