"Fossies" - the Fresh Open Source Software Archive

Member "tmux-3.2a/resize.c" (10 Jun 2021, 10347 Bytes) of package /linux/misc/tmux-3.2a.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "resize.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.2_vs_3.2a.

    1 /* $OpenBSD$ */
    2 
    3 /*
    4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
   15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
   16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 #include <sys/types.h>
   20 
   21 #include <string.h>
   22 
   23 #include "tmux.h"
   24 
   25 void
   26 resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
   27 {
   28     int zoomed;
   29 
   30     /* Check size limits. */
   31     if (sx < WINDOW_MINIMUM)
   32         sx = WINDOW_MINIMUM;
   33     if (sx > WINDOW_MAXIMUM)
   34         sx = WINDOW_MAXIMUM;
   35     if (sy < WINDOW_MINIMUM)
   36         sy = WINDOW_MINIMUM;
   37     if (sy > WINDOW_MAXIMUM)
   38         sy = WINDOW_MAXIMUM;
   39 
   40     /* If the window is zoomed, unzoom. */
   41     zoomed = w->flags & WINDOW_ZOOMED;
   42     if (zoomed)
   43         window_unzoom(w);
   44 
   45     /* Resize the layout first. */
   46     layout_resize(w, sx, sy);
   47 
   48     /* Resize the window, it can be no smaller than the layout. */
   49     if (sx < w->layout_root->sx)
   50         sx = w->layout_root->sx;
   51     if (sy < w->layout_root->sy)
   52         sy = w->layout_root->sy;
   53     window_resize(w, sx, sy, xpixel, ypixel);
   54     log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id,
   55         sx, sy, w->layout_root->sx, w->layout_root->sy);
   56 
   57     /* Restore the window zoom state. */
   58     if (zoomed)
   59         window_zoom(w->active);
   60 
   61     tty_update_window_offset(w);
   62     server_redraw_window(w);
   63     notify_window("window-layout-changed", w);
   64     w->flags &= ~WINDOW_RESIZE;
   65 }
   66 
   67 static int
   68 ignore_client_size(struct client *c)
   69 {
   70     struct client   *loop;
   71 
   72     if (c->session == NULL)
   73         return (1);
   74     if (c->flags & CLIENT_NOSIZEFLAGS)
   75         return (1);
   76     if (c->flags & CLIENT_IGNORESIZE) {
   77         /*
   78          * Ignore flagged clients if there are any attached clients
   79          * that aren't flagged.
   80          */
   81         TAILQ_FOREACH (loop, &clients, entry) {
   82             if (loop->session == NULL)
   83                 continue;
   84             if (loop->flags & CLIENT_NOSIZEFLAGS)
   85                 continue;
   86             if (~loop->flags & CLIENT_IGNORESIZE)
   87                 return (1);
   88         }
   89     }
   90     if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
   91         return (1);
   92     return (0);
   93 }
   94 
   95 static u_int
   96 clients_with_window(struct window *w)
   97 {
   98     struct client   *loop;
   99     u_int        n = 0;
  100 
  101     TAILQ_FOREACH(loop, &clients, entry) {
  102         if (ignore_client_size(loop) || !session_has(loop->session, w))
  103             continue;
  104         if (++n > 1)
  105             break;
  106     }
  107     return (n);
  108 }
  109 
  110 static int
  111 clients_calculate_size(int type, int current, struct client *c,
  112     struct session *s, struct window *w, int (*skip_client)(struct client *,
  113     int, int, struct session *, struct window *), u_int *sx, u_int *sy,
  114     u_int *xpixel, u_int *ypixel)
  115 {
  116     struct client   *loop;
  117     u_int        cx, cy, n = 0;
  118 
  119     /* Manual windows do not have their size changed based on a client. */
  120     if (type == WINDOW_SIZE_MANUAL) {
  121         log_debug("%s: type is manual", __func__);
  122         return (0);
  123     }
  124 
  125     /*
  126      * Start comparing with 0 for largest and UINT_MAX for smallest or
  127      * latest.
  128      */
  129     if (type == WINDOW_SIZE_LARGEST)
  130         *sx = *sy = 0;
  131     else
  132         *sx = *sy = UINT_MAX;
  133     *xpixel = *ypixel = 0;
  134 
  135     /*
  136      * For latest, count the number of clients with this window. We only
  137      * care if there is more than one.
  138      */
  139     if (type == WINDOW_SIZE_LATEST && w != NULL)
  140         n = clients_with_window(w);
  141 
  142     /* Loop over the clients and work out the size. */
  143     TAILQ_FOREACH(loop, &clients, entry) {
  144         if (loop != c && ignore_client_size(loop)) {
  145             log_debug("%s: ignoring %s", __func__, loop->name);
  146             continue;
  147         }
  148         if (loop != c && skip_client(loop, type, current, s, w)) {
  149             log_debug("%s: skipping %s", __func__, loop->name);
  150             continue;
  151         }
  152 
  153         /*
  154          * If there are multiple clients attached, only accept the
  155          * latest client; otherwise let the only client be chosen as
  156          * for smallest.
  157          */
  158         if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) {
  159             log_debug("%s: %s is not latest", __func__, loop->name);
  160             continue;
  161         }
  162 
  163         /* Work out this client's size. */
  164         cx = loop->tty.sx;
  165         cy = loop->tty.sy - status_line_size(loop);
  166 
  167         /*
  168          * If it is larger or smaller than the best so far, update the
  169          * new size.
  170          */
  171         if (type == WINDOW_SIZE_LARGEST) {
  172             if (cx > *sx)
  173                 *sx = cx;
  174             if (cy > *sy)
  175                 *sy = cy;
  176         } else {
  177             if (cx < *sx)
  178                 *sx = cx;
  179             if (cy < *sy)
  180                 *sy = cy;
  181         }
  182         if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) {
  183             *xpixel = loop->tty.xpixel;
  184             *ypixel = loop->tty.ypixel;
  185         }
  186         log_debug("%s: after %s (%ux%u), size is %ux%u", __func__,
  187             loop->name, cx, cy, *sx, *sy);
  188     }
  189 
  190     /* Return whether a suitable size was found. */
  191     if (type == WINDOW_SIZE_LARGEST) {
  192         log_debug("%s: type is largest", __func__);
  193         return (*sx != 0 && *sy != 0);
  194     }
  195     if (type == WINDOW_SIZE_LATEST)
  196         log_debug("%s: type is latest", __func__);
  197     else
  198         log_debug("%s: type is smallest", __func__);
  199     return (*sx != UINT_MAX && *sy != UINT_MAX);
  200 }
  201 
  202 static int
  203 default_window_size_skip_client(struct client *loop, int type,
  204     __unused int current, struct session *s, struct window *w)
  205 {
  206     /*
  207      * Latest checks separately, so do not check here. Otherwise only
  208      * include clients where the session contains the window or where the
  209      * session is the given session.
  210      */
  211     if (type == WINDOW_SIZE_LATEST)
  212         return (0);
  213     if (w != NULL && !session_has(loop->session, w))
  214         return (1);
  215     if (w == NULL && loop->session != s)
  216         return (1);
  217     return (0);
  218 }
  219 
  220 void
  221 default_window_size(struct client *c, struct session *s, struct window *w,
  222     u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
  223 {
  224     const char  *value;
  225 
  226     /* Get type if not provided. */
  227     if (type == -1)
  228         type = options_get_number(global_w_options, "window-size");
  229 
  230     /*
  231      * Latest clients can use the given client if suitable. If there is no
  232      * client and no window, use the default size as for manual type.
  233      */
  234     if (type == WINDOW_SIZE_LATEST) {
  235         if (c != NULL && !ignore_client_size(c)) {
  236             *sx = c->tty.sx;
  237             *sy = c->tty.sy - status_line_size(c);
  238             *xpixel = c->tty.xpixel;
  239             *ypixel = c->tty.ypixel;
  240             log_debug("%s: using %ux%u from %s", __func__, *sx, *sy,
  241                 c->name);
  242             goto done;
  243         }
  244     }
  245 
  246     /*
  247      * Look for a client to base the size on. If none exists (or the type
  248      * is manual), use the default-size option.
  249      */
  250     if (!clients_calculate_size(type, 0, c, s, w,
  251         default_window_size_skip_client, sx, sy, xpixel, ypixel)) {
  252         value = options_get_string(s->options, "default-size");
  253         if (sscanf(value, "%ux%u", sx, sy) != 2) {
  254             *sx = 80;
  255             *sy = 24;
  256         }
  257         log_debug("%s: using %ux%u from default-size", __func__, *sx,
  258             *sy);
  259     }
  260 
  261 done:
  262     /* Make sure the limits are enforced. */
  263     if (*sx < WINDOW_MINIMUM)
  264         *sx = WINDOW_MINIMUM;
  265     if (*sx > WINDOW_MAXIMUM)
  266         *sx = WINDOW_MAXIMUM;
  267     if (*sy < WINDOW_MINIMUM)
  268         *sy = WINDOW_MINIMUM;
  269     if (*sy > WINDOW_MAXIMUM)
  270         *sy = WINDOW_MAXIMUM;
  271     log_debug("%s: resulting size is %ux%u", __func__, *sx, *sy);
  272 }
  273 
  274 static int
  275 recalculate_size_skip_client(struct client *loop, __unused int type,
  276     int current, __unused struct session *s, struct window *w)
  277 {
  278     /*
  279      * If the current flag is set, then skip any client where this window
  280      * is not the current window - this is used for aggressive-resize.
  281      * Otherwise skip any session that doesn't contain the window.
  282      */
  283     if (current)
  284         return (loop->session->curw->window != w);
  285     return (session_has(loop->session, w) == 0);
  286 }
  287 
  288 void
  289 recalculate_size(struct window *w, int now)
  290 {
  291     u_int   sx, sy, xpixel = 0, ypixel = 0;
  292     int type, current, changed;
  293 
  294     /*
  295      * Do not attempt to resize windows which have no pane, they must be on
  296      * the way to destruction.
  297      */
  298     if (w->active == NULL)
  299         return;
  300     log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
  301 
  302     /*
  303      * Type is manual, smallest, largest, latest. Current is the
  304      * aggressive-resize option (do not resize based on clients where the
  305      * window is not the current window).
  306      */
  307     type = options_get_number(w->options, "window-size");
  308     current = options_get_number(w->options, "aggressive-resize");
  309 
  310     /* Look for a suitable client and get the new size. */
  311     changed = clients_calculate_size(type, current, NULL, NULL, w,
  312         recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel);
  313 
  314     /*
  315      * Make sure the size has actually changed. If the window has already
  316      * got a resize scheduled, then use the new size; otherwise the old.
  317      */
  318     if (w->flags & WINDOW_RESIZE) {
  319         if (!now && changed && w->new_sx == sx && w->new_sy == sy)
  320             changed = 0;
  321     } else {
  322         if (!now && changed && w->sx == sx && w->sy == sy)
  323             changed = 0;
  324     }
  325 
  326     /*
  327      * If the size hasn't changed, update the window offset but not the
  328      * size.
  329      */
  330     if (!changed) {
  331         tty_update_window_offset(w);
  332         return;
  333     }
  334 
  335     /*
  336      * If the now flag is set or if the window is sized manually, change
  337      * the size immediately. Otherwise set the flag and it will be done
  338      * later.
  339      */
  340     log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
  341     if (now || type == WINDOW_SIZE_MANUAL)
  342         resize_window(w, sx, sy, xpixel, ypixel);
  343     else {
  344         w->new_sx = sx;
  345         w->new_sy = sy;
  346         w->new_xpixel = xpixel;
  347         w->new_ypixel = ypixel;
  348 
  349         w->flags |= WINDOW_RESIZE;
  350         tty_update_window_offset(w);
  351     }
  352 }
  353 
  354 void
  355 recalculate_sizes(void)
  356 {
  357     recalculate_sizes_now(0);
  358 }
  359 
  360 void
  361 recalculate_sizes_now(int now)
  362 {
  363     struct session  *s;
  364     struct client   *c;
  365     struct window   *w;
  366 
  367     /*
  368      * Clear attached count and update saved status line information for
  369      * each session.
  370      */
  371     RB_FOREACH(s, sessions, &sessions) {
  372         s->attached = 0;
  373         status_update_cache(s);
  374     }
  375 
  376     /*
  377      * Increment attached count and check the status line size for each
  378      * client.
  379      */
  380     TAILQ_FOREACH(c, &clients, entry) {
  381         s = c->session;
  382         if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS))
  383             s->attached++;
  384         if (ignore_client_size(c))
  385             continue;
  386         if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL))
  387             c->flags |= CLIENT_STATUSOFF;
  388         else
  389             c->flags &= ~CLIENT_STATUSOFF;
  390     }
  391 
  392     /* Walk each window and adjust the size. */
  393     RB_FOREACH(w, windows, &windows)
  394         recalculate_size(w, now);
  395 }