"Fossies" - the Fresh Open Source Software Archive

Member "tmux-3.2a/screen.c" (10 Jun 2021, 13916 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 "screen.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 <stdlib.h>
   22 #include <string.h>
   23 #include <unistd.h>
   24 
   25 #include "tmux.h"
   26 
   27 /* Selected area in screen. */
   28 struct screen_sel {
   29     int      hidden;
   30     int      rectangle;
   31     int      modekeys;
   32 
   33     u_int        sx;
   34     u_int        sy;
   35 
   36     u_int        ex;
   37     u_int        ey;
   38 
   39     struct grid_cell cell;
   40 };
   41 
   42 /* Entry on title stack. */
   43 struct screen_title_entry {
   44     char                *text;
   45 
   46     TAILQ_ENTRY(screen_title_entry)  entry;
   47 };
   48 TAILQ_HEAD(screen_titles, screen_title_entry);
   49 
   50 static void screen_resize_y(struct screen *, u_int, int, u_int *);
   51 static void screen_reflow(struct screen *, u_int, u_int *, u_int *, int);
   52 
   53 /* Free titles stack. */
   54 static void
   55 screen_free_titles(struct screen *s)
   56 {
   57     struct screen_title_entry   *title_entry;
   58 
   59     if (s->titles == NULL)
   60         return;
   61 
   62     while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) {
   63         TAILQ_REMOVE(s->titles, title_entry, entry);
   64         free(title_entry->text);
   65         free(title_entry);
   66     }
   67 
   68     free(s->titles);
   69     s->titles = NULL;
   70 }
   71 
   72 /* Create a new screen. */
   73 void
   74 screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
   75 {
   76     s->grid = grid_create(sx, sy, hlimit);
   77     s->saved_grid = NULL;
   78 
   79     s->title = xstrdup("");
   80     s->titles = NULL;
   81     s->path = NULL;
   82 
   83     s->cstyle = 0;
   84     s->ccolour = xstrdup("");
   85     s->tabs = NULL;
   86     s->sel = NULL;
   87 
   88     s->write_list = NULL;
   89 
   90     screen_reinit(s);
   91 }
   92 
   93 /* Reinitialise screen. */
   94 void
   95 screen_reinit(struct screen *s)
   96 {
   97     s->cx = 0;
   98     s->cy = 0;
   99 
  100     s->rupper = 0;
  101     s->rlower = screen_size_y(s) - 1;
  102 
  103     s->mode = MODE_CURSOR|MODE_WRAP;
  104     if (options_get_number(global_options, "extended-keys") == 2)
  105         s->mode |= MODE_KEXTENDED;
  106 
  107     if (s->saved_grid != NULL)
  108         screen_alternate_off(s, NULL, 0);
  109     s->saved_cx = UINT_MAX;
  110     s->saved_cy = UINT_MAX;
  111 
  112     screen_reset_tabs(s);
  113 
  114     grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8);
  115 
  116     screen_clear_selection(s);
  117     screen_free_titles(s);
  118 }
  119 
  120 /* Destroy a screen. */
  121 void
  122 screen_free(struct screen *s)
  123 {
  124     free(s->sel);
  125     free(s->tabs);
  126     free(s->path);
  127     free(s->title);
  128     free(s->ccolour);
  129 
  130     if (s->write_list != NULL)
  131         screen_write_free_list(s);
  132 
  133     if (s->saved_grid != NULL)
  134         grid_destroy(s->saved_grid);
  135     grid_destroy(s->grid);
  136 
  137     screen_free_titles(s);
  138 }
  139 
  140 /* Reset tabs to default, eight spaces apart. */
  141 void
  142 screen_reset_tabs(struct screen *s)
  143 {
  144     u_int   i;
  145 
  146     free(s->tabs);
  147 
  148     if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
  149         fatal("bit_alloc failed");
  150     for (i = 8; i < screen_size_x(s); i += 8)
  151         bit_set(s->tabs, i);
  152 }
  153 
  154 /* Set screen cursor style. */
  155 void
  156 screen_set_cursor_style(struct screen *s, u_int style)
  157 {
  158     if (style <= 6) {
  159         s->cstyle = style;
  160         s->mode &= ~MODE_BLINKING;
  161     }
  162 }
  163 
  164 /* Set screen cursor colour. */
  165 void
  166 screen_set_cursor_colour(struct screen *s, const char *colour)
  167 {
  168     free(s->ccolour);
  169     s->ccolour = xstrdup(colour);
  170 }
  171 
  172 /* Set screen title. */
  173 int
  174 screen_set_title(struct screen *s, const char *title)
  175 {
  176     if (!utf8_isvalid(title))
  177         return (0);
  178     free(s->title);
  179     s->title = xstrdup(title);
  180     return (1);
  181 }
  182 
  183 /* Set screen path. */
  184 void
  185 screen_set_path(struct screen *s, const char *path)
  186 {
  187     free(s->path);
  188     utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
  189 }
  190 
  191 /* Push the current title onto the stack. */
  192 void
  193 screen_push_title(struct screen *s)
  194 {
  195     struct screen_title_entry *title_entry;
  196 
  197     if (s->titles == NULL) {
  198         s->titles = xmalloc(sizeof *s->titles);
  199         TAILQ_INIT(s->titles);
  200     }
  201     title_entry = xmalloc(sizeof *title_entry);
  202     title_entry->text = xstrdup(s->title);
  203     TAILQ_INSERT_HEAD(s->titles, title_entry, entry);
  204 }
  205 
  206 /*
  207  * Pop a title from the stack and set it as the screen title. If the stack is
  208  * empty, do nothing.
  209  */
  210 void
  211 screen_pop_title(struct screen *s)
  212 {
  213     struct screen_title_entry *title_entry;
  214 
  215     if (s->titles == NULL)
  216         return;
  217 
  218     title_entry = TAILQ_FIRST(s->titles);
  219     if (title_entry != NULL) {
  220         screen_set_title(s, title_entry->text);
  221 
  222         TAILQ_REMOVE(s->titles, title_entry, entry);
  223         free(title_entry->text);
  224         free(title_entry);
  225     }
  226 }
  227 
  228 /* Resize screen with options. */
  229 void
  230 screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
  231     int eat_empty, int cursor)
  232 {
  233     u_int   cx = s->cx, cy = s->grid->hsize + s->cy;
  234 
  235     if (s->write_list != NULL)
  236         screen_write_free_list(s);
  237 
  238     log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)",
  239         __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy,
  240         cx, cy);
  241 
  242     if (sx < 1)
  243         sx = 1;
  244     if (sy < 1)
  245         sy = 1;
  246 
  247     if (sx != screen_size_x(s)) {
  248         s->grid->sx = sx;
  249         screen_reset_tabs(s);
  250     } else
  251         reflow = 0;
  252 
  253     if (sy != screen_size_y(s))
  254         screen_resize_y(s, sy, eat_empty, &cy);
  255 
  256     if (reflow)
  257         screen_reflow(s, sx, &cx, &cy, cursor);
  258 
  259     if (cy >= s->grid->hsize) {
  260         s->cx = cx;
  261         s->cy = cy - s->grid->hsize;
  262     } else {
  263         s->cx = 0;
  264         s->cy = 0;
  265     }
  266 
  267     log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx,
  268         s->cy, cx, cy);
  269 
  270     if (s->write_list != NULL)
  271         screen_write_make_list(s);
  272 }
  273 
  274 /* Resize screen. */
  275 void
  276 screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
  277 {
  278     screen_resize_cursor(s, sx, sy, reflow, 1, 1);
  279 }
  280 
  281 static void
  282 screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy)
  283 {
  284     struct grid *gd = s->grid;
  285     u_int        needed, available, oldy, i;
  286 
  287     if (sy == 0)
  288         fatalx("zero size");
  289     oldy = screen_size_y(s);
  290 
  291     /*
  292      * When resizing:
  293      *
  294      * If the height is decreasing, delete lines from the bottom until
  295      * hitting the cursor, then push lines from the top into the history.
  296      *
  297      * When increasing, pull as many lines as possible from scrolled
  298      * history (not explicitly cleared from view) to the top, then fill the
  299      * remaining with blanks at the bottom.
  300      */
  301 
  302     /* Size decreasing. */
  303     if (sy < oldy) {
  304         needed = oldy - sy;
  305 
  306         /* Delete as many lines as possible from the bottom. */
  307         if (eat_empty) {
  308             available = oldy - 1 - s->cy;
  309             if (available > 0) {
  310                 if (available > needed)
  311                     available = needed;
  312                 grid_view_delete_lines(gd, oldy - available,
  313                     available, 8);
  314             }
  315             needed -= available;
  316         }
  317 
  318         /*
  319          * Now just increase the history size, if possible, to take
  320          * over the lines which are left. If history is off, delete
  321          * lines from the top.
  322          */
  323         available = s->cy;
  324         if (gd->flags & GRID_HISTORY) {
  325             gd->hscrolled += needed;
  326             gd->hsize += needed;
  327         } else if (needed > 0 && available > 0) {
  328             if (available > needed)
  329                 available = needed;
  330             grid_view_delete_lines(gd, 0, available, 8);
  331             (*cy) -= available;
  332         }
  333     }
  334 
  335     /* Resize line array. */
  336     grid_adjust_lines(gd, gd->hsize + sy);
  337 
  338     /* Size increasing. */
  339     if (sy > oldy) {
  340         needed = sy - oldy;
  341 
  342         /*
  343          * Try to pull as much as possible out of scrolled history, if
  344          * is is enabled.
  345          */
  346         available = gd->hscrolled;
  347         if (gd->flags & GRID_HISTORY && available > 0) {
  348             if (available > needed)
  349                 available = needed;
  350             gd->hscrolled -= available;
  351             gd->hsize -= available;
  352         } else
  353             available = 0;
  354         needed -= available;
  355 
  356         /* Then fill the rest in with blanks. */
  357         for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
  358             grid_empty_line(gd, i, 8);
  359     }
  360 
  361     /* Set the new size, and reset the scroll region. */
  362     gd->sy = sy;
  363     s->rupper = 0;
  364     s->rlower = screen_size_y(s) - 1;
  365 }
  366 
  367 /* Set selection. */
  368 void
  369 screen_set_selection(struct screen *s, u_int sx, u_int sy,
  370     u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc)
  371 {
  372     if (s->sel == NULL)
  373         s->sel = xcalloc(1, sizeof *s->sel);
  374 
  375     memcpy(&s->sel->cell, gc, sizeof s->sel->cell);
  376     s->sel->hidden = 0;
  377     s->sel->rectangle = rectangle;
  378     s->sel->modekeys = modekeys;
  379 
  380     s->sel->sx = sx;
  381     s->sel->sy = sy;
  382     s->sel->ex = ex;
  383     s->sel->ey = ey;
  384 }
  385 
  386 /* Clear selection. */
  387 void
  388 screen_clear_selection(struct screen *s)
  389 {
  390     free(s->sel);
  391     s->sel = NULL;
  392 }
  393 
  394 /* Hide selection. */
  395 void
  396 screen_hide_selection(struct screen *s)
  397 {
  398     if (s->sel != NULL)
  399         s->sel->hidden = 1;
  400 }
  401 
  402 /* Check if cell in selection. */
  403 int
  404 screen_check_selection(struct screen *s, u_int px, u_int py)
  405 {
  406     struct screen_sel   *sel = s->sel;
  407     u_int            xx;
  408 
  409     if (sel == NULL || sel->hidden)
  410         return (0);
  411 
  412     if (sel->rectangle) {
  413         if (sel->sy < sel->ey) {
  414             /* start line < end line -- downward selection. */
  415             if (py < sel->sy || py > sel->ey)
  416                 return (0);
  417         } else if (sel->sy > sel->ey) {
  418             /* start line > end line -- upward selection. */
  419             if (py > sel->sy || py < sel->ey)
  420                 return (0);
  421         } else {
  422             /* starting line == ending line. */
  423             if (py != sel->sy)
  424                 return (0);
  425         }
  426 
  427         /*
  428          * Need to include the selection start row, but not the cursor
  429          * row, which means the selection changes depending on which
  430          * one is on the left.
  431          */
  432         if (sel->ex < sel->sx) {
  433             /* Cursor (ex) is on the left. */
  434             if (px < sel->ex)
  435                 return (0);
  436 
  437             if (px > sel->sx)
  438                 return (0);
  439         } else {
  440             /* Selection start (sx) is on the left. */
  441             if (px < sel->sx)
  442                 return (0);
  443 
  444             if (px > sel->ex)
  445                 return (0);
  446         }
  447     } else {
  448         /*
  449          * Like emacs, keep the top-left-most character, and drop the
  450          * bottom-right-most, regardless of copy direction.
  451          */
  452         if (sel->sy < sel->ey) {
  453             /* starting line < ending line -- downward selection. */
  454             if (py < sel->sy || py > sel->ey)
  455                 return (0);
  456 
  457             if (py == sel->sy && px < sel->sx)
  458                 return (0);
  459 
  460             if (sel->modekeys == MODEKEY_EMACS)
  461                 xx = (sel->ex == 0 ? 0 : sel->ex - 1);
  462             else
  463                 xx = sel->ex;
  464             if (py == sel->ey && px > xx)
  465                 return (0);
  466         } else if (sel->sy > sel->ey) {
  467             /* starting line > ending line -- upward selection. */
  468             if (py > sel->sy || py < sel->ey)
  469                 return (0);
  470 
  471             if (py == sel->ey && px < sel->ex)
  472                 return (0);
  473 
  474             if (sel->modekeys == MODEKEY_EMACS)
  475                 xx = sel->sx - 1;
  476             else
  477                 xx = sel->sx;
  478             if (py == sel->sy && (sel->sx == 0 || px > xx))
  479                 return (0);
  480         } else {
  481             /* starting line == ending line. */
  482             if (py != sel->sy)
  483                 return (0);
  484 
  485             if (sel->ex < sel->sx) {
  486                 /* cursor (ex) is on the left */
  487                 if (sel->modekeys == MODEKEY_EMACS)
  488                     xx = sel->sx - 1;
  489                 else
  490                     xx = sel->sx;
  491                 if (px > xx || px < sel->ex)
  492                     return (0);
  493             } else {
  494                 /* selection start (sx) is on the left */
  495                 if (sel->modekeys == MODEKEY_EMACS)
  496                     xx = (sel->ex == 0 ? 0 : sel->ex - 1);
  497                 else
  498                     xx = sel->ex;
  499                 if (px < sel->sx || px > xx)
  500                     return (0);
  501             }
  502         }
  503     }
  504 
  505     return (1);
  506 }
  507 
  508 /* Get selected grid cell. */
  509 void
  510 screen_select_cell(struct screen *s, struct grid_cell *dst,
  511     const struct grid_cell *src)
  512 {
  513     if (s->sel == NULL || s->sel->hidden)
  514         return;
  515 
  516     memcpy(dst, &s->sel->cell, sizeof *dst);
  517 
  518     utf8_copy(&dst->data, &src->data);
  519     dst->attr = dst->attr & ~GRID_ATTR_CHARSET;
  520     dst->attr |= src->attr & GRID_ATTR_CHARSET;
  521     dst->flags = src->flags;
  522 }
  523 
  524 /* Reflow wrapped lines. */
  525 static void
  526 screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor)
  527 {
  528     u_int   wx, wy;
  529 
  530     if (cursor) {
  531         grid_wrap_position(s->grid, *cx, *cy, &wx, &wy);
  532         log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx,
  533             wy);
  534     }
  535 
  536     grid_reflow(s->grid, new_x);
  537 
  538     if (cursor) {
  539         grid_unwrap_position(s->grid, cx, cy, wx, wy);
  540         log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy);
  541     }
  542     else {
  543         *cx = 0;
  544         *cy = s->grid->hsize;
  545     }
  546 }
  547 
  548 /*
  549  * Enter alternative screen mode. A copy of the visible screen is saved and the
  550  * history is not updated.
  551  */
  552 void
  553 screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
  554 {
  555     u_int   sx, sy;
  556 
  557     if (s->saved_grid != NULL)
  558         return;
  559     sx = screen_size_x(s);
  560     sy = screen_size_y(s);
  561 
  562     s->saved_grid = grid_create(sx, sy, 0);
  563     grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy);
  564     if (cursor) {
  565         s->saved_cx = s->cx;
  566         s->saved_cy = s->cy;
  567     }
  568     memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
  569 
  570     grid_view_clear(s->grid, 0, 0, sx, sy, 8);
  571 
  572     s->saved_flags = s->grid->flags;
  573     s->grid->flags &= ~GRID_HISTORY;
  574 }
  575 
  576 /* Exit alternate screen mode and restore the copied grid. */
  577 void
  578 screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
  579 {
  580     u_int   sx = screen_size_x(s), sy = screen_size_y(s);
  581 
  582     /*
  583      * If the current size is different, temporarily resize to the old size
  584      * before copying back.
  585      */
  586     if (s->saved_grid != NULL)
  587         screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1);
  588 
  589     /*
  590      * Restore the cursor position and cell. This happens even if not
  591      * currently in the alternate screen.
  592      */
  593     if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) {
  594         s->cx = s->saved_cx;
  595         s->cy = s->saved_cy;
  596         if (gc != NULL)
  597             memcpy(gc, &s->saved_cell, sizeof *gc);
  598     }
  599 
  600     /* If not in the alternate screen, do nothing more. */
  601     if (s->saved_grid == NULL) {
  602         if (s->cx > screen_size_x(s) - 1)
  603             s->cx = screen_size_x(s) - 1;
  604         if (s->cy > screen_size_y(s) - 1)
  605             s->cy = screen_size_y(s) - 1;
  606         return;
  607     }
  608 
  609     /* Restore the saved grid. */
  610     grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
  611         s->saved_grid->sy);
  612 
  613     /*
  614      * Turn history back on (so resize can use it) and then resize back to
  615      * the current size.
  616      */
  617     if (s->saved_flags & GRID_HISTORY)
  618         s->grid->flags |= GRID_HISTORY;
  619     screen_resize(s, sx, sy, 1);
  620 
  621     grid_destroy(s->saved_grid);
  622     s->saved_grid = NULL;
  623 
  624     if (s->cx > screen_size_x(s) - 1)
  625         s->cx = screen_size_x(s) - 1;
  626     if (s->cy > screen_size_y(s) - 1)
  627         s->cy = screen_size_y(s) - 1;
  628 }