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