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