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