Coverage Report

Created: 2026-10-28 06:08

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