Annotation of embedaddon/tmux/screen.c, revision 1.1.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>