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_x(struct screen *, u_int); 51 static void screen_resize_y(struct screen *, u_int); 52 53 static void screen_reflow(struct screen *, u_int); 54 55 /* Free titles stack. */ 56 static void 57 screen_free_titles(struct screen *s) 58 { 59 struct screen_title_entry *title_entry; 60 61 if (s->titles == NULL) 62 return; 63 64 while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) { 65 TAILQ_REMOVE(s->titles, title_entry, entry); 66 free(title_entry->text); 67 free(title_entry); 68 } 69 70 free(s->titles); 71 s->titles = NULL; 72 } 73 74 /* Create a new screen. */ 75 void 76 screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) 77 { 78 s->grid = grid_create(sx, sy, hlimit); 79 s->title = xstrdup(""); 80 s->titles = NULL; 81 82 s->cstyle = 0; 83 s->ccolour = xstrdup(""); 84 s->tabs = NULL; 85 s->sel = NULL; 86 87 screen_reinit(s); 88 } 89 90 /* Reinitialise screen. */ 91 void 92 screen_reinit(struct screen *s) 93 { 94 s->cx = 0; 95 s->cy = 0; 96 97 s->rupper = 0; 98 s->rlower = screen_size_y(s) - 1; 99 100 s->mode = MODE_CURSOR | MODE_WRAP; 101 102 screen_reset_tabs(s); 103 104 grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); 105 106 screen_clear_selection(s); 107 screen_free_titles(s); 108 } 109 110 /* Destroy a screen. */ 111 void 112 screen_free(struct screen *s) 113 { 114 free(s->sel); 115 free(s->tabs); 116 free(s->title); 117 free(s->ccolour); 118 119 grid_destroy(s->grid); 120 121 screen_free_titles(s); 122 } 123 124 /* Reset tabs to default, eight spaces apart. */ 125 void 126 screen_reset_tabs(struct screen *s) 127 { 128 u_int i; 129 130 free(s->tabs); 131 132 if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL) 133 fatal("bit_alloc failed"); 134 for (i = 8; i < screen_size_x(s); i += 8) 135 bit_set(s->tabs, i); 136 } 137 138 /* Set screen cursor style. */ 139 void 140 screen_set_cursor_style(struct screen *s, u_int style) 141 { 142 if (style <= 6) 143 s->cstyle = style; 144 } 145 146 /* Set screen cursor colour. */ 147 void 148 screen_set_cursor_colour(struct screen *s, const char *colour) 149 { 150 free(s->ccolour); 151 s->ccolour = xstrdup(colour); 152 } 153 154 /* Set screen title. */ 155 void 156 screen_set_title(struct screen *s, const char *title) 157 { 158 free(s->title); 159 utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); 160 } 161 162 /* Push the current title onto the stack. */ 163 void 164 screen_push_title(struct screen *s) 165 { 166 struct screen_title_entry *title_entry; 167 168 if (s->titles == NULL) { 169 s->titles = xmalloc(sizeof *s->titles); 170 TAILQ_INIT(s->titles); 171 } 172 title_entry = xmalloc(sizeof *title_entry); 173 title_entry->text = xstrdup(s->title); 174 TAILQ_INSERT_HEAD(s->titles, title_entry, entry); 175 } 176 177 /* 178 * Pop a title from the stack and set it as the screen title. If the stack is 179 * empty, do nothing. 180 */ 181 void 182 screen_pop_title(struct screen *s) 183 { 184 struct screen_title_entry *title_entry; 185 186 if (s->titles == NULL) 187 return; 188 189 title_entry = TAILQ_FIRST(s->titles); 190 if (title_entry != NULL) { 191 screen_set_title(s, title_entry->text); 192 193 TAILQ_REMOVE(s->titles, title_entry, entry); 194 free(title_entry->text); 195 free(title_entry); 196 } 197 } 198 199 /* Resize screen. */ 200 void 201 screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) 202 { 203 if (sx < 1) 204 sx = 1; 205 if (sy < 1) 206 sy = 1; 207 208 if (sx != screen_size_x(s)) { 209 screen_resize_x(s, sx); 210 211 /* 212 * It is unclear what should happen to tabs on resize. xterm 213 * seems to try and maintain them, rxvt resets them. Resetting 214 * is simpler and more reliable so let's do that. 215 */ 216 screen_reset_tabs(s); 217 } else 218 reflow = 0; 219 220 if (sy != screen_size_y(s)) 221 screen_resize_y(s, sy); 222 223 if (reflow) 224 screen_reflow(s, sx); 225 } 226 227 static void 228 screen_resize_x(struct screen *s, u_int sx) 229 { 230 struct grid *gd = s->grid; 231 232 if (sx == 0) 233 fatalx("zero size"); 234 235 /* 236 * Treat resizing horizontally simply: just ensure the cursor is 237 * on-screen and change the size. Don't bother to truncate any lines - 238 * then the data should be accessible if the size is then increased. 239 * 240 * The only potential wrinkle is if UTF-8 double-width characters are 241 * left in the last column, but UTF-8 terminals should deal with this 242 * sanely. 243 */ 244 if (s->cx >= sx) 245 s->cx = sx - 1; 246 gd->sx = sx; 247 } 248 249 static void 250 screen_resize_y(struct screen *s, u_int sy) 251 { 252 struct grid *gd = s->grid; 253 u_int needed, available, oldy, i; 254 255 if (sy == 0) 256 fatalx("zero size"); 257 oldy = screen_size_y(s); 258 259 /* 260 * When resizing: 261 * 262 * If the height is decreasing, delete lines from the bottom until 263 * hitting the cursor, then push lines from the top into the history. 264 * 265 * When increasing, pull as many lines as possible from scrolled 266 * history (not explicitly cleared from view) to the top, then fill the 267 * remaining with blanks at the bottom. 268 */ 269 270 /* Size decreasing. */ 271 if (sy < oldy) { 272 needed = oldy - sy; 273 274 /* Delete as many lines as possible from the bottom. */ 275 available = oldy - 1 - s->cy; 276 if (available > 0) { 277 if (available > needed) 278 available = needed; 279 grid_view_delete_lines(gd, oldy - available, available, 280 8); 281 } 282 needed -= available; 283 284 /* 285 * Now just increase the history size, if possible, to take 286 * over the lines which are left. If history is off, delete 287 * lines from the top. 288 */ 289 available = s->cy; 290 if (gd->flags & GRID_HISTORY) { 291 gd->hscrolled += needed; 292 gd->hsize += needed; 293 } else if (needed > 0 && available > 0) { 294 if (available > needed) 295 available = needed; 296 grid_view_delete_lines(gd, 0, available, 8); 297 } 298 s->cy -= needed; 299 } 300 301 /* Resize line array. */ 302 grid_adjust_lines(gd, gd->hsize + sy); 303 304 /* Size increasing. */ 305 if (sy > oldy) { 306 needed = sy - oldy; 307 308 /* 309 * Try to pull as much as possible out of scrolled history, if 310 * is is enabled. 311 */ 312 available = gd->hscrolled; 313 if (gd->flags & GRID_HISTORY && available > 0) { 314 if (available > needed) 315 available = needed; 316 gd->hscrolled -= available; 317 gd->hsize -= available; 318 s->cy += available; 319 } else 320 available = 0; 321 needed -= available; 322 323 /* Then fill the rest in with blanks. */ 324 for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) 325 memset(grid_get_line(gd, i), 0, sizeof(struct grid_line)); 326 } 327 328 /* Set the new size, and reset the scroll region. */ 329 gd->sy = sy; 330 s->rupper = 0; 331 s->rlower = screen_size_y(s) - 1; 332 } 333 334 /* Set selection. */ 335 void 336 screen_set_selection(struct screen *s, u_int sx, u_int sy, 337 u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc) 338 { 339 if (s->sel == NULL) 340 s->sel = xcalloc(1, sizeof *s->sel); 341 342 memcpy(&s->sel->cell, gc, sizeof s->sel->cell); 343 s->sel->hidden = 0; 344 s->sel->rectangle = rectangle; 345 s->sel->modekeys = modekeys; 346 347 s->sel->sx = sx; 348 s->sel->sy = sy; 349 s->sel->ex = ex; 350 s->sel->ey = ey; 351 } 352 353 /* Clear selection. */ 354 void 355 screen_clear_selection(struct screen *s) 356 { 357 free(s->sel); 358 s->sel = NULL; 359 } 360 361 /* Hide selection. */ 362 void 363 screen_hide_selection(struct screen *s) 364 { 365 if (s->sel != NULL) 366 s->sel->hidden = 1; 367 } 368 369 /* Check if cell in selection. */ 370 int 371 screen_check_selection(struct screen *s, u_int px, u_int py) 372 { 373 struct screen_sel *sel = s->sel; 374 u_int xx; 375 376 if (sel == NULL || sel->hidden) 377 return (0); 378 379 if (sel->rectangle) { 380 if (sel->sy < sel->ey) { 381 /* start line < end line -- downward selection. */ 382 if (py < sel->sy || py > sel->ey) 383 return (0); 384 } else if (sel->sy > sel->ey) { 385 /* start line > end line -- upward selection. */ 386 if (py > sel->sy || py < sel->ey) 387 return (0); 388 } else { 389 /* starting line == ending line. */ 390 if (py != sel->sy) 391 return (0); 392 } 393 394 /* 395 * Need to include the selection start row, but not the cursor 396 * row, which means the selection changes depending on which 397 * one is on the left. 398 */ 399 if (sel->ex < sel->sx) { 400 /* Cursor (ex) is on the left. */ 401 if (px < sel->ex) 402 return (0); 403 404 if (px > sel->sx) 405 return (0); 406 } else { 407 /* Selection start (sx) is on the left. */ 408 if (px < sel->sx) 409 return (0); 410 411 if (px > sel->ex) 412 return (0); 413 } 414 } else { 415 /* 416 * Like emacs, keep the top-left-most character, and drop the 417 * bottom-right-most, regardless of copy direction. 418 */ 419 if (sel->sy < sel->ey) { 420 /* starting line < ending line -- downward selection. */ 421 if (py < sel->sy || py > sel->ey) 422 return (0); 423 424 if (py == sel->sy && px < sel->sx) 425 return (0); 426 427 if (py == sel->ey && px > sel->ex) 428 return (0); 429 } else if (sel->sy > sel->ey) { 430 /* starting line > ending line -- upward selection. */ 431 if (py > sel->sy || py < sel->ey) 432 return (0); 433 434 if (py == sel->ey && px < sel->ex) 435 return (0); 436 437 if (sel->modekeys == MODEKEY_EMACS) 438 xx = sel->sx - 1; 439 else 440 xx = sel->sx; 441 if (py == sel->sy && (sel->sx == 0 || px > xx)) 442 return (0); 443 } else { 444 /* starting line == ending line. */ 445 if (py != sel->sy) 446 return (0); 447 448 if (sel->ex < sel->sx) { 449 /* cursor (ex) is on the left */ 450 if (sel->modekeys == MODEKEY_EMACS) 451 xx = sel->sx - 1; 452 else 453 xx = sel->sx; 454 if (px > xx || px < sel->ex) 455 return (0); 456 } else { 457 /* selection start (sx) is on the left */ 458 if (px < sel->sx || px > sel->ex) 459 return (0); 460 } 461 } 462 } 463 464 return (1); 465 } 466 467 /* Get selected grid cell. */ 468 void 469 screen_select_cell(struct screen *s, struct grid_cell *dst, 470 const struct grid_cell *src) 471 { 472 if (s->sel == NULL || s->sel->hidden) 473 return; 474 475 memcpy(dst, &s->sel->cell, sizeof *dst); 476 477 utf8_copy(&dst->data, &src->data); 478 dst->attr = dst->attr & ~GRID_ATTR_CHARSET; 479 dst->attr |= src->attr & GRID_ATTR_CHARSET; 480 dst->flags = src->flags; 481 } 482 483 /* Reflow wrapped lines. */ 484 static void 485 screen_reflow(struct screen *s, u_int new_x) 486 { 487 grid_reflow(s->grid, new_x, &s->cy); 488 } 489