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