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