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