xref: /netbsd-src/external/bsd/tmux/dist/screen.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
199e242abSchristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4f26e8bc9Schristos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv  *
6698d5317Sjmmv  * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv  * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv  * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv  *
10698d5317Sjmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv  */
18698d5317Sjmmv 
19698d5317Sjmmv #include <sys/types.h>
20698d5317Sjmmv 
21698d5317Sjmmv #include <stdlib.h>
22698d5317Sjmmv #include <string.h>
23698d5317Sjmmv #include <unistd.h>
24698d5317Sjmmv 
25698d5317Sjmmv #include "tmux.h"
26698d5317Sjmmv 
27c7e17de0Schristos /* Selected area in screen. */
28c7e17de0Schristos struct screen_sel {
29c7e17de0Schristos 	int		 hidden;
30c7e17de0Schristos 	int		 rectangle;
31c7e17de0Schristos 	int		 modekeys;
32c7e17de0Schristos 
33c7e17de0Schristos 	u_int		 sx;
34c7e17de0Schristos 	u_int		 sy;
35c7e17de0Schristos 
36c7e17de0Schristos 	u_int		 ex;
37c7e17de0Schristos 	u_int		 ey;
38c7e17de0Schristos 
39c7e17de0Schristos 	struct grid_cell cell;
40c7e17de0Schristos };
41c7e17de0Schristos 
42c7e17de0Schristos /* Entry on title stack. */
43c7e17de0Schristos struct screen_title_entry {
44c7e17de0Schristos 	char				*text;
45c7e17de0Schristos 
46c7e17de0Schristos 	TAILQ_ENTRY(screen_title_entry)	 entry;
47c7e17de0Schristos };
48c7e17de0Schristos TAILQ_HEAD(screen_titles, screen_title_entry);
49c7e17de0Schristos 
50e271dbb8Schristos static void	screen_resize_y(struct screen *, u_int, int, u_int *);
51e271dbb8Schristos static void	screen_reflow(struct screen *, u_int, u_int *, u_int *, int);
52698d5317Sjmmv 
53c7e17de0Schristos /* Free titles stack. */
54c7e17de0Schristos static void
55c7e17de0Schristos screen_free_titles(struct screen *s)
56c7e17de0Schristos {
57c7e17de0Schristos 	struct screen_title_entry	*title_entry;
58c7e17de0Schristos 
59c7e17de0Schristos 	if (s->titles == NULL)
60c7e17de0Schristos 		return;
61c7e17de0Schristos 
62c7e17de0Schristos 	while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) {
63c7e17de0Schristos 		TAILQ_REMOVE(s->titles, title_entry, entry);
64c7e17de0Schristos 		free(title_entry->text);
65c7e17de0Schristos 		free(title_entry);
66c7e17de0Schristos 	}
67c7e17de0Schristos 
68c7e17de0Schristos 	free(s->titles);
69c7e17de0Schristos 	s->titles = NULL;
70c7e17de0Schristos }
71c7e17de0Schristos 
72698d5317Sjmmv /* Create a new screen. */
73698d5317Sjmmv void
74698d5317Sjmmv screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
75698d5317Sjmmv {
76698d5317Sjmmv 	s->grid = grid_create(sx, sy, hlimit);
77e271dbb8Schristos 	s->saved_grid = NULL;
78e271dbb8Schristos 
79698d5317Sjmmv 	s->title = xstrdup("");
80c7e17de0Schristos 	s->titles = NULL;
81e271dbb8Schristos 	s->path = NULL;
82698d5317Sjmmv 
8346548964Swiz 	s->cstyle = SCREEN_CURSOR_DEFAULT;
8446548964Swiz 	s->default_cstyle = SCREEN_CURSOR_DEFAULT;
85f844e94eSwiz 	s->mode = MODE_CURSOR;
8646548964Swiz 	s->default_mode = 0;
8746548964Swiz 	s->ccolour = -1;
8846548964Swiz 	s->default_ccolour = -1;
89698d5317Sjmmv 	s->tabs = NULL;
90c7e17de0Schristos 	s->sel = NULL;
91698d5317Sjmmv 
92f844e94eSwiz #ifdef ENABLE_SIXEL
93f844e94eSwiz 	TAILQ_INIT(&s->images);
94f844e94eSwiz #endif
95f844e94eSwiz 
96e271dbb8Schristos 	s->write_list = NULL;
97f844e94eSwiz 	s->hyperlinks = NULL;
98e271dbb8Schristos 
99698d5317Sjmmv 	screen_reinit(s);
100698d5317Sjmmv }
101698d5317Sjmmv 
102698d5317Sjmmv /* Reinitialise screen. */
103698d5317Sjmmv void
104698d5317Sjmmv screen_reinit(struct screen *s)
105698d5317Sjmmv {
106698d5317Sjmmv 	s->cx = 0;
107698d5317Sjmmv 	s->cy = 0;
108698d5317Sjmmv 
109698d5317Sjmmv 	s->rupper = 0;
110698d5317Sjmmv 	s->rlower = screen_size_y(s) - 1;
111698d5317Sjmmv 
11246548964Swiz 	s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF);
113*890b6d91Swiz 
11459b94b2cSchristos 	if (options_get_number(global_options, "extended-keys") == 2)
115*890b6d91Swiz 		s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
116698d5317Sjmmv 
117e271dbb8Schristos 	if (s->saved_grid != NULL)
118e271dbb8Schristos 		screen_alternate_off(s, NULL, 0);
119e271dbb8Schristos 	s->saved_cx = UINT_MAX;
120e271dbb8Schristos 	s->saved_cy = UINT_MAX;
121e271dbb8Schristos 
122698d5317Sjmmv 	screen_reset_tabs(s);
123698d5317Sjmmv 
124e9a2d6faSchristos 	grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8);
125698d5317Sjmmv 
126698d5317Sjmmv 	screen_clear_selection(s);
127c7e17de0Schristos 	screen_free_titles(s);
128f844e94eSwiz 
129f844e94eSwiz #ifdef ENABLE_SIXEL
130f844e94eSwiz 	image_free_all(s);
131f844e94eSwiz #endif
132f844e94eSwiz 
133f844e94eSwiz 	screen_reset_hyperlinks(s);
134f844e94eSwiz }
135f844e94eSwiz 
136f844e94eSwiz /* Reset hyperlinks of a screen. */
137f844e94eSwiz void
138f844e94eSwiz screen_reset_hyperlinks(struct screen *s)
139f844e94eSwiz {
140f844e94eSwiz 	if (s->hyperlinks == NULL)
141f844e94eSwiz 		s->hyperlinks = hyperlinks_init();
142f844e94eSwiz 	else
143f844e94eSwiz 		hyperlinks_reset(s->hyperlinks);
144698d5317Sjmmv }
145698d5317Sjmmv 
146698d5317Sjmmv /* Destroy a screen. */
147698d5317Sjmmv void
148698d5317Sjmmv screen_free(struct screen *s)
149698d5317Sjmmv {
150c7e17de0Schristos 	free(s->sel);
15161fba46bSchristos 	free(s->tabs);
152e271dbb8Schristos 	free(s->path);
15361fba46bSchristos 	free(s->title);
154c7e17de0Schristos 
155e271dbb8Schristos 	if (s->write_list != NULL)
156e271dbb8Schristos 		screen_write_free_list(s);
157e271dbb8Schristos 
158e271dbb8Schristos 	if (s->saved_grid != NULL)
159e271dbb8Schristos 		grid_destroy(s->saved_grid);
160698d5317Sjmmv 	grid_destroy(s->grid);
161c7e17de0Schristos 
162f844e94eSwiz 	if (s->hyperlinks != NULL)
163f844e94eSwiz 		hyperlinks_free(s->hyperlinks);
164c7e17de0Schristos 	screen_free_titles(s);
165f844e94eSwiz 
166f844e94eSwiz #ifdef ENABLE_SIXEL
167f844e94eSwiz 	image_free_all(s);
168f844e94eSwiz #endif
169698d5317Sjmmv }
170698d5317Sjmmv 
171698d5317Sjmmv /* Reset tabs to default, eight spaces apart. */
172698d5317Sjmmv void
173698d5317Sjmmv screen_reset_tabs(struct screen *s)
174698d5317Sjmmv {
175698d5317Sjmmv 	u_int	i;
176698d5317Sjmmv 
17761fba46bSchristos 	free(s->tabs);
178698d5317Sjmmv 
179698d5317Sjmmv 	if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
180698d5317Sjmmv 		fatal("bit_alloc failed");
181698d5317Sjmmv 	for (i = 8; i < screen_size_x(s); i += 8)
182698d5317Sjmmv 		bit_set(s->tabs, i);
183698d5317Sjmmv }
184698d5317Sjmmv 
18546548964Swiz /* Set screen cursor style and mode. */
1860f3d2746Sjmmv void
18746548964Swiz screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle,
18846548964Swiz     int *mode)
1890f3d2746Sjmmv {
19046548964Swiz 	switch (style) {
19146548964Swiz 	case 0:
19246548964Swiz 		*cstyle = SCREEN_CURSOR_DEFAULT;
19346548964Swiz 		break;
19446548964Swiz 	case 1:
19546548964Swiz 		*cstyle = SCREEN_CURSOR_BLOCK;
19646548964Swiz 		*mode |= MODE_CURSOR_BLINKING;
19746548964Swiz 		break;
19846548964Swiz 	case 2:
19946548964Swiz 		*cstyle = SCREEN_CURSOR_BLOCK;
20046548964Swiz 		*mode &= ~MODE_CURSOR_BLINKING;
20146548964Swiz 		break;
20246548964Swiz 	case 3:
20346548964Swiz 		*cstyle = SCREEN_CURSOR_UNDERLINE;
20446548964Swiz 		*mode |= MODE_CURSOR_BLINKING;
20546548964Swiz 		break;
20646548964Swiz 	case 4:
20746548964Swiz 		*cstyle = SCREEN_CURSOR_UNDERLINE;
20846548964Swiz 		*mode &= ~MODE_CURSOR_BLINKING;
20946548964Swiz 		break;
21046548964Swiz 	case 5:
21146548964Swiz 		*cstyle = SCREEN_CURSOR_BAR;
21246548964Swiz 		*mode |= MODE_CURSOR_BLINKING;
21346548964Swiz 		break;
21446548964Swiz 	case 6:
21546548964Swiz 		*cstyle = SCREEN_CURSOR_BAR;
21646548964Swiz 		*mode &= ~MODE_CURSOR_BLINKING;
21746548964Swiz 		break;
218e271dbb8Schristos 	}
2190f3d2746Sjmmv }
2200f3d2746Sjmmv 
2210f3d2746Sjmmv /* Set screen cursor colour. */
2220f3d2746Sjmmv void
22346548964Swiz screen_set_cursor_colour(struct screen *s, int colour)
2240f3d2746Sjmmv {
22546548964Swiz 	s->ccolour = colour;
2260f3d2746Sjmmv }
2270f3d2746Sjmmv 
228698d5317Sjmmv /* Set screen title. */
22968e6ba84Schristos int
230698d5317Sjmmv screen_set_title(struct screen *s, const char *title)
231698d5317Sjmmv {
23268e6ba84Schristos 	if (!utf8_isvalid(title))
23368e6ba84Schristos 		return (0);
23461fba46bSchristos 	free(s->title);
23568e6ba84Schristos 	s->title = xstrdup(title);
23668e6ba84Schristos 	return (1);
23768e6ba84Schristos }
23868e6ba84Schristos 
23968e6ba84Schristos /* Set screen path. */
24068e6ba84Schristos void
24168e6ba84Schristos screen_set_path(struct screen *s, const char *path)
24268e6ba84Schristos {
24368e6ba84Schristos 	free(s->path);
24468e6ba84Schristos 	utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
245698d5317Sjmmv }
246698d5317Sjmmv 
247c7e17de0Schristos /* Push the current title onto the stack. */
248c7e17de0Schristos void
249c7e17de0Schristos screen_push_title(struct screen *s)
250c7e17de0Schristos {
251c7e17de0Schristos 	struct screen_title_entry *title_entry;
252c7e17de0Schristos 
253c7e17de0Schristos 	if (s->titles == NULL) {
254c7e17de0Schristos 		s->titles = xmalloc(sizeof *s->titles);
255c7e17de0Schristos 		TAILQ_INIT(s->titles);
256c7e17de0Schristos 	}
257c7e17de0Schristos 	title_entry = xmalloc(sizeof *title_entry);
258c7e17de0Schristos 	title_entry->text = xstrdup(s->title);
259c7e17de0Schristos 	TAILQ_INSERT_HEAD(s->titles, title_entry, entry);
260c7e17de0Schristos }
261c7e17de0Schristos 
262c7e17de0Schristos /*
263c7e17de0Schristos  * Pop a title from the stack and set it as the screen title. If the stack is
264c7e17de0Schristos  * empty, do nothing.
265c7e17de0Schristos  */
266c7e17de0Schristos void
267c7e17de0Schristos screen_pop_title(struct screen *s)
268c7e17de0Schristos {
269c7e17de0Schristos 	struct screen_title_entry *title_entry;
270c7e17de0Schristos 
271c7e17de0Schristos 	if (s->titles == NULL)
272c7e17de0Schristos 		return;
273c7e17de0Schristos 
274c7e17de0Schristos 	title_entry = TAILQ_FIRST(s->titles);
275c7e17de0Schristos 	if (title_entry != NULL) {
276c7e17de0Schristos 		screen_set_title(s, title_entry->text);
277c7e17de0Schristos 
278c7e17de0Schristos 		TAILQ_REMOVE(s->titles, title_entry, entry);
279c7e17de0Schristos 		free(title_entry->text);
280c7e17de0Schristos 		free(title_entry);
281c7e17de0Schristos 	}
282c7e17de0Schristos }
283c7e17de0Schristos 
284e271dbb8Schristos /* Resize screen with options. */
285698d5317Sjmmv void
286e271dbb8Schristos screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
287e271dbb8Schristos     int eat_empty, int cursor)
288698d5317Sjmmv {
289e271dbb8Schristos 	u_int	cx = s->cx, cy = s->grid->hsize + s->cy;
290e271dbb8Schristos 
291e271dbb8Schristos 	if (s->write_list != NULL)
292e271dbb8Schristos 		screen_write_free_list(s);
293e271dbb8Schristos 
294e271dbb8Schristos 	log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)",
295e271dbb8Schristos 	    __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy,
296e271dbb8Schristos 	    cx, cy);
297e271dbb8Schristos 
298698d5317Sjmmv 	if (sx < 1)
299698d5317Sjmmv 		sx = 1;
300698d5317Sjmmv 	if (sy < 1)
301698d5317Sjmmv 		sy = 1;
302698d5317Sjmmv 
303698d5317Sjmmv 	if (sx != screen_size_x(s)) {
3040a274e86Schristos 		s->grid->sx = sx;
305698d5317Sjmmv 		screen_reset_tabs(s);
306c7e17de0Schristos 	} else
307c7e17de0Schristos 		reflow = 0;
308698d5317Sjmmv 
309698d5317Sjmmv 	if (sy != screen_size_y(s))
310e271dbb8Schristos 		screen_resize_y(s, sy, eat_empty, &cy);
31161fba46bSchristos 
312f844e94eSwiz #ifdef ENABLE_SIXEL
313f844e94eSwiz 	image_free_all(s);
314f844e94eSwiz #endif
315*890b6d91Swiz 
316*890b6d91Swiz 	if (reflow)
317e271dbb8Schristos 		screen_reflow(s, sx, &cx, &cy, cursor);
318e271dbb8Schristos 
319e271dbb8Schristos 	if (cy >= s->grid->hsize) {
320e271dbb8Schristos 		s->cx = cx;
321e271dbb8Schristos 		s->cy = cy - s->grid->hsize;
322e271dbb8Schristos 	} else {
323e271dbb8Schristos 		s->cx = 0;
324e271dbb8Schristos 		s->cy = 0;
325e271dbb8Schristos 	}
326e271dbb8Schristos 
327e271dbb8Schristos 	log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx,
328e271dbb8Schristos 	    s->cy, cx, cy);
329e271dbb8Schristos 
330e271dbb8Schristos 	if (s->write_list != NULL)
331e271dbb8Schristos 		screen_write_make_list(s);
332e271dbb8Schristos }
333e271dbb8Schristos 
334e271dbb8Schristos /* Resize screen. */
335e271dbb8Schristos void
336e271dbb8Schristos screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
337e271dbb8Schristos {
338e271dbb8Schristos 	screen_resize_cursor(s, sx, sy, reflow, 1, 1);
339698d5317Sjmmv }
340698d5317Sjmmv 
341e9a2d6faSchristos static void
342e271dbb8Schristos screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy)
343698d5317Sjmmv {
344698d5317Sjmmv 	struct grid	*gd = s->grid;
345698d5317Sjmmv 	u_int		 needed, available, oldy, i;
346698d5317Sjmmv 
347698d5317Sjmmv 	if (sy == 0)
348698d5317Sjmmv 		fatalx("zero size");
349698d5317Sjmmv 	oldy = screen_size_y(s);
350698d5317Sjmmv 
351698d5317Sjmmv 	/*
352698d5317Sjmmv 	 * When resizing:
353698d5317Sjmmv 	 *
354698d5317Sjmmv 	 * If the height is decreasing, delete lines from the bottom until
355698d5317Sjmmv 	 * hitting the cursor, then push lines from the top into the history.
356698d5317Sjmmv 	 *
357e9a2d6faSchristos 	 * When increasing, pull as many lines as possible from scrolled
358e9a2d6faSchristos 	 * history (not explicitly cleared from view) to the top, then fill the
359e9a2d6faSchristos 	 * remaining with blanks at the bottom.
360698d5317Sjmmv 	 */
361698d5317Sjmmv 
362698d5317Sjmmv 	/* Size decreasing. */
363698d5317Sjmmv 	if (sy < oldy) {
364698d5317Sjmmv 		needed = oldy - sy;
365698d5317Sjmmv 
366698d5317Sjmmv 		/* Delete as many lines as possible from the bottom. */
367e271dbb8Schristos 		if (eat_empty) {
368698d5317Sjmmv 			available = oldy - 1 - s->cy;
369698d5317Sjmmv 			if (available > 0) {
370698d5317Sjmmv 				if (available > needed)
371698d5317Sjmmv 					available = needed;
372e271dbb8Schristos 				grid_view_delete_lines(gd, oldy - available,
373e271dbb8Schristos 				    available, 8);
374698d5317Sjmmv 			}
375698d5317Sjmmv 			needed -= available;
376e271dbb8Schristos 		}
377698d5317Sjmmv 
378698d5317Sjmmv 		/*
379698d5317Sjmmv 		 * Now just increase the history size, if possible, to take
380698d5317Sjmmv 		 * over the lines which are left. If history is off, delete
381698d5317Sjmmv 		 * lines from the top.
382698d5317Sjmmv 		 */
383698d5317Sjmmv 		available = s->cy;
384e9a2d6faSchristos 		if (gd->flags & GRID_HISTORY) {
385e9a2d6faSchristos 			gd->hscrolled += needed;
386698d5317Sjmmv 			gd->hsize += needed;
387e9a2d6faSchristos 		} else if (needed > 0 && available > 0) {
388698d5317Sjmmv 			if (available > needed)
389698d5317Sjmmv 				available = needed;
390e9a2d6faSchristos 			grid_view_delete_lines(gd, 0, available, 8);
391e271dbb8Schristos 			(*cy) -= available;
392698d5317Sjmmv 		}
393698d5317Sjmmv 	}
394698d5317Sjmmv 
395c7e17de0Schristos 	/* Resize line array. */
396c7e17de0Schristos 	grid_adjust_lines(gd, gd->hsize + sy);
397698d5317Sjmmv 
398698d5317Sjmmv 	/* Size increasing. */
399698d5317Sjmmv 	if (sy > oldy) {
400698d5317Sjmmv 		needed = sy - oldy;
401698d5317Sjmmv 
402698d5317Sjmmv 		/*
403e9a2d6faSchristos 		 * Try to pull as much as possible out of scrolled history, if
404*890b6d91Swiz 		 * it is enabled.
405698d5317Sjmmv 		 */
406e9a2d6faSchristos 		available = gd->hscrolled;
407698d5317Sjmmv 		if (gd->flags & GRID_HISTORY && available > 0) {
408698d5317Sjmmv 			if (available > needed)
409698d5317Sjmmv 				available = needed;
410e9a2d6faSchristos 			gd->hscrolled -= available;
411698d5317Sjmmv 			gd->hsize -= available;
412698d5317Sjmmv 		} else
413698d5317Sjmmv 			available = 0;
414698d5317Sjmmv 		needed -= available;
415698d5317Sjmmv 
416698d5317Sjmmv 		/* Then fill the rest in with blanks. */
417698d5317Sjmmv 		for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
418e271dbb8Schristos 			grid_empty_line(gd, i, 8);
419698d5317Sjmmv 	}
420698d5317Sjmmv 
421698d5317Sjmmv 	/* Set the new size, and reset the scroll region. */
422698d5317Sjmmv 	gd->sy = sy;
423698d5317Sjmmv 	s->rupper = 0;
424698d5317Sjmmv 	s->rlower = screen_size_y(s) - 1;
425698d5317Sjmmv }
426698d5317Sjmmv 
427698d5317Sjmmv /* Set selection. */
428698d5317Sjmmv void
429698d5317Sjmmv screen_set_selection(struct screen *s, u_int sx, u_int sy,
430c7e17de0Schristos     u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc)
431698d5317Sjmmv {
432c7e17de0Schristos 	if (s->sel == NULL)
433c7e17de0Schristos 		s->sel = xcalloc(1, sizeof *s->sel);
434698d5317Sjmmv 
435c7e17de0Schristos 	memcpy(&s->sel->cell, gc, sizeof s->sel->cell);
436c7e17de0Schristos 	s->sel->hidden = 0;
437c7e17de0Schristos 	s->sel->rectangle = rectangle;
438c7e17de0Schristos 	s->sel->modekeys = modekeys;
439e9a2d6faSchristos 
440c7e17de0Schristos 	s->sel->sx = sx;
441c7e17de0Schristos 	s->sel->sy = sy;
442c7e17de0Schristos 	s->sel->ex = ex;
443c7e17de0Schristos 	s->sel->ey = ey;
444698d5317Sjmmv }
445698d5317Sjmmv 
446698d5317Sjmmv /* Clear selection. */
447698d5317Sjmmv void
448698d5317Sjmmv screen_clear_selection(struct screen *s)
449698d5317Sjmmv {
450c7e17de0Schristos 	free(s->sel);
451c7e17de0Schristos 	s->sel = NULL;
452698d5317Sjmmv }
453698d5317Sjmmv 
454e9a2d6faSchristos /* Hide selection. */
455e9a2d6faSchristos void
456e9a2d6faSchristos screen_hide_selection(struct screen *s)
457e9a2d6faSchristos {
458c7e17de0Schristos 	if (s->sel != NULL)
459c7e17de0Schristos 		s->sel->hidden = 1;
460e9a2d6faSchristos }
461e9a2d6faSchristos 
462698d5317Sjmmv /* Check if cell in selection. */
463698d5317Sjmmv int
464698d5317Sjmmv screen_check_selection(struct screen *s, u_int px, u_int py)
465698d5317Sjmmv {
466c7e17de0Schristos 	struct screen_sel	*sel = s->sel;
46799e242abSchristos 	u_int			 xx;
468698d5317Sjmmv 
469c7e17de0Schristos 	if (sel == NULL || sel->hidden)
470698d5317Sjmmv 		return (0);
471698d5317Sjmmv 
472c7e17de0Schristos 	if (sel->rectangle) {
473698d5317Sjmmv 		if (sel->sy < sel->ey) {
474698d5317Sjmmv 			/* start line < end line -- downward selection. */
475698d5317Sjmmv 			if (py < sel->sy || py > sel->ey)
476698d5317Sjmmv 				return (0);
477698d5317Sjmmv 		} else if (sel->sy > sel->ey) {
478698d5317Sjmmv 			/* start line > end line -- upward selection. */
479698d5317Sjmmv 			if (py > sel->sy || py < sel->ey)
480698d5317Sjmmv 				return (0);
481698d5317Sjmmv 		} else {
482698d5317Sjmmv 			/* starting line == ending line. */
483698d5317Sjmmv 			if (py != sel->sy)
484698d5317Sjmmv 				return (0);
485698d5317Sjmmv 		}
486698d5317Sjmmv 
487698d5317Sjmmv 		/*
488698d5317Sjmmv 		 * Need to include the selection start row, but not the cursor
489698d5317Sjmmv 		 * row, which means the selection changes depending on which
490698d5317Sjmmv 		 * one is on the left.
491698d5317Sjmmv 		 */
492698d5317Sjmmv 		if (sel->ex < sel->sx) {
493698d5317Sjmmv 			/* Cursor (ex) is on the left. */
494698d5317Sjmmv 			if (px < sel->ex)
495698d5317Sjmmv 				return (0);
496698d5317Sjmmv 
497698d5317Sjmmv 			if (px > sel->sx)
498698d5317Sjmmv 				return (0);
499698d5317Sjmmv 		} else {
500698d5317Sjmmv 			/* Selection start (sx) is on the left. */
501698d5317Sjmmv 			if (px < sel->sx)
502698d5317Sjmmv 				return (0);
503698d5317Sjmmv 
504698d5317Sjmmv 			if (px > sel->ex)
505698d5317Sjmmv 				return (0);
506698d5317Sjmmv 		}
507698d5317Sjmmv 	} else {
508698d5317Sjmmv 		/*
509698d5317Sjmmv 		 * Like emacs, keep the top-left-most character, and drop the
510698d5317Sjmmv 		 * bottom-right-most, regardless of copy direction.
511698d5317Sjmmv 		 */
512698d5317Sjmmv 		if (sel->sy < sel->ey) {
513698d5317Sjmmv 			/* starting line < ending line -- downward selection. */
514698d5317Sjmmv 			if (py < sel->sy || py > sel->ey)
515698d5317Sjmmv 				return (0);
516698d5317Sjmmv 
51799e242abSchristos 			if (py == sel->sy && px < sel->sx)
51899e242abSchristos 				return (0);
51999e242abSchristos 
5200a274e86Schristos 			if (sel->modekeys == MODEKEY_EMACS)
5210a274e86Schristos 				xx = (sel->ex == 0 ? 0 : sel->ex - 1);
5220a274e86Schristos 			else
5230a274e86Schristos 				xx = sel->ex;
5240a274e86Schristos 			if (py == sel->ey && px > xx)
525698d5317Sjmmv 				return (0);
526698d5317Sjmmv 		} else if (sel->sy > sel->ey) {
527698d5317Sjmmv 			/* starting line > ending line -- upward selection. */
528698d5317Sjmmv 			if (py > sel->sy || py < sel->ey)
529698d5317Sjmmv 				return (0);
530698d5317Sjmmv 
53199e242abSchristos 			if (py == sel->ey && px < sel->ex)
53299e242abSchristos 				return (0);
53399e242abSchristos 
53499e242abSchristos 			if (sel->modekeys == MODEKEY_EMACS)
53599e242abSchristos 				xx = sel->sx - 1;
53699e242abSchristos 			else
53799e242abSchristos 				xx = sel->sx;
538e9a2d6faSchristos 			if (py == sel->sy && (sel->sx == 0 || px > xx))
539698d5317Sjmmv 				return (0);
540698d5317Sjmmv 		} else {
541698d5317Sjmmv 			/* starting line == ending line. */
542698d5317Sjmmv 			if (py != sel->sy)
543698d5317Sjmmv 				return (0);
544698d5317Sjmmv 
545698d5317Sjmmv 			if (sel->ex < sel->sx) {
546698d5317Sjmmv 				/* cursor (ex) is on the left */
54799e242abSchristos 				if (sel->modekeys == MODEKEY_EMACS)
54899e242abSchristos 					xx = sel->sx - 1;
54999e242abSchristos 				else
55099e242abSchristos 					xx = sel->sx;
55199e242abSchristos 				if (px > xx || px < sel->ex)
552698d5317Sjmmv 					return (0);
553698d5317Sjmmv 			} else {
554698d5317Sjmmv 				/* selection start (sx) is on the left */
5550a274e86Schristos 				if (sel->modekeys == MODEKEY_EMACS)
5560a274e86Schristos 					xx = (sel->ex == 0 ? 0 : sel->ex - 1);
5570a274e86Schristos 				else
5580a274e86Schristos 					xx = sel->ex;
5590a274e86Schristos 				if (px < sel->sx || px > xx)
560698d5317Sjmmv 					return (0);
561698d5317Sjmmv 			}
562698d5317Sjmmv 		}
563698d5317Sjmmv 	}
564698d5317Sjmmv 
565698d5317Sjmmv 	return (1);
566698d5317Sjmmv }
56761fba46bSchristos 
568e9a2d6faSchristos /* Get selected grid cell. */
56961fba46bSchristos void
570e9a2d6faSchristos screen_select_cell(struct screen *s, struct grid_cell *dst,
571e9a2d6faSchristos     const struct grid_cell *src)
572e9a2d6faSchristos {
573c7e17de0Schristos 	if (s->sel == NULL || s->sel->hidden)
574e9a2d6faSchristos 		return;
575e9a2d6faSchristos 
576c7e17de0Schristos 	memcpy(dst, &s->sel->cell, sizeof *dst);
577e9a2d6faSchristos 
578e9a2d6faSchristos 	utf8_copy(&dst->data, &src->data);
579e9a2d6faSchristos 	dst->attr = dst->attr & ~GRID_ATTR_CHARSET;
580e9a2d6faSchristos 	dst->attr |= src->attr & GRID_ATTR_CHARSET;
581e9a2d6faSchristos 	dst->flags = src->flags;
582e9a2d6faSchristos }
583e9a2d6faSchristos 
584e9a2d6faSchristos /* Reflow wrapped lines. */
585e9a2d6faSchristos static void
586e271dbb8Schristos screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor)
58761fba46bSchristos {
588e271dbb8Schristos 	u_int	wx, wy;
5890a274e86Schristos 
590e271dbb8Schristos 	if (cursor) {
591e271dbb8Schristos 		grid_wrap_position(s->grid, *cx, *cy, &wx, &wy);
592e271dbb8Schristos 		log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx,
593e271dbb8Schristos 		    wy);
594e271dbb8Schristos 	}
5950a274e86Schristos 
5960a274e86Schristos 	grid_reflow(s->grid, new_x);
5970a274e86Schristos 
598e271dbb8Schristos 	if (cursor) {
599e271dbb8Schristos 		grid_unwrap_position(s->grid, cx, cy, wx, wy);
600e271dbb8Schristos 		log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy);
601e271dbb8Schristos 	}
602e271dbb8Schristos 	else {
603e271dbb8Schristos 		*cx = 0;
604e271dbb8Schristos 		*cy = s->grid->hsize;
605e271dbb8Schristos 	}
6060a274e86Schristos }
6070a274e86Schristos 
608e271dbb8Schristos /*
609e271dbb8Schristos  * Enter alternative screen mode. A copy of the visible screen is saved and the
610e271dbb8Schristos  * history is not updated.
611e271dbb8Schristos  */
612e271dbb8Schristos void
613e271dbb8Schristos screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
614e271dbb8Schristos {
615e271dbb8Schristos 	u_int	sx, sy;
6160a274e86Schristos 
617e271dbb8Schristos 	if (s->saved_grid != NULL)
618e271dbb8Schristos 		return;
619e271dbb8Schristos 	sx = screen_size_x(s);
620e271dbb8Schristos 	sy = screen_size_y(s);
621e271dbb8Schristos 
622e271dbb8Schristos 	s->saved_grid = grid_create(sx, sy, 0);
623e271dbb8Schristos 	grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy);
624e271dbb8Schristos 	if (cursor) {
625e271dbb8Schristos 		s->saved_cx = s->cx;
626e271dbb8Schristos 		s->saved_cy = s->cy;
627e271dbb8Schristos 	}
628e271dbb8Schristos 	memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
629e271dbb8Schristos 
630e271dbb8Schristos 	grid_view_clear(s->grid, 0, 0, sx, sy, 8);
631e271dbb8Schristos 
632e271dbb8Schristos 	s->saved_flags = s->grid->flags;
633e271dbb8Schristos 	s->grid->flags &= ~GRID_HISTORY;
634e271dbb8Schristos }
635e271dbb8Schristos 
636e271dbb8Schristos /* Exit alternate screen mode and restore the copied grid. */
637e271dbb8Schristos void
638e271dbb8Schristos screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
639e271dbb8Schristos {
640e271dbb8Schristos 	u_int	sx = screen_size_x(s), sy = screen_size_y(s);
641e271dbb8Schristos 
642e271dbb8Schristos 	/*
643e271dbb8Schristos 	 * If the current size is different, temporarily resize to the old size
644e271dbb8Schristos 	 * before copying back.
645e271dbb8Schristos 	 */
646e271dbb8Schristos 	if (s->saved_grid != NULL)
647f844e94eSwiz 		screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0);
648e271dbb8Schristos 
649e271dbb8Schristos 	/*
650e271dbb8Schristos 	 * Restore the cursor position and cell. This happens even if not
651e271dbb8Schristos 	 * currently in the alternate screen.
652e271dbb8Schristos 	 */
653e271dbb8Schristos 	if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) {
654e271dbb8Schristos 		s->cx = s->saved_cx;
655e271dbb8Schristos 		s->cy = s->saved_cy;
656e271dbb8Schristos 		if (gc != NULL)
657e271dbb8Schristos 			memcpy(gc, &s->saved_cell, sizeof *gc);
658e271dbb8Schristos 	}
659e271dbb8Schristos 
660e271dbb8Schristos 	/* If not in the alternate screen, do nothing more. */
661e271dbb8Schristos 	if (s->saved_grid == NULL) {
662e271dbb8Schristos 		if (s->cx > screen_size_x(s) - 1)
663e271dbb8Schristos 			s->cx = screen_size_x(s) - 1;
664e271dbb8Schristos 		if (s->cy > screen_size_y(s) - 1)
665e271dbb8Schristos 			s->cy = screen_size_y(s) - 1;
666e271dbb8Schristos 		return;
667e271dbb8Schristos 	}
668e271dbb8Schristos 
669e271dbb8Schristos 	/* Restore the saved grid. */
670e271dbb8Schristos 	grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
671e271dbb8Schristos 	    s->saved_grid->sy);
672e271dbb8Schristos 
673e271dbb8Schristos 	/*
674e271dbb8Schristos 	 * Turn history back on (so resize can use it) and then resize back to
675e271dbb8Schristos 	 * the current size.
676e271dbb8Schristos 	 */
677e271dbb8Schristos 	if (s->saved_flags & GRID_HISTORY)
678e271dbb8Schristos 		s->grid->flags |= GRID_HISTORY;
679e271dbb8Schristos 	screen_resize(s, sx, sy, 1);
680e271dbb8Schristos 
681e271dbb8Schristos 	grid_destroy(s->saved_grid);
682e271dbb8Schristos 	s->saved_grid = NULL;
683e271dbb8Schristos 
684e271dbb8Schristos 	if (s->cx > screen_size_x(s) - 1)
685e271dbb8Schristos 		s->cx = screen_size_x(s) - 1;
686e271dbb8Schristos 	if (s->cy > screen_size_y(s) - 1)
687e271dbb8Schristos 		s->cy = screen_size_y(s) - 1;
68861fba46bSchristos }
68946548964Swiz 
69046548964Swiz /* Get mode as a string. */
69146548964Swiz const char *
69246548964Swiz screen_mode_to_string(int mode)
69346548964Swiz {
69446548964Swiz 	static char	tmp[1024];
69546548964Swiz 
69646548964Swiz 	if (mode == 0)
69746548964Swiz 		return ("NONE");
69846548964Swiz 	if (mode == ALL_MODES)
69946548964Swiz 		return ("ALL");
70046548964Swiz 
70146548964Swiz 	*tmp = '\0';
70246548964Swiz 	if (mode & MODE_CURSOR)
70346548964Swiz 		strlcat(tmp, "CURSOR,", sizeof tmp);
70446548964Swiz 	if (mode & MODE_INSERT)
70546548964Swiz 		strlcat(tmp, "INSERT,", sizeof tmp);
70646548964Swiz 	if (mode & MODE_KCURSOR)
70746548964Swiz 		strlcat(tmp, "KCURSOR,", sizeof tmp);
70846548964Swiz 	if (mode & MODE_KKEYPAD)
70946548964Swiz 		strlcat(tmp, "KKEYPAD,", sizeof tmp);
71046548964Swiz 	if (mode & MODE_WRAP)
71146548964Swiz 		strlcat(tmp, "WRAP,", sizeof tmp);
71246548964Swiz 	if (mode & MODE_MOUSE_STANDARD)
71346548964Swiz 		strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp);
71446548964Swiz 	if (mode & MODE_MOUSE_BUTTON)
71546548964Swiz 		strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp);
71646548964Swiz 	if (mode & MODE_CURSOR_BLINKING)
71746548964Swiz 		strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp);
71846548964Swiz 	if (mode & MODE_CURSOR_VERY_VISIBLE)
71946548964Swiz 		strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp);
72046548964Swiz 	if (mode & MODE_MOUSE_UTF8)
721f844e94eSwiz 		strlcat(tmp, "MOUSE_UTF8,", sizeof tmp);
72246548964Swiz 	if (mode & MODE_MOUSE_SGR)
723f844e94eSwiz 		strlcat(tmp, "MOUSE_SGR,", sizeof tmp);
72446548964Swiz 	if (mode & MODE_BRACKETPASTE)
72546548964Swiz 		strlcat(tmp, "BRACKETPASTE,", sizeof tmp);
72646548964Swiz 	if (mode & MODE_FOCUSON)
72746548964Swiz 		strlcat(tmp, "FOCUSON,", sizeof tmp);
72846548964Swiz 	if (mode & MODE_MOUSE_ALL)
72946548964Swiz 		strlcat(tmp, "MOUSE_ALL,", sizeof tmp);
73046548964Swiz 	if (mode & MODE_ORIGIN)
73146548964Swiz 		strlcat(tmp, "ORIGIN,", sizeof tmp);
73246548964Swiz 	if (mode & MODE_CRLF)
73346548964Swiz 		strlcat(tmp, "CRLF,", sizeof tmp);
734*890b6d91Swiz 	if (mode & MODE_KEYS_EXTENDED)
735*890b6d91Swiz 		strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp);
736*890b6d91Swiz 	if (mode & MODE_KEYS_EXTENDED_2)
737*890b6d91Swiz 		strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp);
73846548964Swiz 	tmp[strlen(tmp) - 1] = '\0';
73946548964Swiz 	return (tmp);
74046548964Swiz }
741