1*78d51a74Snicm /* $OpenBSD: screen.c,v 1.14 2010/02/06 17:35:01 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 4311827fbSnicm * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm 2114e0d2d2Snicm #include <stdlib.h> 22311827fbSnicm #include <string.h> 234d6bb6c6Snicm #include <vis.h> 24311827fbSnicm 25311827fbSnicm #include "tmux.h" 26311827fbSnicm 27311827fbSnicm void screen_resize_x(struct screen *, u_int); 28311827fbSnicm void screen_resize_y(struct screen *, u_int); 29311827fbSnicm 30311827fbSnicm /* Create a new screen. */ 31311827fbSnicm void 32311827fbSnicm screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) 33311827fbSnicm { 34311827fbSnicm s->grid = grid_create(sx, sy, hlimit); 35311827fbSnicm 36311827fbSnicm s->title = xstrdup(""); 37311827fbSnicm 3814e0d2d2Snicm s->tabs = NULL; 3914e0d2d2Snicm 40311827fbSnicm screen_reinit(s); 41311827fbSnicm } 42311827fbSnicm 43311827fbSnicm /* Reinitialise screen. */ 44311827fbSnicm void 45311827fbSnicm screen_reinit(struct screen *s) 46311827fbSnicm { 47311827fbSnicm s->cx = 0; 48311827fbSnicm s->cy = 0; 49311827fbSnicm 50311827fbSnicm s->rupper = 0; 51311827fbSnicm s->rlower = screen_size_y(s) - 1; 52311827fbSnicm 53311827fbSnicm s->mode = MODE_CURSOR; 54311827fbSnicm 5514e0d2d2Snicm screen_reset_tabs(s); 5614e0d2d2Snicm 5709b0fd37Snicm grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy); 58311827fbSnicm 59311827fbSnicm screen_clear_selection(s); 60311827fbSnicm } 61311827fbSnicm 62311827fbSnicm /* Destroy a screen. */ 63311827fbSnicm void 64311827fbSnicm screen_free(struct screen *s) 65311827fbSnicm { 6624da6ca1Snicm if (s->tabs != NULL) 6724da6ca1Snicm xfree(s->tabs); 68311827fbSnicm xfree(s->title); 69311827fbSnicm grid_destroy(s->grid); 70311827fbSnicm } 71311827fbSnicm 7214e0d2d2Snicm /* Reset tabs to default, eight spaces apart. */ 7314e0d2d2Snicm void 7414e0d2d2Snicm screen_reset_tabs(struct screen *s) 7514e0d2d2Snicm { 7614e0d2d2Snicm u_int i; 7714e0d2d2Snicm 7814e0d2d2Snicm if (s->tabs != NULL) 7914e0d2d2Snicm xfree(s->tabs); 8014e0d2d2Snicm 8114e0d2d2Snicm if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL) 8214e0d2d2Snicm fatal("bit_alloc failed"); 8314e0d2d2Snicm for (i = 8; i < screen_size_x(s); i += 8) 8414e0d2d2Snicm bit_set(s->tabs, i); 8514e0d2d2Snicm } 8614e0d2d2Snicm 87311827fbSnicm /* Set screen title. */ 88311827fbSnicm void 89311827fbSnicm screen_set_title(struct screen *s, const char *title) 90311827fbSnicm { 914d6bb6c6Snicm char tmp[BUFSIZ]; 924d6bb6c6Snicm 934d6bb6c6Snicm strnvis(tmp, title, sizeof tmp, VIS_OCTAL|VIS_TAB|VIS_NL); 944d6bb6c6Snicm 95311827fbSnicm xfree(s->title); 964d6bb6c6Snicm s->title = xstrdup(tmp); 97311827fbSnicm } 98311827fbSnicm 99311827fbSnicm /* Resize screen. */ 100311827fbSnicm void 101311827fbSnicm screen_resize(struct screen *s, u_int sx, u_int sy) 102311827fbSnicm { 103311827fbSnicm if (sx < 1) 104311827fbSnicm sx = 1; 105311827fbSnicm if (sy < 1) 106311827fbSnicm sy = 1; 107311827fbSnicm 10814e0d2d2Snicm if (sx != screen_size_x(s)) { 109311827fbSnicm screen_resize_x(s, sx); 11014e0d2d2Snicm 11114e0d2d2Snicm /* 11214e0d2d2Snicm * It is unclear what should happen to tabs on resize. xterm 11314e0d2d2Snicm * seems to try and maintain them, rxvt resets them. Resetting 11414e0d2d2Snicm * is simpler and more reliable so let's do that. 11514e0d2d2Snicm */ 11614e0d2d2Snicm screen_reset_tabs(s); 11714e0d2d2Snicm } 11814e0d2d2Snicm 119311827fbSnicm if (sy != screen_size_y(s)) 120311827fbSnicm screen_resize_y(s, sy); 121311827fbSnicm } 122311827fbSnicm 123311827fbSnicm void 124311827fbSnicm screen_resize_x(struct screen *s, u_int sx) 125311827fbSnicm { 126311827fbSnicm struct grid *gd = s->grid; 127311827fbSnicm 128311827fbSnicm if (sx == 0) 129311827fbSnicm fatalx("zero size"); 130311827fbSnicm 131311827fbSnicm /* 132e8739adcSnicm * Treat resizing horizontally simply: just ensure the cursor is 133e8739adcSnicm * on-screen and change the size. Don't bother to truncate any lines - 134e8739adcSnicm * then the data should be accessible if the size is then incrased. 135e8739adcSnicm * 136e8739adcSnicm * The only potential wrinkle is if UTF-8 double-width characters are 137e8739adcSnicm * left in the last column, but UTF-8 terminals should deal with this 138e8739adcSnicm * sanely. 139311827fbSnicm */ 140311827fbSnicm if (s->cx >= sx) 141311827fbSnicm s->cx = sx - 1; 142311827fbSnicm gd->sx = sx; 143311827fbSnicm } 144311827fbSnicm 145311827fbSnicm void 146311827fbSnicm screen_resize_y(struct screen *s, u_int sy) 147311827fbSnicm { 148311827fbSnicm struct grid *gd = s->grid; 149516fb81dSnicm u_int needed, available, oldy, i; 150311827fbSnicm 151311827fbSnicm if (sy == 0) 152311827fbSnicm fatalx("zero size"); 153516fb81dSnicm oldy = screen_size_y(s); 154516fb81dSnicm 155516fb81dSnicm /* 156516fb81dSnicm * When resizing: 157516fb81dSnicm * 158516fb81dSnicm * If the height is decreasing, delete lines from the bottom until 159516fb81dSnicm * hitting the cursor, then push lines from the top into the history. 160516fb81dSnicm * 161516fb81dSnicm * When increasing, pull as many lines as possible from the history to 162516fb81dSnicm * the top, then fill the remaining with blanks at the bottom. 163516fb81dSnicm */ 164311827fbSnicm 165311827fbSnicm /* Size decreasing. */ 166516fb81dSnicm if (sy < oldy) { 167516fb81dSnicm needed = oldy - sy; 168311827fbSnicm 169516fb81dSnicm /* Delete as many lines as possible from the bottom. */ 170516fb81dSnicm available = oldy - 1 - s->cy; 171516fb81dSnicm if (available > 0) { 172516fb81dSnicm if (available > needed) 173516fb81dSnicm available = needed; 174516fb81dSnicm grid_view_delete_lines(gd, oldy - available, available); 175516fb81dSnicm } 176516fb81dSnicm needed -= available; 177516fb81dSnicm 178311827fbSnicm /* 179953d773cSnicm * Now just increase the history size, if possible, to take 180953d773cSnicm * over the lines which are left. If history is off, delete 181953d773cSnicm * lines from the top. 182953d773cSnicm * 183953d773cSnicm * XXX Should apply history limit? 184311827fbSnicm */ 185953d773cSnicm available = s->cy; 186953d773cSnicm if (gd->flags & GRID_HISTORY) 187516fb81dSnicm gd->hsize += needed; 1887e4d386bSnicm else if (needed > 0 && available > 0) { 189953d773cSnicm if (available > needed) 190953d773cSnicm available = needed; 191953d773cSnicm grid_view_delete_lines(gd, 0, available); 192953d773cSnicm } 193516fb81dSnicm s->cy -= needed; 194311827fbSnicm } 195311827fbSnicm 196311827fbSnicm /* Resize line arrays. */ 1979554a682Snicm gd->linedata = xrealloc( 1989554a682Snicm gd->linedata, gd->hsize + sy, sizeof *gd->linedata); 199311827fbSnicm 200311827fbSnicm /* Size increasing. */ 201516fb81dSnicm if (sy > oldy) { 202516fb81dSnicm needed = sy - oldy; 203516fb81dSnicm 204953d773cSnicm /* 205953d773cSnicm * Try to pull as much as possible out of the history, if is 206953d773cSnicm * is enabled. 207953d773cSnicm */ 208516fb81dSnicm available = gd->hsize; 209953d773cSnicm if (gd->flags & GRID_HISTORY && available > 0) { 210516fb81dSnicm if (available > needed) 211516fb81dSnicm available = needed; 212516fb81dSnicm gd->hsize -= available; 213516fb81dSnicm s->cy += available; 214953d773cSnicm } else 215953d773cSnicm available = 0; 216516fb81dSnicm needed -= available; 217516fb81dSnicm 218516fb81dSnicm /* Then fill the rest in with blanks. */ 2199554a682Snicm for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) 2209554a682Snicm memset(&gd->linedata[i], 0, sizeof gd->linedata[i]); 221311827fbSnicm } 222311827fbSnicm 223516fb81dSnicm /* Set the new size, and reset the scroll region. */ 224311827fbSnicm gd->sy = sy; 225311827fbSnicm s->rupper = 0; 226311827fbSnicm s->rlower = screen_size_y(s) - 1; 227311827fbSnicm } 228311827fbSnicm 229311827fbSnicm /* Set selection. */ 230311827fbSnicm void 231*78d51a74Snicm screen_set_selection(struct screen *s, u_int sx, u_int sy, 232*78d51a74Snicm u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc) 233311827fbSnicm { 234311827fbSnicm struct screen_sel *sel = &s->sel; 235311827fbSnicm 236311827fbSnicm memcpy(&sel->cell, gc, sizeof sel->cell); 237311827fbSnicm sel->flag = 1; 238*78d51a74Snicm sel->rectflag = rectflag; 23969466ab0Snicm 24069466ab0Snicm sel->sx = sx; sel->sy = sy; 24169466ab0Snicm sel->ex = ex; sel->ey = ey; 242311827fbSnicm } 243311827fbSnicm 244311827fbSnicm /* Clear selection. */ 245311827fbSnicm void 246311827fbSnicm screen_clear_selection(struct screen *s) 247311827fbSnicm { 248311827fbSnicm struct screen_sel *sel = &s->sel; 249311827fbSnicm 250311827fbSnicm sel->flag = 0; 251311827fbSnicm } 252311827fbSnicm 253311827fbSnicm /* Check if cell in selection. */ 254311827fbSnicm int 255311827fbSnicm screen_check_selection(struct screen *s, u_int px, u_int py) 256311827fbSnicm { 257311827fbSnicm struct screen_sel *sel = &s->sel; 258311827fbSnicm 259*78d51a74Snicm if (!sel->flag) 260311827fbSnicm return (0); 261311827fbSnicm 262*78d51a74Snicm if (sel->rectflag) { 263*78d51a74Snicm if (sel->sy < sel->ey) { 264*78d51a74Snicm /* start line < end line -- downward selection. */ 265*78d51a74Snicm if (py < sel->sy || py > sel->ey) 266311827fbSnicm return (0); 267*78d51a74Snicm } else if (sel->sy > sel->ey) { 268*78d51a74Snicm /* start line > end line -- upward selection. */ 269*78d51a74Snicm if (py > sel->sy || py < sel->ey) 270*78d51a74Snicm return (0); 271*78d51a74Snicm } else { 272*78d51a74Snicm /* starting line == ending line. */ 273*78d51a74Snicm if (py != sel->sy) 274*78d51a74Snicm return (0); 275311827fbSnicm } 276311827fbSnicm 277*78d51a74Snicm /* 278*78d51a74Snicm * Need to include the selection start row, but not the cursor 279*78d51a74Snicm * row, which means the selection changes depending on which 280*78d51a74Snicm * one is on the left. 281*78d51a74Snicm */ 282*78d51a74Snicm if (sel->ex < sel->sx) { 283*78d51a74Snicm /* Cursor (ex) is on the left. */ 284*78d51a74Snicm if (px <= sel->ex) 285311827fbSnicm return (0); 286*78d51a74Snicm 287*78d51a74Snicm if (px > sel->sx) 288*78d51a74Snicm return (0); 289*78d51a74Snicm } else { 290*78d51a74Snicm /* Selection start (sx) is on the left. */ 291*78d51a74Snicm if (px < sel->sx) 292*78d51a74Snicm return (0); 293*78d51a74Snicm 294*78d51a74Snicm if (px >= sel->ex) 295*78d51a74Snicm return (0); 296*78d51a74Snicm } 297*78d51a74Snicm } else { 298*78d51a74Snicm /* 299*78d51a74Snicm * Like emacs, keep the top-left-most character, and drop the 300*78d51a74Snicm * bottom-right-most, regardless of copy direction. 301*78d51a74Snicm */ 302*78d51a74Snicm if (sel->sy < sel->ey) { 303*78d51a74Snicm /* starting line < ending line -- downward selection. */ 304*78d51a74Snicm if (py < sel->sy || py > sel->ey) 305*78d51a74Snicm return (0); 306*78d51a74Snicm 307*78d51a74Snicm if ((py == sel->sy && px < sel->sx) 308*78d51a74Snicm || (py == sel->ey && px > sel->ex)) 309*78d51a74Snicm return (0); 310*78d51a74Snicm } else if (sel->sy > sel->ey) { 311*78d51a74Snicm /* starting line > ending line -- upward selection. */ 312*78d51a74Snicm if (py > sel->sy || py < sel->ey) 313*78d51a74Snicm return (0); 314*78d51a74Snicm 315*78d51a74Snicm if ((py == sel->sy && px >= sel->sx) 316*78d51a74Snicm || (py == sel->ey && px < sel->ex)) 317*78d51a74Snicm return (0); 318*78d51a74Snicm } else { 319*78d51a74Snicm /* starting line == ending line. */ 320*78d51a74Snicm if (py != sel->sy) 321*78d51a74Snicm return (0); 322*78d51a74Snicm 323*78d51a74Snicm if (sel->ex < sel->sx) { 324*78d51a74Snicm /* cursor (ex) is on the left */ 325*78d51a74Snicm if (px > sel->sx || px < sel->ex) 326*78d51a74Snicm return (0); 327*78d51a74Snicm } else { 328*78d51a74Snicm /* selection start (sx) is on the left */ 329*78d51a74Snicm if (px < sel->sx || px > sel->ex) 330*78d51a74Snicm return (0); 331*78d51a74Snicm } 332*78d51a74Snicm } 333*78d51a74Snicm } 334*78d51a74Snicm 335311827fbSnicm return (1); 336311827fbSnicm } 337