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