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
screen_free_titles(struct screen * s)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
screen_init(struct screen * s,u_int sx,u_int sy,u_int hlimit)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 = SCREEN_CURSOR_DEFAULT;
84           s->default_cstyle = SCREEN_CURSOR_DEFAULT;
85           s->mode = MODE_CURSOR;
86           s->default_mode = 0;
87           s->ccolour = -1;
88           s->default_ccolour = -1;
89           s->tabs = NULL;
90           s->sel = NULL;
91 
92 #ifdef ENABLE_SIXEL
93           TAILQ_INIT(&s->images);
94 #endif
95 
96           s->write_list = NULL;
97           s->hyperlinks = NULL;
98 
99           screen_reinit(s);
100 }
101 
102 /* Reinitialise screen. */
103 void
screen_reinit(struct screen * s)104 screen_reinit(struct screen *s)
105 {
106           s->cx = 0;
107           s->cy = 0;
108 
109           s->rupper = 0;
110           s->rlower = screen_size_y(s) - 1;
111 
112           s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF);
113 
114           if (options_get_number(global_options, "extended-keys") == 2)
115                     s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
116 
117           if (s->saved_grid != NULL)
118                     screen_alternate_off(s, NULL, 0);
119           s->saved_cx = UINT_MAX;
120           s->saved_cy = UINT_MAX;
121 
122           screen_reset_tabs(s);
123 
124           grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8);
125 
126           screen_clear_selection(s);
127           screen_free_titles(s);
128 
129 #ifdef ENABLE_SIXEL
130           image_free_all(s);
131 #endif
132 
133           screen_reset_hyperlinks(s);
134 }
135 
136 /* Reset hyperlinks of a screen. */
137 void
screen_reset_hyperlinks(struct screen * s)138 screen_reset_hyperlinks(struct screen *s)
139 {
140           if (s->hyperlinks == NULL)
141                     s->hyperlinks = hyperlinks_init();
142           else
143                     hyperlinks_reset(s->hyperlinks);
144 }
145 
146 /* Destroy a screen. */
147 void
screen_free(struct screen * s)148 screen_free(struct screen *s)
149 {
150           free(s->sel);
151           free(s->tabs);
152           free(s->path);
153           free(s->title);
154 
155           if (s->write_list != NULL)
156                     screen_write_free_list(s);
157 
158           if (s->saved_grid != NULL)
159                     grid_destroy(s->saved_grid);
160           grid_destroy(s->grid);
161 
162           if (s->hyperlinks != NULL)
163                     hyperlinks_free(s->hyperlinks);
164           screen_free_titles(s);
165 
166 #ifdef ENABLE_SIXEL
167           image_free_all(s);
168 #endif
169 }
170 
171 /* Reset tabs to default, eight spaces apart. */
172 void
screen_reset_tabs(struct screen * s)173 screen_reset_tabs(struct screen *s)
174 {
175           u_int     i;
176 
177           free(s->tabs);
178 
179           if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
180                     fatal("bit_alloc failed");
181           for (i = 8; i < screen_size_x(s); i += 8)
182                     bit_set(s->tabs, i);
183 }
184 
185 /* Set screen cursor style and mode. */
186 void
screen_set_cursor_style(u_int style,enum screen_cursor_style * cstyle,int * mode)187 screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle,
188     int *mode)
189 {
190           switch (style) {
191           case 0:
192                     *cstyle = SCREEN_CURSOR_DEFAULT;
193                     break;
194           case 1:
195                     *cstyle = SCREEN_CURSOR_BLOCK;
196                     *mode |= MODE_CURSOR_BLINKING;
197                     break;
198           case 2:
199                     *cstyle = SCREEN_CURSOR_BLOCK;
200                     *mode &= ~MODE_CURSOR_BLINKING;
201                     break;
202           case 3:
203                     *cstyle = SCREEN_CURSOR_UNDERLINE;
204                     *mode |= MODE_CURSOR_BLINKING;
205                     break;
206           case 4:
207                     *cstyle = SCREEN_CURSOR_UNDERLINE;
208                     *mode &= ~MODE_CURSOR_BLINKING;
209                     break;
210           case 5:
211                     *cstyle = SCREEN_CURSOR_BAR;
212                     *mode |= MODE_CURSOR_BLINKING;
213                     break;
214           case 6:
215                     *cstyle = SCREEN_CURSOR_BAR;
216                     *mode &= ~MODE_CURSOR_BLINKING;
217                     break;
218           }
219 }
220 
221 /* Set screen cursor colour. */
222 void
screen_set_cursor_colour(struct screen * s,int colour)223 screen_set_cursor_colour(struct screen *s, int colour)
224 {
225           s->ccolour = colour;
226 }
227 
228 /* Set screen title. */
229 int
screen_set_title(struct screen * s,const char * title)230 screen_set_title(struct screen *s, const char *title)
231 {
232           if (!utf8_isvalid(title))
233                     return (0);
234           free(s->title);
235           s->title = xstrdup(title);
236           return (1);
237 }
238 
239 /* Set screen path. */
240 void
screen_set_path(struct screen * s,const char * path)241 screen_set_path(struct screen *s, const char *path)
242 {
243           free(s->path);
244           utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
245 }
246 
247 /* Push the current title onto the stack. */
248 void
screen_push_title(struct screen * s)249 screen_push_title(struct screen *s)
250 {
251           struct screen_title_entry *title_entry;
252 
253           if (s->titles == NULL) {
254                     s->titles = xmalloc(sizeof *s->titles);
255                     TAILQ_INIT(s->titles);
256           }
257           title_entry = xmalloc(sizeof *title_entry);
258           title_entry->text = xstrdup(s->title);
259           TAILQ_INSERT_HEAD(s->titles, title_entry, entry);
260 }
261 
262 /*
263  * Pop a title from the stack and set it as the screen title. If the stack is
264  * empty, do nothing.
265  */
266 void
screen_pop_title(struct screen * s)267 screen_pop_title(struct screen *s)
268 {
269           struct screen_title_entry *title_entry;
270 
271           if (s->titles == NULL)
272                     return;
273 
274           title_entry = TAILQ_FIRST(s->titles);
275           if (title_entry != NULL) {
276                     screen_set_title(s, title_entry->text);
277 
278                     TAILQ_REMOVE(s->titles, title_entry, entry);
279                     free(title_entry->text);
280                     free(title_entry);
281           }
282 }
283 
284 /* Resize screen with options. */
285 void
screen_resize_cursor(struct screen * s,u_int sx,u_int sy,int reflow,int eat_empty,int cursor)286 screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
287     int eat_empty, int cursor)
288 {
289           u_int     cx = s->cx, cy = s->grid->hsize + s->cy;
290 
291           if (s->write_list != NULL)
292                     screen_write_free_list(s);
293 
294           log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)",
295               __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy,
296               cx, cy);
297 
298           if (sx < 1)
299                     sx = 1;
300           if (sy < 1)
301                     sy = 1;
302 
303           if (sx != screen_size_x(s)) {
304                     s->grid->sx = sx;
305                     screen_reset_tabs(s);
306           } else
307                     reflow = 0;
308 
309           if (sy != screen_size_y(s))
310                     screen_resize_y(s, sy, eat_empty, &cy);
311 
312 #ifdef ENABLE_SIXEL
313           image_free_all(s);
314 #endif
315 
316           if (reflow)
317                     screen_reflow(s, sx, &cx, &cy, cursor);
318 
319           if (cy >= s->grid->hsize) {
320                     s->cx = cx;
321                     s->cy = cy - s->grid->hsize;
322           } else {
323                     s->cx = 0;
324                     s->cy = 0;
325           }
326 
327           log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx,
328               s->cy, cx, cy);
329 
330           if (s->write_list != NULL)
331                     screen_write_make_list(s);
332 }
333 
334 /* Resize screen. */
335 void
screen_resize(struct screen * s,u_int sx,u_int sy,int reflow)336 screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
337 {
338           screen_resize_cursor(s, sx, sy, reflow, 1, 1);
339 }
340 
341 static void
screen_resize_y(struct screen * s,u_int sy,int eat_empty,u_int * cy)342 screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy)
343 {
344           struct grid         *gd = s->grid;
345           u_int                needed, available, oldy, i;
346 
347           if (sy == 0)
348                     fatalx("zero size");
349           oldy = screen_size_y(s);
350 
351           /*
352            * When resizing:
353            *
354            * If the height is decreasing, delete lines from the bottom until
355            * hitting the cursor, then push lines from the top into the history.
356            *
357            * When increasing, pull as many lines as possible from scrolled
358            * history (not explicitly cleared from view) to the top, then fill the
359            * remaining with blanks at the bottom.
360            */
361 
362           /* Size decreasing. */
363           if (sy < oldy) {
364                     needed = oldy - sy;
365 
366                     /* Delete as many lines as possible from the bottom. */
367                     if (eat_empty) {
368                               available = oldy - 1 - s->cy;
369                               if (available > 0) {
370                                         if (available > needed)
371                                                   available = needed;
372                                         grid_view_delete_lines(gd, oldy - available,
373                                             available, 8);
374                               }
375                               needed -= available;
376                     }
377 
378                     /*
379                      * Now just increase the history size, if possible, to take
380                      * over the lines which are left. If history is off, delete
381                      * lines from the top.
382                      */
383                     available = s->cy;
384                     if (gd->flags & GRID_HISTORY) {
385                               gd->hscrolled += needed;
386                               gd->hsize += needed;
387                     } else if (needed > 0 && available > 0) {
388                               if (available > needed)
389                                         available = needed;
390                               grid_view_delete_lines(gd, 0, available, 8);
391                               (*cy) -= available;
392                     }
393           }
394 
395           /* Resize line array. */
396           grid_adjust_lines(gd, gd->hsize + sy);
397 
398           /* Size increasing. */
399           if (sy > oldy) {
400                     needed = sy - oldy;
401 
402                     /*
403                      * Try to pull as much as possible out of scrolled history, if
404                      * it is enabled.
405                      */
406                     available = gd->hscrolled;
407                     if (gd->flags & GRID_HISTORY && available > 0) {
408                               if (available > needed)
409                                         available = needed;
410                               gd->hscrolled -= available;
411                               gd->hsize -= available;
412                     } else
413                               available = 0;
414                     needed -= available;
415 
416                     /* Then fill the rest in with blanks. */
417                     for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
418                               grid_empty_line(gd, i, 8);
419           }
420 
421           /* Set the new size, and reset the scroll region. */
422           gd->sy = sy;
423           s->rupper = 0;
424           s->rlower = screen_size_y(s) - 1;
425 }
426 
427 /* Set selection. */
428 void
screen_set_selection(struct screen * s,u_int sx,u_int sy,u_int ex,u_int ey,u_int rectangle,int modekeys,struct grid_cell * gc)429 screen_set_selection(struct screen *s, u_int sx, u_int sy,
430     u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc)
431 {
432           if (s->sel == NULL)
433                     s->sel = xcalloc(1, sizeof *s->sel);
434 
435           memcpy(&s->sel->cell, gc, sizeof s->sel->cell);
436           s->sel->hidden = 0;
437           s->sel->rectangle = rectangle;
438           s->sel->modekeys = modekeys;
439 
440           s->sel->sx = sx;
441           s->sel->sy = sy;
442           s->sel->ex = ex;
443           s->sel->ey = ey;
444 }
445 
446 /* Clear selection. */
447 void
screen_clear_selection(struct screen * s)448 screen_clear_selection(struct screen *s)
449 {
450           free(s->sel);
451           s->sel = NULL;
452 }
453 
454 /* Hide selection. */
455 void
screen_hide_selection(struct screen * s)456 screen_hide_selection(struct screen *s)
457 {
458           if (s->sel != NULL)
459                     s->sel->hidden = 1;
460 }
461 
462 /* Check if cell in selection. */
463 int
screen_check_selection(struct screen * s,u_int px,u_int py)464 screen_check_selection(struct screen *s, u_int px, u_int py)
465 {
466           struct screen_sel   *sel = s->sel;
467           u_int                          xx;
468 
469           if (sel == NULL || sel->hidden)
470                     return (0);
471 
472           if (sel->rectangle) {
473                     if (sel->sy < sel->ey) {
474                               /* start line < end line -- downward selection. */
475                               if (py < sel->sy || py > sel->ey)
476                                         return (0);
477                     } else if (sel->sy > sel->ey) {
478                               /* start line > end line -- upward selection. */
479                               if (py > sel->sy || py < sel->ey)
480                                         return (0);
481                     } else {
482                               /* starting line == ending line. */
483                               if (py != sel->sy)
484                                         return (0);
485                     }
486 
487                     /*
488                      * Need to include the selection start row, but not the cursor
489                      * row, which means the selection changes depending on which
490                      * one is on the left.
491                      */
492                     if (sel->ex < sel->sx) {
493                               /* Cursor (ex) is on the left. */
494                               if (px < sel->ex)
495                                         return (0);
496 
497                               if (px > sel->sx)
498                                         return (0);
499                     } else {
500                               /* Selection start (sx) is on the left. */
501                               if (px < sel->sx)
502                                         return (0);
503 
504                               if (px > sel->ex)
505                                         return (0);
506                     }
507           } else {
508                     /*
509                      * Like emacs, keep the top-left-most character, and drop the
510                      * bottom-right-most, regardless of copy direction.
511                      */
512                     if (sel->sy < sel->ey) {
513                               /* starting line < ending line -- downward selection. */
514                               if (py < sel->sy || py > sel->ey)
515                                         return (0);
516 
517                               if (py == sel->sy && px < sel->sx)
518                                         return (0);
519 
520                               if (sel->modekeys == MODEKEY_EMACS)
521                                         xx = (sel->ex == 0 ? 0 : sel->ex - 1);
522                               else
523                                         xx = sel->ex;
524                               if (py == sel->ey && px > xx)
525                                         return (0);
526                     } else if (sel->sy > sel->ey) {
527                               /* starting line > ending line -- upward selection. */
528                               if (py > sel->sy || py < sel->ey)
529                                         return (0);
530 
531                               if (py == sel->ey && px < sel->ex)
532                                         return (0);
533 
534                               if (sel->modekeys == MODEKEY_EMACS)
535                                         xx = sel->sx - 1;
536                               else
537                                         xx = sel->sx;
538                               if (py == sel->sy && (sel->sx == 0 || px > xx))
539                                         return (0);
540                     } else {
541                               /* starting line == ending line. */
542                               if (py != sel->sy)
543                                         return (0);
544 
545                               if (sel->ex < sel->sx) {
546                                         /* cursor (ex) is on the left */
547                                         if (sel->modekeys == MODEKEY_EMACS)
548                                                   xx = sel->sx - 1;
549                                         else
550                                                   xx = sel->sx;
551                                         if (px > xx || px < sel->ex)
552                                                   return (0);
553                               } else {
554                                         /* selection start (sx) is on the left */
555                                         if (sel->modekeys == MODEKEY_EMACS)
556                                                   xx = (sel->ex == 0 ? 0 : sel->ex - 1);
557                                         else
558                                                   xx = sel->ex;
559                                         if (px < sel->sx || px > xx)
560                                                   return (0);
561                               }
562                     }
563           }
564 
565           return (1);
566 }
567 
568 /* Get selected grid cell. */
569 void
screen_select_cell(struct screen * s,struct grid_cell * dst,const struct grid_cell * src)570 screen_select_cell(struct screen *s, struct grid_cell *dst,
571     const struct grid_cell *src)
572 {
573           if (s->sel == NULL || s->sel->hidden)
574                     return;
575 
576           memcpy(dst, &s->sel->cell, sizeof *dst);
577 
578           utf8_copy(&dst->data, &src->data);
579           dst->attr = dst->attr & ~GRID_ATTR_CHARSET;
580           dst->attr |= src->attr & GRID_ATTR_CHARSET;
581           dst->flags = src->flags;
582 }
583 
584 /* Reflow wrapped lines. */
585 static void
screen_reflow(struct screen * s,u_int new_x,u_int * cx,u_int * cy,int cursor)586 screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor)
587 {
588           u_int     wx, wy;
589 
590           if (cursor) {
591                     grid_wrap_position(s->grid, *cx, *cy, &wx, &wy);
592                     log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx,
593                         wy);
594           }
595 
596           grid_reflow(s->grid, new_x);
597 
598           if (cursor) {
599                     grid_unwrap_position(s->grid, cx, cy, wx, wy);
600                     log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy);
601           }
602           else {
603                     *cx = 0;
604                     *cy = s->grid->hsize;
605           }
606 }
607 
608 /*
609  * Enter alternative screen mode. A copy of the visible screen is saved and the
610  * history is not updated.
611  */
612 void
screen_alternate_on(struct screen * s,struct grid_cell * gc,int cursor)613 screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
614 {
615           u_int     sx, sy;
616 
617           if (s->saved_grid != NULL)
618                     return;
619           sx = screen_size_x(s);
620           sy = screen_size_y(s);
621 
622           s->saved_grid = grid_create(sx, sy, 0);
623           grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy);
624           if (cursor) {
625                     s->saved_cx = s->cx;
626                     s->saved_cy = s->cy;
627           }
628           memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
629 
630           grid_view_clear(s->grid, 0, 0, sx, sy, 8);
631 
632           s->saved_flags = s->grid->flags;
633           s->grid->flags &= ~GRID_HISTORY;
634 }
635 
636 /* Exit alternate screen mode and restore the copied grid. */
637 void
screen_alternate_off(struct screen * s,struct grid_cell * gc,int cursor)638 screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
639 {
640           u_int     sx = screen_size_x(s), sy = screen_size_y(s);
641 
642           /*
643            * If the current size is different, temporarily resize to the old size
644            * before copying back.
645            */
646           if (s->saved_grid != NULL)
647                     screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0);
648 
649           /*
650            * Restore the cursor position and cell. This happens even if not
651            * currently in the alternate screen.
652            */
653           if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) {
654                     s->cx = s->saved_cx;
655                     s->cy = s->saved_cy;
656                     if (gc != NULL)
657                               memcpy(gc, &s->saved_cell, sizeof *gc);
658           }
659 
660           /* If not in the alternate screen, do nothing more. */
661           if (s->saved_grid == NULL) {
662                     if (s->cx > screen_size_x(s) - 1)
663                               s->cx = screen_size_x(s) - 1;
664                     if (s->cy > screen_size_y(s) - 1)
665                               s->cy = screen_size_y(s) - 1;
666                     return;
667           }
668 
669           /* Restore the saved grid. */
670           grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
671               s->saved_grid->sy);
672 
673           /*
674            * Turn history back on (so resize can use it) and then resize back to
675            * the current size.
676            */
677           if (s->saved_flags & GRID_HISTORY)
678                     s->grid->flags |= GRID_HISTORY;
679           screen_resize(s, sx, sy, 1);
680 
681           grid_destroy(s->saved_grid);
682           s->saved_grid = NULL;
683 
684           if (s->cx > screen_size_x(s) - 1)
685                     s->cx = screen_size_x(s) - 1;
686           if (s->cy > screen_size_y(s) - 1)
687                     s->cy = screen_size_y(s) - 1;
688 }
689 
690 /* Get mode as a string. */
691 const char *
screen_mode_to_string(int mode)692 screen_mode_to_string(int mode)
693 {
694           static char         tmp[1024];
695 
696           if (mode == 0)
697                     return ("NONE");
698           if (mode == ALL_MODES)
699                     return ("ALL");
700 
701           *tmp = '\0';
702           if (mode & MODE_CURSOR)
703                     strlcat(tmp, "CURSOR,", sizeof tmp);
704           if (mode & MODE_INSERT)
705                     strlcat(tmp, "INSERT,", sizeof tmp);
706           if (mode & MODE_KCURSOR)
707                     strlcat(tmp, "KCURSOR,", sizeof tmp);
708           if (mode & MODE_KKEYPAD)
709                     strlcat(tmp, "KKEYPAD,", sizeof tmp);
710           if (mode & MODE_WRAP)
711                     strlcat(tmp, "WRAP,", sizeof tmp);
712           if (mode & MODE_MOUSE_STANDARD)
713                     strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp);
714           if (mode & MODE_MOUSE_BUTTON)
715                     strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp);
716           if (mode & MODE_CURSOR_BLINKING)
717                     strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp);
718           if (mode & MODE_CURSOR_VERY_VISIBLE)
719                     strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp);
720           if (mode & MODE_MOUSE_UTF8)
721                     strlcat(tmp, "MOUSE_UTF8,", sizeof tmp);
722           if (mode & MODE_MOUSE_SGR)
723                     strlcat(tmp, "MOUSE_SGR,", sizeof tmp);
724           if (mode & MODE_BRACKETPASTE)
725                     strlcat(tmp, "BRACKETPASTE,", sizeof tmp);
726           if (mode & MODE_FOCUSON)
727                     strlcat(tmp, "FOCUSON,", sizeof tmp);
728           if (mode & MODE_MOUSE_ALL)
729                     strlcat(tmp, "MOUSE_ALL,", sizeof tmp);
730           if (mode & MODE_ORIGIN)
731                     strlcat(tmp, "ORIGIN,", sizeof tmp);
732           if (mode & MODE_CRLF)
733                     strlcat(tmp, "CRLF,", sizeof tmp);
734           if (mode & MODE_KEYS_EXTENDED)
735                     strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp);
736           if (mode & MODE_KEYS_EXTENDED_2)
737                     strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp);
738           tmp[strlen(tmp) - 1] = '\0';
739           return (tmp);
740 }
741