Coverage Report

Created: 2025-10-10 06:33

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