1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #include "tmux.h" 26 27 /* Selected area in screen. */ 28 struct screen_sel { 29 int hidden; 30 int rectangle; 31 int modekeys; 32 33 u_int sx; 34 u_int sy; 35 36 u_int ex; 37 u_int ey; 38 39 struct grid_cell cell; 40 }; 41 42 /* Entry on title stack. */ 43 struct screen_title_entry { 44 char *text; 45 46 TAILQ_ENTRY(screen_title_entry) entry; 47 }; 48 TAILQ_HEAD(screen_titles, screen_title_entry); 49 50 static void screen_resize_y(struct screen *, u_int, int, u_int *); 51 static void screen_reflow(struct screen *, u_int, u_int *, u_int *, int); 52 53 /* Free titles stack. */ 54 static void 55 screen_free_titles(struct screen *s) 56 { 57 struct screen_title_entry *title_entry; 58 59 if (s->titles == NULL) 60 return; 61 62 while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) { 63 TAILQ_REMOVE(s->titles, title_entry, entry); 64 free(title_entry->text); 65 free(title_entry); 66 } 67 68 free(s->titles); 69 s->titles = NULL; 70 } 71 72 /* Create a new screen. */ 73 void 74 screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) 75 { 76 s->grid = grid_create(sx, sy, hlimit); 77 s->saved_grid = NULL; 78 79 s->title = xstrdup(""); 80 s->titles = NULL; 81 s->path = NULL; 82 83 s->cstyle = SCREEN_CURSOR_DEFAULT; 84 s->default_cstyle = SCREEN_CURSOR_DEFAULT; 85 s->mode = MODE_CURSOR; 86 s->default_mode = 0; 87 s->ccolour = -1; 88 s->default_ccolour = -1; 89 s->tabs = NULL; 90 s->sel = NULL; 91 92 #ifdef ENABLE_SIXEL 93 TAILQ_INIT(&s->images); 94 #endif 95 96 s->write_list = NULL; 97 s->hyperlinks = NULL; 98 99 screen_reinit(s); 100 } 101 102 /* Reinitialise screen. */ 103 void 104 screen_reinit(struct screen *s) 105 { 106 s->cx = 0; 107 s->cy = 0; 108 109 s->rupper = 0; 110 s->rlower = screen_size_y(s) - 1; 111 112 s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF); 113 114 if (options_get_number(global_options, "extended-keys") == 2) 115 s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED; 116 117 if (s->saved_grid != NULL) 118 screen_alternate_off(s, NULL, 0); 119 s->saved_cx = UINT_MAX; 120 s->saved_cy = UINT_MAX; 121 122 screen_reset_tabs(s); 123 124 grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); 125 126 screen_clear_selection(s); 127 screen_free_titles(s); 128 129 #ifdef ENABLE_SIXEL 130 image_free_all(s); 131 #endif 132 133 screen_reset_hyperlinks(s); 134 } 135 136 /* Reset hyperlinks of a screen. */ 137 void 138 screen_reset_hyperlinks(struct screen *s) 139 { 140 if (s->hyperlinks == NULL) 141 s->hyperlinks = hyperlinks_init(); 142 else 143 hyperlinks_reset(s->hyperlinks); 144 } 145 146 /* Destroy a screen. */ 147 void 148 screen_free(struct screen *s) 149 { 150 free(s->sel); 151 free(s->tabs); 152 free(s->path); 153 free(s->title); 154 155 if (s->write_list != NULL) 156 screen_write_free_list(s); 157 158 if (s->saved_grid != NULL) 159 grid_destroy(s->saved_grid); 160 grid_destroy(s->grid); 161 162 if (s->hyperlinks != NULL) 163 hyperlinks_free(s->hyperlinks); 164 screen_free_titles(s); 165 166 #ifdef ENABLE_SIXEL 167 image_free_all(s); 168 #endif 169 } 170 171 /* Reset tabs to default, eight spaces apart. */ 172 void 173 screen_reset_tabs(struct screen *s) 174 { 175 u_int i; 176 177 free(s->tabs); 178 179 if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL) 180 fatal("bit_alloc failed"); 181 for (i = 8; i < screen_size_x(s); i += 8) 182 bit_set(s->tabs, i); 183 } 184 185 /* Set screen cursor style and mode. */ 186 void 187 screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle, 188 int *mode) 189 { 190 switch (style) { 191 case 0: 192 *cstyle = SCREEN_CURSOR_DEFAULT; 193 break; 194 case 1: 195 *cstyle = SCREEN_CURSOR_BLOCK; 196 *mode |= MODE_CURSOR_BLINKING; 197 break; 198 case 2: 199 *cstyle = SCREEN_CURSOR_BLOCK; 200 *mode &= ~MODE_CURSOR_BLINKING; 201 break; 202 case 3: 203 *cstyle = SCREEN_CURSOR_UNDERLINE; 204 *mode |= MODE_CURSOR_BLINKING; 205 break; 206 case 4: 207 *cstyle = SCREEN_CURSOR_UNDERLINE; 208 *mode &= ~MODE_CURSOR_BLINKING; 209 break; 210 case 5: 211 *cstyle = SCREEN_CURSOR_BAR; 212 *mode |= MODE_CURSOR_BLINKING; 213 break; 214 case 6: 215 *cstyle = SCREEN_CURSOR_BAR; 216 *mode &= ~MODE_CURSOR_BLINKING; 217 break; 218 } 219 } 220 221 /* Set screen cursor colour. */ 222 void 223 screen_set_cursor_colour(struct screen *s, int colour) 224 { 225 s->ccolour = colour; 226 } 227 228 /* Set screen title. */ 229 int 230 screen_set_title(struct screen *s, const char *title) 231 { 232 if (!utf8_isvalid(title)) 233 return (0); 234 free(s->title); 235 s->title = xstrdup(title); 236 return (1); 237 } 238 239 /* Set screen path. */ 240 void 241 screen_set_path(struct screen *s, const char *path) 242 { 243 free(s->path); 244 utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); 245 } 246 247 /* Push the current title onto the stack. */ 248 void 249 screen_push_title(struct screen *s) 250 { 251 struct screen_title_entry *title_entry; 252 253 if (s->titles == NULL) { 254 s->titles = xmalloc(sizeof *s->titles); 255 TAILQ_INIT(s->titles); 256 } 257 title_entry = xmalloc(sizeof *title_entry); 258 title_entry->text = xstrdup(s->title); 259 TAILQ_INSERT_HEAD(s->titles, title_entry, entry); 260 } 261 262 /* 263 * Pop a title from the stack and set it as the screen title. If the stack is 264 * empty, do nothing. 265 */ 266 void 267 screen_pop_title(struct screen *s) 268 { 269 struct screen_title_entry *title_entry; 270 271 if (s->titles == NULL) 272 return; 273 274 title_entry = TAILQ_FIRST(s->titles); 275 if (title_entry != NULL) { 276 screen_set_title(s, title_entry->text); 277 278 TAILQ_REMOVE(s->titles, title_entry, entry); 279 free(title_entry->text); 280 free(title_entry); 281 } 282 } 283 284 /* Resize screen with options. */ 285 void 286 screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, 287 int eat_empty, int cursor) 288 { 289 u_int cx = s->cx, cy = s->grid->hsize + s->cy; 290 291 if (s->write_list != NULL) 292 screen_write_free_list(s); 293 294 log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", 295 __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, 296 cx, cy); 297 298 if (sx < 1) 299 sx = 1; 300 if (sy < 1) 301 sy = 1; 302 303 if (sx != screen_size_x(s)) { 304 s->grid->sx = sx; 305 screen_reset_tabs(s); 306 } else 307 reflow = 0; 308 309 if (sy != screen_size_y(s)) 310 screen_resize_y(s, sy, eat_empty, &cy); 311 312 #ifdef ENABLE_SIXEL 313 image_free_all(s); 314 #endif 315 316 if (reflow) 317 screen_reflow(s, sx, &cx, &cy, cursor); 318 319 if (cy >= s->grid->hsize) { 320 s->cx = cx; 321 s->cy = cy - s->grid->hsize; 322 } else { 323 s->cx = 0; 324 s->cy = 0; 325 } 326 327 log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, 328 s->cy, cx, cy); 329 330 if (s->write_list != NULL) 331 screen_write_make_list(s); 332 } 333 334 /* Resize screen. */ 335 void 336 screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) 337 { 338 screen_resize_cursor(s, sx, sy, reflow, 1, 1); 339 } 340 341 static void 342 screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy) 343 { 344 struct grid *gd = s->grid; 345 u_int needed, available, oldy, i; 346 347 if (sy == 0) 348 fatalx("zero size"); 349 oldy = screen_size_y(s); 350 351 /* 352 * When resizing: 353 * 354 * If the height is decreasing, delete lines from the bottom until 355 * hitting the cursor, then push lines from the top into the history. 356 * 357 * When increasing, pull as many lines as possible from scrolled 358 * history (not explicitly cleared from view) to the top, then fill the 359 * remaining with blanks at the bottom. 360 */ 361 362 /* Size decreasing. */ 363 if (sy < oldy) { 364 needed = oldy - sy; 365 366 /* Delete as many lines as possible from the bottom. */ 367 if (eat_empty) { 368 available = oldy - 1 - s->cy; 369 if (available > 0) { 370 if (available > needed) 371 available = needed; 372 grid_view_delete_lines(gd, oldy - available, 373 available, 8); 374 } 375 needed -= available; 376 } 377 378 /* 379 * Now just increase the history size, if possible, to take 380 * over the lines which are left. If history is off, delete 381 * lines from the top. 382 */ 383 available = s->cy; 384 if (gd->flags & GRID_HISTORY) { 385 gd->hscrolled += needed; 386 gd->hsize += needed; 387 } else if (needed > 0 && available > 0) { 388 if (available > needed) 389 available = needed; 390 grid_view_delete_lines(gd, 0, available, 8); 391 (*cy) -= available; 392 } 393 } 394 395 /* Resize line array. */ 396 grid_adjust_lines(gd, gd->hsize + sy); 397 398 /* Size increasing. */ 399 if (sy > oldy) { 400 needed = sy - oldy; 401 402 /* 403 * Try to pull as much as possible out of scrolled history, if 404 * it is enabled. 405 */ 406 available = gd->hscrolled; 407 if (gd->flags & GRID_HISTORY && available > 0) { 408 if (available > needed) 409 available = needed; 410 gd->hscrolled -= available; 411 gd->hsize -= available; 412 } else 413 available = 0; 414 needed -= available; 415 416 /* Then fill the rest in with blanks. */ 417 for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) 418 grid_empty_line(gd, i, 8); 419 } 420 421 /* Set the new size, and reset the scroll region. */ 422 gd->sy = sy; 423 s->rupper = 0; 424 s->rlower = screen_size_y(s) - 1; 425 } 426 427 /* Set selection. */ 428 void 429 screen_set_selection(struct screen *s, u_int sx, u_int sy, 430 u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc) 431 { 432 if (s->sel == NULL) 433 s->sel = xcalloc(1, sizeof *s->sel); 434 435 memcpy(&s->sel->cell, gc, sizeof s->sel->cell); 436 s->sel->hidden = 0; 437 s->sel->rectangle = rectangle; 438 s->sel->modekeys = modekeys; 439 440 s->sel->sx = sx; 441 s->sel->sy = sy; 442 s->sel->ex = ex; 443 s->sel->ey = ey; 444 } 445 446 /* Clear selection. */ 447 void 448 screen_clear_selection(struct screen *s) 449 { 450 free(s->sel); 451 s->sel = NULL; 452 } 453 454 /* Hide selection. */ 455 void 456 screen_hide_selection(struct screen *s) 457 { 458 if (s->sel != NULL) 459 s->sel->hidden = 1; 460 } 461 462 /* Check if cell in selection. */ 463 int 464 screen_check_selection(struct screen *s, u_int px, u_int py) 465 { 466 struct screen_sel *sel = s->sel; 467 u_int xx; 468 469 if (sel == NULL || sel->hidden) 470 return (0); 471 472 if (sel->rectangle) { 473 if (sel->sy < sel->ey) { 474 /* start line < end line -- downward selection. */ 475 if (py < sel->sy || py > sel->ey) 476 return (0); 477 } else if (sel->sy > sel->ey) { 478 /* start line > end line -- upward selection. */ 479 if (py > sel->sy || py < sel->ey) 480 return (0); 481 } else { 482 /* starting line == ending line. */ 483 if (py != sel->sy) 484 return (0); 485 } 486 487 /* 488 * Need to include the selection start row, but not the cursor 489 * row, which means the selection changes depending on which 490 * one is on the left. 491 */ 492 if (sel->ex < sel->sx) { 493 /* Cursor (ex) is on the left. */ 494 if (px < sel->ex) 495 return (0); 496 497 if (px > sel->sx) 498 return (0); 499 } else { 500 /* Selection start (sx) is on the left. */ 501 if (px < sel->sx) 502 return (0); 503 504 if (px > sel->ex) 505 return (0); 506 } 507 } else { 508 /* 509 * Like emacs, keep the top-left-most character, and drop the 510 * bottom-right-most, regardless of copy direction. 511 */ 512 if (sel->sy < sel->ey) { 513 /* starting line < ending line -- downward selection. */ 514 if (py < sel->sy || py > sel->ey) 515 return (0); 516 517 if (py == sel->sy && px < sel->sx) 518 return (0); 519 520 if (sel->modekeys == MODEKEY_EMACS) 521 xx = (sel->ex == 0 ? 0 : sel->ex - 1); 522 else 523 xx = sel->ex; 524 if (py == sel->ey && px > xx) 525 return (0); 526 } else if (sel->sy > sel->ey) { 527 /* starting line > ending line -- upward selection. */ 528 if (py > sel->sy || py < sel->ey) 529 return (0); 530 531 if (py == sel->ey && px < sel->ex) 532 return (0); 533 534 if (sel->modekeys == MODEKEY_EMACS) 535 xx = sel->sx - 1; 536 else 537 xx = sel->sx; 538 if (py == sel->sy && (sel->sx == 0 || px > xx)) 539 return (0); 540 } else { 541 /* starting line == ending line. */ 542 if (py != sel->sy) 543 return (0); 544 545 if (sel->ex < sel->sx) { 546 /* cursor (ex) is on the left */ 547 if (sel->modekeys == MODEKEY_EMACS) 548 xx = sel->sx - 1; 549 else 550 xx = sel->sx; 551 if (px > xx || px < sel->ex) 552 return (0); 553 } else { 554 /* selection start (sx) is on the left */ 555 if (sel->modekeys == MODEKEY_EMACS) 556 xx = (sel->ex == 0 ? 0 : sel->ex - 1); 557 else 558 xx = sel->ex; 559 if (px < sel->sx || px > xx) 560 return (0); 561 } 562 } 563 } 564 565 return (1); 566 } 567 568 /* Get selected grid cell. */ 569 void 570 screen_select_cell(struct screen *s, struct grid_cell *dst, 571 const struct grid_cell *src) 572 { 573 if (s->sel == NULL || s->sel->hidden) 574 return; 575 576 memcpy(dst, &s->sel->cell, sizeof *dst); 577 578 utf8_copy(&dst->data, &src->data); 579 dst->attr = dst->attr & ~GRID_ATTR_CHARSET; 580 dst->attr |= src->attr & GRID_ATTR_CHARSET; 581 dst->flags = src->flags; 582 } 583 584 /* Reflow wrapped lines. */ 585 static void 586 screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) 587 { 588 u_int wx, wy; 589 590 if (cursor) { 591 grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); 592 log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, 593 wy); 594 } 595 596 grid_reflow(s->grid, new_x); 597 598 if (cursor) { 599 grid_unwrap_position(s->grid, cx, cy, wx, wy); 600 log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy); 601 } 602 else { 603 *cx = 0; 604 *cy = s->grid->hsize; 605 } 606 } 607 608 /* 609 * Enter alternative screen mode. A copy of the visible screen is saved and the 610 * history is not updated. 611 */ 612 void 613 screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) 614 { 615 u_int sx, sy; 616 617 if (s->saved_grid != NULL) 618 return; 619 sx = screen_size_x(s); 620 sy = screen_size_y(s); 621 622 s->saved_grid = grid_create(sx, sy, 0); 623 grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy); 624 if (cursor) { 625 s->saved_cx = s->cx; 626 s->saved_cy = s->cy; 627 } 628 memcpy(&s->saved_cell, gc, sizeof s->saved_cell); 629 630 grid_view_clear(s->grid, 0, 0, sx, sy, 8); 631 632 s->saved_flags = s->grid->flags; 633 s->grid->flags &= ~GRID_HISTORY; 634 } 635 636 /* Exit alternate screen mode and restore the copied grid. */ 637 void 638 screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) 639 { 640 u_int sx = screen_size_x(s), sy = screen_size_y(s); 641 642 /* 643 * If the current size is different, temporarily resize to the old size 644 * before copying back. 645 */ 646 if (s->saved_grid != NULL) 647 screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0); 648 649 /* 650 * Restore the cursor position and cell. This happens even if not 651 * currently in the alternate screen. 652 */ 653 if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { 654 s->cx = s->saved_cx; 655 s->cy = s->saved_cy; 656 if (gc != NULL) 657 memcpy(gc, &s->saved_cell, sizeof *gc); 658 } 659 660 /* If not in the alternate screen, do nothing more. */ 661 if (s->saved_grid == NULL) { 662 if (s->cx > screen_size_x(s) - 1) 663 s->cx = screen_size_x(s) - 1; 664 if (s->cy > screen_size_y(s) - 1) 665 s->cy = screen_size_y(s) - 1; 666 return; 667 } 668 669 /* Restore the saved grid. */ 670 grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, 671 s->saved_grid->sy); 672 673 /* 674 * Turn history back on (so resize can use it) and then resize back to 675 * the current size. 676 */ 677 if (s->saved_flags & GRID_HISTORY) 678 s->grid->flags |= GRID_HISTORY; 679 screen_resize(s, sx, sy, 1); 680 681 grid_destroy(s->saved_grid); 682 s->saved_grid = NULL; 683 684 if (s->cx > screen_size_x(s) - 1) 685 s->cx = screen_size_x(s) - 1; 686 if (s->cy > screen_size_y(s) - 1) 687 s->cy = screen_size_y(s) - 1; 688 } 689 690 /* Get mode as a string. */ 691 const char * 692 screen_mode_to_string(int mode) 693 { 694 static char tmp[1024]; 695 696 if (mode == 0) 697 return ("NONE"); 698 if (mode == ALL_MODES) 699 return ("ALL"); 700 701 *tmp = '\0'; 702 if (mode & MODE_CURSOR) 703 strlcat(tmp, "CURSOR,", sizeof tmp); 704 if (mode & MODE_INSERT) 705 strlcat(tmp, "INSERT,", sizeof tmp); 706 if (mode & MODE_KCURSOR) 707 strlcat(tmp, "KCURSOR,", sizeof tmp); 708 if (mode & MODE_KKEYPAD) 709 strlcat(tmp, "KKEYPAD,", sizeof tmp); 710 if (mode & MODE_WRAP) 711 strlcat(tmp, "WRAP,", sizeof tmp); 712 if (mode & MODE_MOUSE_STANDARD) 713 strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp); 714 if (mode & MODE_MOUSE_BUTTON) 715 strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp); 716 if (mode & MODE_CURSOR_BLINKING) 717 strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp); 718 if (mode & MODE_CURSOR_VERY_VISIBLE) 719 strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp); 720 if (mode & MODE_MOUSE_UTF8) 721 strlcat(tmp, "MOUSE_UTF8,", sizeof tmp); 722 if (mode & MODE_MOUSE_SGR) 723 strlcat(tmp, "MOUSE_SGR,", sizeof tmp); 724 if (mode & MODE_BRACKETPASTE) 725 strlcat(tmp, "BRACKETPASTE,", sizeof tmp); 726 if (mode & MODE_FOCUSON) 727 strlcat(tmp, "FOCUSON,", sizeof tmp); 728 if (mode & MODE_MOUSE_ALL) 729 strlcat(tmp, "MOUSE_ALL,", sizeof tmp); 730 if (mode & MODE_ORIGIN) 731 strlcat(tmp, "ORIGIN,", sizeof tmp); 732 if (mode & MODE_CRLF) 733 strlcat(tmp, "CRLF,", sizeof tmp); 734 if (mode & MODE_KEYS_EXTENDED) 735 strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp); 736 if (mode & MODE_KEYS_EXTENDED_2) 737 strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp); 738 tmp[strlen(tmp) - 1] = '\0'; 739 return (tmp); 740 } 741