xref: /openbsd-src/usr.bin/tmux/screen.c (revision 8b4580606d31f7b3f7bb552747be02e2835e1142)
1*8b458060Snicm /* $OpenBSD: screen.c,v 1.88 2024/11/15 09:01:16 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
2114e0d2d2Snicm #include <stdlib.h>
22311827fbSnicm #include <string.h>
235d82a79bSnicm #include <unistd.h>
249d9ffcabSnicm #include <vis.h>
25311827fbSnicm 
26311827fbSnicm #include "tmux.h"
27311827fbSnicm 
283f2a5436Snicm /* Selected area in screen. */
293f2a5436Snicm struct screen_sel {
303f2a5436Snicm 	int		 hidden;
313f2a5436Snicm 	int		 rectangle;
323f2a5436Snicm 	int		 modekeys;
333f2a5436Snicm 
343f2a5436Snicm 	u_int		 sx;
353f2a5436Snicm 	u_int		 sy;
363f2a5436Snicm 
373f2a5436Snicm 	u_int		 ex;
383f2a5436Snicm 	u_int		 ey;
393f2a5436Snicm 
403f2a5436Snicm 	struct grid_cell cell;
413f2a5436Snicm };
423f2a5436Snicm 
433f2a5436Snicm /* Entry on title stack. */
442981c1f2Snicm struct screen_title_entry {
452981c1f2Snicm 	char				*text;
462981c1f2Snicm 
472981c1f2Snicm 	TAILQ_ENTRY(screen_title_entry)	 entry;
482981c1f2Snicm };
492981c1f2Snicm TAILQ_HEAD(screen_titles, screen_title_entry);
502981c1f2Snicm 
51f191cc43Snicm static void	screen_resize_y(struct screen *, u_int, int, u_int *);
52f2afc29bSnicm static void	screen_reflow(struct screen *, u_int, u_int *, u_int *, int);
53ced21769Snicm 
542981c1f2Snicm /* Free titles stack. */
552981c1f2Snicm static void
562981c1f2Snicm screen_free_titles(struct screen *s)
572981c1f2Snicm {
582981c1f2Snicm 	struct screen_title_entry	*title_entry;
592981c1f2Snicm 
602981c1f2Snicm 	if (s->titles == NULL)
612981c1f2Snicm 		return;
622981c1f2Snicm 
632981c1f2Snicm 	while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) {
642981c1f2Snicm 		TAILQ_REMOVE(s->titles, title_entry, entry);
652981c1f2Snicm 		free(title_entry->text);
662981c1f2Snicm 		free(title_entry);
672981c1f2Snicm 	}
682981c1f2Snicm 
692981c1f2Snicm 	free(s->titles);
702981c1f2Snicm 	s->titles = NULL;
712981c1f2Snicm }
722981c1f2Snicm 
73311827fbSnicm /* Create a new screen. */
74311827fbSnicm void
75311827fbSnicm screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
76311827fbSnicm {
77311827fbSnicm 	s->grid = grid_create(sx, sy, hlimit);
78af550f8bSnicm 	s->saved_grid = NULL;
79af550f8bSnicm 
80311827fbSnicm 	s->title = xstrdup("");
812981c1f2Snicm 	s->titles = NULL;
82d431089bSnicm 	s->path = NULL;
83311827fbSnicm 
84cd59b7b0Snicm 	s->cstyle = SCREEN_CURSOR_DEFAULT;
856a238659Snicm 	s->default_cstyle = SCREEN_CURSOR_DEFAULT;
861d6a833eSnicm 	s->mode = MODE_CURSOR;
876a238659Snicm 	s->default_mode = 0;
881db1a6bbSnicm 	s->ccolour = -1;
891db1a6bbSnicm 	s->default_ccolour = -1;
9014e0d2d2Snicm 	s->tabs = NULL;
913f2a5436Snicm 	s->sel = NULL;
9214e0d2d2Snicm 
93977b6338Snicm 	s->write_list = NULL;
942df6775cSnicm 	s->hyperlinks = NULL;
95977b6338Snicm 
96311827fbSnicm 	screen_reinit(s);
97311827fbSnicm }
98311827fbSnicm 
99311827fbSnicm /* Reinitialise screen. */
100311827fbSnicm void
101311827fbSnicm screen_reinit(struct screen *s)
102311827fbSnicm {
103311827fbSnicm 	s->cx = 0;
104311827fbSnicm 	s->cy = 0;
105311827fbSnicm 
106311827fbSnicm 	s->rupper = 0;
107311827fbSnicm 	s->rlower = screen_size_y(s) - 1;
108311827fbSnicm 
109d3c2ad4fSnicm 	s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF);
110719f5715Snicm 
1110465bfa0Snicm 	if (options_get_number(global_options, "extended-keys") == 2)
112719f5715Snicm 		s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
113311827fbSnicm 
114*8b458060Snicm 	if (SCREEN_IS_ALTERNATE(s))
115af550f8bSnicm 		screen_alternate_off(s, NULL, 0);
116af550f8bSnicm 	s->saved_cx = UINT_MAX;
117af550f8bSnicm 	s->saved_cy = UINT_MAX;
118af550f8bSnicm 
11914e0d2d2Snicm 	screen_reset_tabs(s);
12014e0d2d2Snicm 
121ae4624e7Snicm 	grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8);
122311827fbSnicm 
123311827fbSnicm 	screen_clear_selection(s);
1242981c1f2Snicm 	screen_free_titles(s);
1252df6775cSnicm 	screen_reset_hyperlinks(s);
1262df6775cSnicm }
1272df6775cSnicm 
1282df6775cSnicm /* Reset hyperlinks of a screen. */
1292df6775cSnicm void
1302df6775cSnicm screen_reset_hyperlinks(struct screen *s)
1312df6775cSnicm {
1322df6775cSnicm 	if (s->hyperlinks == NULL)
1332df6775cSnicm 		s->hyperlinks = hyperlinks_init();
1342df6775cSnicm 	else
1352df6775cSnicm 		hyperlinks_reset(s->hyperlinks);
136311827fbSnicm }
137311827fbSnicm 
138311827fbSnicm /* Destroy a screen. */
139311827fbSnicm void
140311827fbSnicm screen_free(struct screen *s)
141311827fbSnicm {
1423f2a5436Snicm 	free(s->sel);
1437d053cf9Snicm 	free(s->tabs);
144d431089bSnicm 	free(s->path);
1457d053cf9Snicm 	free(s->title);
1462981c1f2Snicm 
147977b6338Snicm 	if (s->write_list != NULL)
148977b6338Snicm 		screen_write_free_list(s);
149977b6338Snicm 
150*8b458060Snicm 	if (SCREEN_IS_ALTERNATE(s))
151af550f8bSnicm 		grid_destroy(s->saved_grid);
152311827fbSnicm 	grid_destroy(s->grid);
1532981c1f2Snicm 
1542df6775cSnicm 	if (s->hyperlinks != NULL)
1552df6775cSnicm 		hyperlinks_free(s->hyperlinks);
1562981c1f2Snicm 	screen_free_titles(s);
157311827fbSnicm }
158311827fbSnicm 
15914e0d2d2Snicm /* Reset tabs to default, eight spaces apart. */
16014e0d2d2Snicm void
16114e0d2d2Snicm screen_reset_tabs(struct screen *s)
16214e0d2d2Snicm {
16314e0d2d2Snicm 	u_int	i;
16414e0d2d2Snicm 
1657d053cf9Snicm 	free(s->tabs);
16614e0d2d2Snicm 
16714e0d2d2Snicm 	if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
16814e0d2d2Snicm 		fatal("bit_alloc failed");
16914e0d2d2Snicm 	for (i = 8; i < screen_size_x(s); i += 8)
17014e0d2d2Snicm 		bit_set(s->tabs, i);
17114e0d2d2Snicm }
17214e0d2d2Snicm 
173d1cbf82eSnicm /* Set default cursor style and colour from options. */
174d1cbf82eSnicm void
175d1cbf82eSnicm screen_set_default_cursor(struct screen *s, struct options *oo)
176d1cbf82eSnicm {
177d1cbf82eSnicm 	int	c;
178d1cbf82eSnicm 
179d1cbf82eSnicm 	c = options_get_number(oo, "cursor-colour");
180d1cbf82eSnicm 	s->default_ccolour = c;
181d1cbf82eSnicm 
182d1cbf82eSnicm 	c = options_get_number(oo, "cursor-style");
183d1cbf82eSnicm 	s->default_mode = 0;
184d1cbf82eSnicm 	screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode);
185d1cbf82eSnicm }
186d1cbf82eSnicm 
1876a238659Snicm /* Set screen cursor style and mode. */
188e5464ab3Snicm void
1896a238659Snicm screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle,
1906a238659Snicm     int *mode)
191e5464ab3Snicm {
19226a875eaSnicm 	switch (style) {
193cd59b7b0Snicm 	case 0:
1946a238659Snicm 		*cstyle = SCREEN_CURSOR_DEFAULT;
195cd59b7b0Snicm 		break;
196cd59b7b0Snicm 	case 1:
1976a238659Snicm 		*cstyle = SCREEN_CURSOR_BLOCK;
1986a238659Snicm 		*mode |= MODE_CURSOR_BLINKING;
199cd59b7b0Snicm 		break;
200cd59b7b0Snicm 	case 2:
2016a238659Snicm 		*cstyle = SCREEN_CURSOR_BLOCK;
2026a238659Snicm 		*mode &= ~MODE_CURSOR_BLINKING;
203cd59b7b0Snicm 		break;
204cd59b7b0Snicm 	case 3:
2056a238659Snicm 		*cstyle = SCREEN_CURSOR_UNDERLINE;
2066a238659Snicm 		*mode |= MODE_CURSOR_BLINKING;
207cd59b7b0Snicm 		break;
208cd59b7b0Snicm 	case 4:
2096a238659Snicm 		*cstyle = SCREEN_CURSOR_UNDERLINE;
2106a238659Snicm 		*mode &= ~MODE_CURSOR_BLINKING;
211cd59b7b0Snicm 		break;
212cd59b7b0Snicm 	case 5:
2136a238659Snicm 		*cstyle = SCREEN_CURSOR_BAR;
2146a238659Snicm 		*mode |= MODE_CURSOR_BLINKING;
215cd59b7b0Snicm 		break;
216cd59b7b0Snicm 	case 6:
2176a238659Snicm 		*cstyle = SCREEN_CURSOR_BAR;
2186a238659Snicm 		*mode &= ~MODE_CURSOR_BLINKING;
219cd59b7b0Snicm 		break;
22085dd3030Snicm 	}
221e5464ab3Snicm }
222e5464ab3Snicm 
223e417d1c0Snicm /* Set screen cursor colour. */
224e417d1c0Snicm void
2251db1a6bbSnicm screen_set_cursor_colour(struct screen *s, int colour)
226e417d1c0Snicm {
2271db1a6bbSnicm 	s->ccolour = colour;
228e417d1c0Snicm }
229e417d1c0Snicm 
230311827fbSnicm /* Set screen title. */
231fe77834bSnicm int
232311827fbSnicm screen_set_title(struct screen *s, const char *title)
233311827fbSnicm {
234fe77834bSnicm 	if (!utf8_isvalid(title))
235fe77834bSnicm 		return (0);
2367d053cf9Snicm 	free(s->title);
237fe77834bSnicm 	s->title = xstrdup(title);
238fe77834bSnicm 	return (1);
239311827fbSnicm }
240311827fbSnicm 
241e9afa876Snicm /* Set screen path. */
242e9afa876Snicm void
243e9afa876Snicm screen_set_path(struct screen *s, const char *path)
244e9afa876Snicm {
245e9afa876Snicm 	free(s->path);
246e9afa876Snicm 	utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
247e9afa876Snicm }
248e9afa876Snicm 
2492981c1f2Snicm /* Push the current title onto the stack. */
2502981c1f2Snicm void
2512981c1f2Snicm screen_push_title(struct screen *s)
2522981c1f2Snicm {
2532981c1f2Snicm 	struct screen_title_entry *title_entry;
2542981c1f2Snicm 
2552981c1f2Snicm 	if (s->titles == NULL) {
2562981c1f2Snicm 		s->titles = xmalloc(sizeof *s->titles);
2572981c1f2Snicm 		TAILQ_INIT(s->titles);
2582981c1f2Snicm 	}
2592981c1f2Snicm 	title_entry = xmalloc(sizeof *title_entry);
2602981c1f2Snicm 	title_entry->text = xstrdup(s->title);
2612981c1f2Snicm 	TAILQ_INSERT_HEAD(s->titles, title_entry, entry);
2622981c1f2Snicm }
2632981c1f2Snicm 
2642981c1f2Snicm /*
2652981c1f2Snicm  * Pop a title from the stack and set it as the screen title. If the stack is
2662981c1f2Snicm  * empty, do nothing.
2672981c1f2Snicm  */
2682981c1f2Snicm void
2692981c1f2Snicm screen_pop_title(struct screen *s)
2702981c1f2Snicm {
2712981c1f2Snicm 	struct screen_title_entry *title_entry;
2722981c1f2Snicm 
2732981c1f2Snicm 	if (s->titles == NULL)
2742981c1f2Snicm 		return;
2752981c1f2Snicm 
2762981c1f2Snicm 	title_entry = TAILQ_FIRST(s->titles);
2772981c1f2Snicm 	if (title_entry != NULL) {
2782981c1f2Snicm 		screen_set_title(s, title_entry->text);
2792981c1f2Snicm 
2802981c1f2Snicm 		TAILQ_REMOVE(s->titles, title_entry, entry);
2812981c1f2Snicm 		free(title_entry->text);
2822981c1f2Snicm 		free(title_entry);
2832981c1f2Snicm 	}
2842981c1f2Snicm }
2852981c1f2Snicm 
286f2afc29bSnicm /* Resize screen with options. */
287311827fbSnicm void
2889c3d9d86Snicm screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
289f2afc29bSnicm     int eat_empty, int cursor)
290311827fbSnicm {
291f2afc29bSnicm 	u_int	cx = s->cx, cy = s->grid->hsize + s->cy;
2929c3d9d86Snicm 
29350376775Snicm 	if (s->write_list != NULL)
294977b6338Snicm 		screen_write_free_list(s);
295977b6338Snicm 
29681c92089Snicm 	log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)",
29781c92089Snicm 	    __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy,
298f2afc29bSnicm 	    cx, cy);
2999c3d9d86Snicm 
300311827fbSnicm 	if (sx < 1)
301311827fbSnicm 		sx = 1;
302311827fbSnicm 	if (sy < 1)
303311827fbSnicm 		sy = 1;
304311827fbSnicm 
30514e0d2d2Snicm 	if (sx != screen_size_x(s)) {
306db552f66Snicm 		s->grid->sx = sx;
30714e0d2d2Snicm 		screen_reset_tabs(s);
308ce9229f4Snicm 	} else
309ce9229f4Snicm 		reflow = 0;
31014e0d2d2Snicm 
311311827fbSnicm 	if (sy != screen_size_y(s))
312f2afc29bSnicm 		screen_resize_y(s, sy, eat_empty, &cy);
313db4b44a2Snicm 
314db4b44a2Snicm 	if (reflow)
315f2afc29bSnicm 		screen_reflow(s, sx, &cx, &cy, cursor);
3169c3d9d86Snicm 
317f2afc29bSnicm 	if (cy >= s->grid->hsize) {
318f2afc29bSnicm 		s->cx = cx;
319f2afc29bSnicm 		s->cy = cy - s->grid->hsize;
3209c3d9d86Snicm 	} else {
3219c3d9d86Snicm 		s->cx = 0;
3229c3d9d86Snicm 		s->cy = 0;
3239c3d9d86Snicm 	}
324f2afc29bSnicm 
32581c92089Snicm 	log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx,
326f2afc29bSnicm 	    s->cy, cx, cy);
32750376775Snicm 
32850376775Snicm 	if (s->write_list != NULL)
32950376775Snicm 		screen_write_make_list(s);
3309c3d9d86Snicm }
3319c3d9d86Snicm 
3329c3d9d86Snicm /* Resize screen. */
3339c3d9d86Snicm void
3349c3d9d86Snicm screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
3359c3d9d86Snicm {
336f2afc29bSnicm 	screen_resize_cursor(s, sx, sy, reflow, 1, 1);
337311827fbSnicm }
338311827fbSnicm 
3399883b791Snicm static void
340f191cc43Snicm screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy)
341311827fbSnicm {
342311827fbSnicm 	struct grid	*gd = s->grid;
343516fb81dSnicm 	u_int		 needed, available, oldy, i;
344311827fbSnicm 
345311827fbSnicm 	if (sy == 0)
346311827fbSnicm 		fatalx("zero size");
347516fb81dSnicm 	oldy = screen_size_y(s);
348516fb81dSnicm 
349516fb81dSnicm 	/*
350516fb81dSnicm 	 * When resizing:
351516fb81dSnicm 	 *
352516fb81dSnicm 	 * If the height is decreasing, delete lines from the bottom until
353516fb81dSnicm 	 * hitting the cursor, then push lines from the top into the history.
354516fb81dSnicm 	 *
35593ccd604Snicm 	 * When increasing, pull as many lines as possible from scrolled
35693ccd604Snicm 	 * history (not explicitly cleared from view) to the top, then fill the
35793ccd604Snicm 	 * remaining with blanks at the bottom.
358516fb81dSnicm 	 */
359311827fbSnicm 
360311827fbSnicm 	/* Size decreasing. */
361516fb81dSnicm 	if (sy < oldy) {
362516fb81dSnicm 		needed = oldy - sy;
363311827fbSnicm 
364516fb81dSnicm 		/* Delete as many lines as possible from the bottom. */
3659c3d9d86Snicm 		if (eat_empty) {
366516fb81dSnicm 			available = oldy - 1 - s->cy;
367516fb81dSnicm 			if (available > 0) {
368516fb81dSnicm 				if (available > needed)
369516fb81dSnicm 					available = needed;
3709c3d9d86Snicm 				grid_view_delete_lines(gd, oldy - available,
3719c3d9d86Snicm 				    available, 8);
372516fb81dSnicm 			}
373516fb81dSnicm 			needed -= available;
3749c3d9d86Snicm 		}
375516fb81dSnicm 
376311827fbSnicm 		/*
377953d773cSnicm 		 * Now just increase the history size, if possible, to take
378953d773cSnicm 		 * over the lines which are left. If history is off, delete
379953d773cSnicm 		 * lines from the top.
380311827fbSnicm 		 */
381953d773cSnicm 		available = s->cy;
38293ccd604Snicm 		if (gd->flags & GRID_HISTORY) {
38393ccd604Snicm 			gd->hscrolled += needed;
384516fb81dSnicm 			gd->hsize += needed;
38593ccd604Snicm 		} else if (needed > 0 && available > 0) {
386953d773cSnicm 			if (available > needed)
387953d773cSnicm 				available = needed;
388ae4624e7Snicm 			grid_view_delete_lines(gd, 0, available, 8);
389f191cc43Snicm 			(*cy) -= available;
390953d773cSnicm 		}
391311827fbSnicm 	}
392311827fbSnicm 
3937621e88fSnicm 	/* Resize line array. */
3947621e88fSnicm 	grid_adjust_lines(gd, gd->hsize + sy);
395311827fbSnicm 
396311827fbSnicm 	/* Size increasing. */
397516fb81dSnicm 	if (sy > oldy) {
398516fb81dSnicm 		needed = sy - oldy;
399516fb81dSnicm 
400953d773cSnicm 		/*
4019883b791Snicm 		 * Try to pull as much as possible out of scrolled history, if
40233c89b8bSnicm 		 * it is enabled.
403953d773cSnicm 		 */
40493ccd604Snicm 		available = gd->hscrolled;
405953d773cSnicm 		if (gd->flags & GRID_HISTORY && available > 0) {
406516fb81dSnicm 			if (available > needed)
407516fb81dSnicm 				available = needed;
40893ccd604Snicm 			gd->hscrolled -= available;
409516fb81dSnicm 			gd->hsize -= available;
410953d773cSnicm 		} else
411953d773cSnicm 			available = 0;
412516fb81dSnicm 		needed -= available;
413516fb81dSnicm 
414516fb81dSnicm 		/* Then fill the rest in with blanks. */
4159554a682Snicm 		for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
41675343b37Snicm 			grid_empty_line(gd, i, 8);
417311827fbSnicm 	}
418311827fbSnicm 
419516fb81dSnicm 	/* Set the new size, and reset the scroll region. */
420311827fbSnicm 	gd->sy = sy;
421311827fbSnicm 	s->rupper = 0;
422311827fbSnicm 	s->rlower = screen_size_y(s) - 1;
423311827fbSnicm }
424311827fbSnicm 
425311827fbSnicm /* Set selection. */
426311827fbSnicm void
42778d51a74Snicm screen_set_selection(struct screen *s, u_int sx, u_int sy,
4283f2a5436Snicm     u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc)
429311827fbSnicm {
4303f2a5436Snicm 	if (s->sel == NULL)
4313f2a5436Snicm 		s->sel = xcalloc(1, sizeof *s->sel);
432311827fbSnicm 
4333f2a5436Snicm 	memcpy(&s->sel->cell, gc, sizeof s->sel->cell);
4343f2a5436Snicm 	s->sel->hidden = 0;
4353f2a5436Snicm 	s->sel->rectangle = rectangle;
4363f2a5436Snicm 	s->sel->modekeys = modekeys;
437e9c0de95Snicm 
4383f2a5436Snicm 	s->sel->sx = sx;
4393f2a5436Snicm 	s->sel->sy = sy;
4403f2a5436Snicm 	s->sel->ex = ex;
4413f2a5436Snicm 	s->sel->ey = ey;
442311827fbSnicm }
443311827fbSnicm 
444311827fbSnicm /* Clear selection. */
445311827fbSnicm void
446311827fbSnicm screen_clear_selection(struct screen *s)
447311827fbSnicm {
4483f2a5436Snicm 	free(s->sel);
4493f2a5436Snicm 	s->sel = NULL;
450311827fbSnicm }
451311827fbSnicm 
452e9c0de95Snicm /* Hide selection. */
453e9c0de95Snicm void
454e9c0de95Snicm screen_hide_selection(struct screen *s)
455e9c0de95Snicm {
4563f2a5436Snicm 	if (s->sel != NULL)
4573f2a5436Snicm 		s->sel->hidden = 1;
458e9c0de95Snicm }
459e9c0de95Snicm 
460311827fbSnicm /* Check if cell in selection. */
461311827fbSnicm int
462311827fbSnicm screen_check_selection(struct screen *s, u_int px, u_int py)
463311827fbSnicm {
4643f2a5436Snicm 	struct screen_sel	*sel = s->sel;
465371f2138Snicm 	u_int			 xx;
466311827fbSnicm 
4673f2a5436Snicm 	if (sel == NULL || sel->hidden)
468311827fbSnicm 		return (0);
469311827fbSnicm 
4703f2a5436Snicm 	if (sel->rectangle) {
47178d51a74Snicm 		if (sel->sy < sel->ey) {
47278d51a74Snicm 			/* start line < end line -- downward selection. */
47378d51a74Snicm 			if (py < sel->sy || py > sel->ey)
474311827fbSnicm 				return (0);
47578d51a74Snicm 		} else if (sel->sy > sel->ey) {
47678d51a74Snicm 			/* start line > end line -- upward selection. */
47778d51a74Snicm 			if (py > sel->sy || py < sel->ey)
47878d51a74Snicm 				return (0);
47978d51a74Snicm 		} else {
48078d51a74Snicm 			/* starting line == ending line. */
48178d51a74Snicm 			if (py != sel->sy)
48278d51a74Snicm 				return (0);
483311827fbSnicm 		}
484311827fbSnicm 
48578d51a74Snicm 		/*
48678d51a74Snicm 		 * Need to include the selection start row, but not the cursor
48778d51a74Snicm 		 * row, which means the selection changes depending on which
48878d51a74Snicm 		 * one is on the left.
48978d51a74Snicm 		 */
49078d51a74Snicm 		if (sel->ex < sel->sx) {
49178d51a74Snicm 			/* Cursor (ex) is on the left. */
492998044b5Snicm 			if (px < sel->ex)
493311827fbSnicm 				return (0);
49478d51a74Snicm 
49578d51a74Snicm 			if (px > sel->sx)
49678d51a74Snicm 				return (0);
49778d51a74Snicm 		} else {
49878d51a74Snicm 			/* Selection start (sx) is on the left. */
49978d51a74Snicm 			if (px < sel->sx)
50078d51a74Snicm 				return (0);
50178d51a74Snicm 
502998044b5Snicm 			if (px > sel->ex)
50378d51a74Snicm 				return (0);
50478d51a74Snicm 		}
50578d51a74Snicm 	} else {
50678d51a74Snicm 		/*
50778d51a74Snicm 		 * Like emacs, keep the top-left-most character, and drop the
50878d51a74Snicm 		 * bottom-right-most, regardless of copy direction.
50978d51a74Snicm 		 */
51078d51a74Snicm 		if (sel->sy < sel->ey) {
51178d51a74Snicm 			/* starting line < ending line -- downward selection. */
51278d51a74Snicm 			if (py < sel->sy || py > sel->ey)
51378d51a74Snicm 				return (0);
51478d51a74Snicm 
515371f2138Snicm 			if (py == sel->sy && px < sel->sx)
516c6cf1effSnicm 				return (0);
517371f2138Snicm 
5185bf9cff1Snicm 			if (sel->modekeys == MODEKEY_EMACS)
5195bf9cff1Snicm 				xx = (sel->ex == 0 ? 0 : sel->ex - 1);
5205bf9cff1Snicm 			else
5215bf9cff1Snicm 				xx = sel->ex;
5225bf9cff1Snicm 			if (py == sel->ey && px > xx)
52378d51a74Snicm 				return (0);
52478d51a74Snicm 		} else if (sel->sy > sel->ey) {
52578d51a74Snicm 			/* starting line > ending line -- upward selection. */
52678d51a74Snicm 			if (py > sel->sy || py < sel->ey)
52778d51a74Snicm 				return (0);
52878d51a74Snicm 
529371f2138Snicm 			if (py == sel->ey && px < sel->ex)
530371f2138Snicm 				return (0);
531371f2138Snicm 
532371f2138Snicm 			if (sel->modekeys == MODEKEY_EMACS)
533371f2138Snicm 				xx = sel->sx - 1;
534371f2138Snicm 			else
535371f2138Snicm 				xx = sel->sx;
5368248eab7Snicm 			if (py == sel->sy && (sel->sx == 0 || px > xx))
53778d51a74Snicm 				return (0);
53878d51a74Snicm 		} else {
53978d51a74Snicm 			/* starting line == ending line. */
54078d51a74Snicm 			if (py != sel->sy)
54178d51a74Snicm 				return (0);
54278d51a74Snicm 
54378d51a74Snicm 			if (sel->ex < sel->sx) {
54478d51a74Snicm 				/* cursor (ex) is on the left */
545371f2138Snicm 				if (sel->modekeys == MODEKEY_EMACS)
546371f2138Snicm 					xx = sel->sx - 1;
547371f2138Snicm 				else
548371f2138Snicm 					xx = sel->sx;
549371f2138Snicm 				if (px > xx || px < sel->ex)
55078d51a74Snicm 					return (0);
55178d51a74Snicm 			} else {
55278d51a74Snicm 				/* selection start (sx) is on the left */
5535bf9cff1Snicm 				if (sel->modekeys == MODEKEY_EMACS)
5545bf9cff1Snicm 					xx = (sel->ex == 0 ? 0 : sel->ex - 1);
5555bf9cff1Snicm 				else
5565bf9cff1Snicm 					xx = sel->ex;
5575bf9cff1Snicm 				if (px < sel->sx || px > xx)
55878d51a74Snicm 					return (0);
55978d51a74Snicm 			}
56078d51a74Snicm 		}
56178d51a74Snicm 	}
56278d51a74Snicm 
563311827fbSnicm 	return (1);
564311827fbSnicm }
565db4b44a2Snicm 
5668ab25c40Snicm /* Get selected grid cell. */
5678ab25c40Snicm void
5688ab25c40Snicm screen_select_cell(struct screen *s, struct grid_cell *dst,
5698ab25c40Snicm     const struct grid_cell *src)
5708ab25c40Snicm {
5713f2a5436Snicm 	if (s->sel == NULL || s->sel->hidden)
5728ab25c40Snicm 		return;
5738ab25c40Snicm 
5743f2a5436Snicm 	memcpy(dst, &s->sel->cell, sizeof *dst);
5758ab25c40Snicm 
5768ab25c40Snicm 	utf8_copy(&dst->data, &src->data);
5778ab25c40Snicm 	dst->attr = dst->attr & ~GRID_ATTR_CHARSET;
5788ab25c40Snicm 	dst->attr |= src->attr & GRID_ATTR_CHARSET;
5798ab25c40Snicm 	dst->flags = src->flags;
5808ab25c40Snicm }
5818ab25c40Snicm 
582db4b44a2Snicm /* Reflow wrapped lines. */
583ced21769Snicm static void
584f2afc29bSnicm screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor)
585db4b44a2Snicm {
5869c3d9d86Snicm 	u_int	wx, wy;
587db552f66Snicm 
588f2afc29bSnicm 	if (cursor) {
5899c3d9d86Snicm 		grid_wrap_position(s->grid, *cx, *cy, &wx, &wy);
590f2afc29bSnicm 		log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx,
591f2afc29bSnicm 		    wy);
592f2afc29bSnicm 	}
593db552f66Snicm 
594db552f66Snicm 	grid_reflow(s->grid, new_x);
595db552f66Snicm 
596f2afc29bSnicm 	if (cursor) {
5979c3d9d86Snicm 		grid_unwrap_position(s->grid, cx, cy, wx, wy);
5989c3d9d86Snicm 		log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy);
599db4b44a2Snicm 	}
600f2afc29bSnicm 	else {
601f2afc29bSnicm 		*cx = 0;
602f2afc29bSnicm 		*cy = s->grid->hsize;
603f2afc29bSnicm 	}
604f2afc29bSnicm }
605af550f8bSnicm 
606af550f8bSnicm /*
607af550f8bSnicm  * Enter alternative screen mode. A copy of the visible screen is saved and the
608af550f8bSnicm  * history is not updated.
609af550f8bSnicm  */
610af550f8bSnicm void
611af550f8bSnicm screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
612af550f8bSnicm {
613af550f8bSnicm 	u_int	sx, sy;
614af550f8bSnicm 
615*8b458060Snicm 	if (SCREEN_IS_ALTERNATE(s))
616af550f8bSnicm 		return;
617af550f8bSnicm 	sx = screen_size_x(s);
618af550f8bSnicm 	sy = screen_size_y(s);
619af550f8bSnicm 
620af550f8bSnicm 	s->saved_grid = grid_create(sx, sy, 0);
621af550f8bSnicm 	grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy);
622af550f8bSnicm 	if (cursor) {
623af550f8bSnicm 		s->saved_cx = s->cx;
624af550f8bSnicm 		s->saved_cy = s->cy;
625af550f8bSnicm 	}
626af550f8bSnicm 	memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
627af550f8bSnicm 
628af550f8bSnicm 	grid_view_clear(s->grid, 0, 0, sx, sy, 8);
629af550f8bSnicm 
630a6732819Snicm 	s->saved_flags = s->grid->flags;
631af550f8bSnicm 	s->grid->flags &= ~GRID_HISTORY;
632af550f8bSnicm }
633af550f8bSnicm 
634af550f8bSnicm /* Exit alternate screen mode and restore the copied grid. */
635af550f8bSnicm void
636af550f8bSnicm screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
637af550f8bSnicm {
63888dcfb46Snicm 	u_int	sx = screen_size_x(s), sy = screen_size_y(s);
63988dcfb46Snicm 
64088dcfb46Snicm 	/*
64188dcfb46Snicm 	 * If the current size is different, temporarily resize to the old size
64288dcfb46Snicm 	 * before copying back.
64388dcfb46Snicm 	 */
644*8b458060Snicm 	if (SCREEN_IS_ALTERNATE(s))
64537e1ec45Snicm 		screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0);
646af550f8bSnicm 
647af550f8bSnicm 	/*
648af550f8bSnicm 	 * Restore the cursor position and cell. This happens even if not
649af550f8bSnicm 	 * currently in the alternate screen.
650af550f8bSnicm 	 */
651af550f8bSnicm 	if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) {
652af550f8bSnicm 		s->cx = s->saved_cx;
653af550f8bSnicm 		s->cy = s->saved_cy;
654af550f8bSnicm 		if (gc != NULL)
655af550f8bSnicm 			memcpy(gc, &s->saved_cell, sizeof *gc);
656af550f8bSnicm 	}
657af550f8bSnicm 
65888dcfb46Snicm 	/* If not in the alternate screen, do nothing more. */
659*8b458060Snicm 	if (!SCREEN_IS_ALTERNATE(s)) {
66088dcfb46Snicm 		if (s->cx > screen_size_x(s) - 1)
66188dcfb46Snicm 			s->cx = screen_size_x(s) - 1;
66288dcfb46Snicm 		if (s->cy > screen_size_y(s) - 1)
66388dcfb46Snicm 			s->cy = screen_size_y(s) - 1;
664af550f8bSnicm 		return;
66588dcfb46Snicm 	}
666af550f8bSnicm 
667af550f8bSnicm 	/* Restore the saved grid. */
66888dcfb46Snicm 	grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
66988dcfb46Snicm 	    s->saved_grid->sy);
670af550f8bSnicm 
671af550f8bSnicm 	/*
672af550f8bSnicm 	 * Turn history back on (so resize can use it) and then resize back to
673af550f8bSnicm 	 * the current size.
674af550f8bSnicm 	 */
675a6732819Snicm 	if (s->saved_flags & GRID_HISTORY)
676af550f8bSnicm 		s->grid->flags |= GRID_HISTORY;
677af550f8bSnicm 	screen_resize(s, sx, sy, 1);
678af550f8bSnicm 
679af550f8bSnicm 	grid_destroy(s->saved_grid);
680af550f8bSnicm 	s->saved_grid = NULL;
68188dcfb46Snicm 
68288dcfb46Snicm 	if (s->cx > screen_size_x(s) - 1)
68388dcfb46Snicm 		s->cx = screen_size_x(s) - 1;
68488dcfb46Snicm 	if (s->cy > screen_size_y(s) - 1)
68588dcfb46Snicm 		s->cy = screen_size_y(s) - 1;
686af550f8bSnicm }
68726a875eaSnicm 
68826a875eaSnicm /* Get mode as a string. */
68926a875eaSnicm const char *
69026a875eaSnicm screen_mode_to_string(int mode)
69126a875eaSnicm {
69226a875eaSnicm 	static char	tmp[1024];
69326a875eaSnicm 
69426a875eaSnicm 	if (mode == 0)
6953e8355bdSnicm 		return ("NONE");
69626a875eaSnicm 	if (mode == ALL_MODES)
6973e8355bdSnicm 		return ("ALL");
69826a875eaSnicm 
69926a875eaSnicm 	*tmp = '\0';
70026a875eaSnicm 	if (mode & MODE_CURSOR)
70126a875eaSnicm 		strlcat(tmp, "CURSOR,", sizeof tmp);
70226a875eaSnicm 	if (mode & MODE_INSERT)
70326a875eaSnicm 		strlcat(tmp, "INSERT,", sizeof tmp);
70426a875eaSnicm 	if (mode & MODE_KCURSOR)
70526a875eaSnicm 		strlcat(tmp, "KCURSOR,", sizeof tmp);
70626a875eaSnicm 	if (mode & MODE_KKEYPAD)
70726a875eaSnicm 		strlcat(tmp, "KKEYPAD,", sizeof tmp);
70826a875eaSnicm 	if (mode & MODE_WRAP)
70926a875eaSnicm 		strlcat(tmp, "WRAP,", sizeof tmp);
71026a875eaSnicm 	if (mode & MODE_MOUSE_STANDARD)
711cb3c07dcSnicm 		strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp);
71226a875eaSnicm 	if (mode & MODE_MOUSE_BUTTON)
713cb3c07dcSnicm 		strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp);
71449b698acSnicm 	if (mode & MODE_CURSOR_BLINKING)
71549b698acSnicm 		strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp);
71649b698acSnicm 	if (mode & MODE_CURSOR_VERY_VISIBLE)
71749b698acSnicm 		strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp);
71826a875eaSnicm 	if (mode & MODE_MOUSE_UTF8)
7193f86e50fSnicm 		strlcat(tmp, "MOUSE_UTF8,", sizeof tmp);
72026a875eaSnicm 	if (mode & MODE_MOUSE_SGR)
7213f86e50fSnicm 		strlcat(tmp, "MOUSE_SGR,", sizeof tmp);
72226a875eaSnicm 	if (mode & MODE_BRACKETPASTE)
72326a875eaSnicm 		strlcat(tmp, "BRACKETPASTE,", sizeof tmp);
72426a875eaSnicm 	if (mode & MODE_FOCUSON)
72526a875eaSnicm 		strlcat(tmp, "FOCUSON,", sizeof tmp);
72626a875eaSnicm 	if (mode & MODE_MOUSE_ALL)
727cb3c07dcSnicm 		strlcat(tmp, "MOUSE_ALL,", sizeof tmp);
72826a875eaSnicm 	if (mode & MODE_ORIGIN)
72926a875eaSnicm 		strlcat(tmp, "ORIGIN,", sizeof tmp);
73026a875eaSnicm 	if (mode & MODE_CRLF)
73126a875eaSnicm 		strlcat(tmp, "CRLF,", sizeof tmp);
732719f5715Snicm 	if (mode & MODE_KEYS_EXTENDED)
733719f5715Snicm 		strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp);
734719f5715Snicm 	if (mode & MODE_KEYS_EXTENDED_2)
735719f5715Snicm 		strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp);
73626a875eaSnicm 	tmp[strlen(tmp) - 1] = '\0';
73726a875eaSnicm 	return (tmp);
73826a875eaSnicm }
739