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