Annotation of src/usr.bin/tmux/window-copy.c, Revision 1.23
1.23 ! nicm 1: /* $OpenBSD: window-copy.c,v 1.22 2009/08/18 07:23:43 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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:
1.21 nicm 21: #include <stdlib.h>
1.1 nicm 22: #include <string.h>
23:
24: #include "tmux.h"
25:
26: struct screen *window_copy_init(struct window_pane *);
27: void window_copy_free(struct window_pane *);
28: void window_copy_resize(struct window_pane *, u_int, u_int);
29: void window_copy_key(struct window_pane *, struct client *, int);
1.21 nicm 30: int window_copy_key_input(struct window_pane *, int);
1.1 nicm 31: void window_copy_mouse(
32: struct window_pane *, struct client *, u_char, u_char, u_char);
33:
34: void window_copy_redraw_lines(struct window_pane *, u_int, u_int);
35: void window_copy_redraw_screen(struct window_pane *);
36: void window_copy_write_line(
37: struct window_pane *, struct screen_write_ctx *, u_int);
38: void window_copy_write_lines(
39: struct window_pane *, struct screen_write_ctx *, u_int, u_int);
40:
1.21 nicm 41: void window_copy_scroll_to(struct window_pane *, u_int, u_int);
42: int window_copy_search_compare(
43: struct grid *, u_int, u_int, struct grid *, u_int);
44: int window_copy_search_lr(
45: struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
46: int window_copy_search_rl(
47: struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
48: void window_copy_search_up(struct window_pane *, const char *);
49: void window_copy_search_down(struct window_pane *, const char *);
50: void window_copy_goto_line(struct window_pane *, const char *);
1.18 nicm 51: void window_copy_update_cursor(struct window_pane *, u_int, u_int);
1.1 nicm 52: void window_copy_start_selection(struct window_pane *);
53: int window_copy_update_selection(struct window_pane *);
54: void window_copy_copy_selection(struct window_pane *, struct client *);
55: void window_copy_copy_line(
56: struct window_pane *, char **, size_t *, u_int, u_int, u_int);
57: int window_copy_is_space(struct window_pane *, u_int, u_int);
58: u_int window_copy_find_length(struct window_pane *, u_int);
59: void window_copy_cursor_start_of_line(struct window_pane *);
1.6 nicm 60: void window_copy_cursor_back_to_indentation(struct window_pane *);
1.1 nicm 61: void window_copy_cursor_end_of_line(struct window_pane *);
62: void window_copy_cursor_left(struct window_pane *);
63: void window_copy_cursor_right(struct window_pane *);
64: void window_copy_cursor_up(struct window_pane *);
65: void window_copy_cursor_down(struct window_pane *);
66: void window_copy_cursor_next_word(struct window_pane *);
67: void window_copy_cursor_previous_word(struct window_pane *);
68: void window_copy_scroll_up(struct window_pane *, u_int);
69: void window_copy_scroll_down(struct window_pane *, u_int);
70:
71: const struct window_mode window_copy_mode = {
72: window_copy_init,
73: window_copy_free,
74: window_copy_resize,
75: window_copy_key,
76: window_copy_mouse,
77: NULL,
78: };
79:
1.21 nicm 80: enum window_copy_input_type {
81: WINDOW_COPY_OFF,
82: WINDOW_COPY_SEARCHUP,
83: WINDOW_COPY_SEARCHDOWN,
84: WINDOW_COPY_GOTOLINE,
85: };
86:
1.1 nicm 87: struct window_copy_mode_data {
88: struct screen screen;
89:
1.21 nicm 90: struct mode_key_data mdata;
91:
92: u_int oy;
93:
94: u_int selx;
95: u_int sely;
1.1 nicm 96:
1.21 nicm 97: u_int cx;
98: u_int cy;
1.1 nicm 99:
1.21 nicm 100: enum window_copy_input_type inputtype;
101: const char *inputprompt;
102: char *inputstr;
1.1 nicm 103:
1.21 nicm 104: enum window_copy_input_type searchtype;
105: char *searchstr;
1.1 nicm 106: };
107:
108: struct screen *
109: window_copy_init(struct window_pane *wp)
110: {
111: struct window_copy_mode_data *data;
112: struct screen *s;
113: struct screen_write_ctx ctx;
114: u_int i;
1.10 nicm 115: int keys;
1.1 nicm 116:
117: wp->modedata = data = xmalloc(sizeof *data);
118: data->oy = 0;
119: data->cx = wp->base.cx;
120: data->cy = wp->base.cy;
121:
1.21 nicm 122: data->inputtype = WINDOW_COPY_OFF;
123: data->inputprompt = NULL;
124: data->inputstr = xstrdup("");
125:
126: data->searchtype = WINDOW_COPY_OFF;
127: data->searchstr = NULL;
128:
1.1 nicm 129: s = &data->screen;
130: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
1.13 nicm 131: if (options_get_number(&wp->window->options, "mode-mouse"))
132: s->mode |= MODE_MOUSE;
1.1 nicm 133:
1.10 nicm 134: keys = options_get_number(&wp->window->options, "mode-keys");
135: if (keys == MODEKEY_EMACS)
1.12 nicm 136: mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
1.10 nicm 137: else
1.12 nicm 138: mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
1.1 nicm 139:
140: s->cx = data->cx;
141: s->cy = data->cy;
142:
143: screen_write_start(&ctx, NULL, s);
144: for (i = 0; i < screen_size_y(s); i++)
145: window_copy_write_line(wp, &ctx, i);
146: screen_write_cursormove(&ctx, data->cx, data->cy);
147: screen_write_stop(&ctx);
148:
149: return (s);
150: }
151:
152: void
153: window_copy_free(struct window_pane *wp)
154: {
155: struct window_copy_mode_data *data = wp->modedata;
156:
1.21 nicm 157: if (data->searchstr != NULL)
158: xfree(data->searchstr);
159: xfree(data->inputstr);
160:
1.1 nicm 161: screen_free(&data->screen);
1.21 nicm 162:
1.1 nicm 163: xfree(data);
164: }
165:
166: void
167: window_copy_pageup(struct window_pane *wp)
168: {
169: struct window_copy_mode_data *data = wp->modedata;
170: struct screen *s = &data->screen;
1.19 nicm 171: u_int n;
1.1 nicm 172:
1.19 nicm 173: n = 1;
174: if (screen_size_y(s) > 2)
175: n = screen_size_y(s) - 2;
176: if (data->oy + n > screen_hsize(&wp->base))
1.1 nicm 177: data->oy = screen_hsize(&wp->base);
178: else
1.19 nicm 179: data->oy += n;
1.1 nicm 180: window_copy_update_selection(wp);
181: window_copy_redraw_screen(wp);
182: }
183:
184: void
185: window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
186: {
187: struct window_copy_mode_data *data = wp->modedata;
188: struct screen *s = &data->screen;
189: struct screen_write_ctx ctx;
190:
191: screen_resize(s, sx, sy);
1.18 nicm 192:
193: if (data->cy > sy - 1)
194: data->cy = sy - 1;
195: if (data->cx > sx)
196: data->cx = sx;
197:
198: screen_clear_selection(&data->screen);
199:
1.1 nicm 200: screen_write_start(&ctx, NULL, s);
201: window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
202: screen_write_stop(&ctx);
1.18 nicm 203:
1.17 nicm 204: window_copy_redraw_screen(wp);
1.1 nicm 205: }
206:
207: void
208: window_copy_key(struct window_pane *wp, struct client *c, int key)
209: {
210: struct window_copy_mode_data *data = wp->modedata;
211: struct screen *s = &data->screen;
1.19 nicm 212: u_int n;
1.21 nicm 213: int keys;
214:
215: if (data->inputtype != WINDOW_COPY_OFF) {
216: if (window_copy_key_input(wp, key) != 0)
217: goto input_off;
218: return;
219: }
1.1 nicm 220:
221: switch (mode_key_lookup(&data->mdata, key)) {
1.11 nicm 222: case MODEKEYCOPY_CANCEL:
1.1 nicm 223: window_pane_reset_mode(wp);
224: break;
1.10 nicm 225: case MODEKEYCOPY_LEFT:
1.1 nicm 226: window_copy_cursor_left(wp);
227: return;
1.10 nicm 228: case MODEKEYCOPY_RIGHT:
1.1 nicm 229: window_copy_cursor_right(wp);
230: return;
1.10 nicm 231: case MODEKEYCOPY_UP:
1.1 nicm 232: window_copy_cursor_up(wp);
233: return;
1.10 nicm 234: case MODEKEYCOPY_DOWN:
1.1 nicm 235: window_copy_cursor_down(wp);
236: return;
1.10 nicm 237: case MODEKEYCOPY_PREVIOUSPAGE:
1.1 nicm 238: window_copy_pageup(wp);
239: break;
1.10 nicm 240: case MODEKEYCOPY_NEXTPAGE:
1.19 nicm 241: n = 1;
242: if (screen_size_y(s) > 2)
243: n = screen_size_y(s) - 2;
1.20 nicm 244: if (data->oy < n)
245: data->oy = 0;
246: else
247: data->oy -= n;
248: window_copy_update_selection(wp);
249: window_copy_redraw_screen(wp);
250: break;
251: case MODEKEYCOPY_HALFPAGEUP:
252: n = screen_size_y(s) / 2;
253: if (data->oy + n > screen_hsize(&wp->base))
254: data->oy = screen_hsize(&wp->base);
255: else
256: data->oy += n;
257: window_copy_update_selection(wp);
258: window_copy_redraw_screen(wp);
259: break;
260: case MODEKEYCOPY_HALFPAGEDOWN:
261: n = screen_size_y(s) / 2;
1.19 nicm 262: if (data->oy < n)
1.1 nicm 263: data->oy = 0;
264: else
1.19 nicm 265: data->oy -= n;
1.1 nicm 266: window_copy_update_selection(wp);
267: window_copy_redraw_screen(wp);
268: break;
1.10 nicm 269: case MODEKEYCOPY_STARTSELECTION:
1.1 nicm 270: window_copy_start_selection(wp);
1.7 nicm 271: window_copy_redraw_screen(wp);
1.1 nicm 272: break;
1.10 nicm 273: case MODEKEYCOPY_CLEARSELECTION:
1.1 nicm 274: screen_clear_selection(&data->screen);
275: window_copy_redraw_screen(wp);
276: break;
1.10 nicm 277: case MODEKEYCOPY_COPYSELECTION:
1.1 nicm 278: if (c != NULL && c->session != NULL) {
279: window_copy_copy_selection(wp, c);
280: window_pane_reset_mode(wp);
281: }
282: break;
1.10 nicm 283: case MODEKEYCOPY_STARTOFLINE:
1.1 nicm 284: window_copy_cursor_start_of_line(wp);
285: break;
1.10 nicm 286: case MODEKEYCOPY_BACKTOINDENTATION:
1.6 nicm 287: window_copy_cursor_back_to_indentation(wp);
288: break;
1.10 nicm 289: case MODEKEYCOPY_ENDOFLINE:
1.1 nicm 290: window_copy_cursor_end_of_line(wp);
291: break;
1.10 nicm 292: case MODEKEYCOPY_NEXTWORD:
1.1 nicm 293: window_copy_cursor_next_word(wp);
294: break;
1.10 nicm 295: case MODEKEYCOPY_PREVIOUSWORD:
1.1 nicm 296: window_copy_cursor_previous_word(wp);
297: break;
1.21 nicm 298: case MODEKEYCOPY_SEARCHUP:
299: data->inputtype = WINDOW_COPY_SEARCHUP;
300: data->inputprompt = "Search Up";
301: goto input_on;
302: case MODEKEYCOPY_SEARCHDOWN:
303: data->inputtype = WINDOW_COPY_SEARCHDOWN;
304: data->inputprompt = "Search Down";
305: goto input_on;
306: case MODEKEYCOPY_SEARCHAGAIN:
307: switch (data->searchtype) {
308: case WINDOW_COPY_OFF:
309: case WINDOW_COPY_GOTOLINE:
310: break;
311: case WINDOW_COPY_SEARCHUP:
312: window_copy_search_up(wp, data->searchstr);
313: break;
314: case WINDOW_COPY_SEARCHDOWN:
315: window_copy_search_down(wp, data->searchstr);
316: break;
317: }
318: break;
319: case MODEKEYCOPY_GOTOLINE:
320: data->inputtype = WINDOW_COPY_GOTOLINE;
321: data->inputprompt = "Goto Line";
322: *data->inputstr = '\0';
323: goto input_on;
324: default:
325: break;
326: }
327:
328: return;
329:
330: input_on:
331: keys = options_get_number(&wp->window->options, "mode-keys");
332: if (keys == MODEKEY_EMACS)
333: mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
334: else
335: mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
336:
337: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
338: return;
339:
340: input_off:
341: keys = options_get_number(&wp->window->options, "mode-keys");
342: if (keys == MODEKEY_EMACS)
343: mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
344: else
345: mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
346:
347: data->inputtype = WINDOW_COPY_OFF;
348: data->inputprompt = NULL;
349:
350: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
351: }
352:
353: int
354: window_copy_key_input(struct window_pane *wp, int key)
355: {
356: struct window_copy_mode_data *data = wp->modedata;
357: struct screen *s = &data->screen;
358: size_t inputlen;
359:
360: switch (mode_key_lookup(&data->mdata, key)) {
361: case MODEKEYEDIT_CANCEL:
362: return (-1);
363: case MODEKEYEDIT_BACKSPACE:
364: inputlen = strlen(data->inputstr);
365: if (inputlen > 0)
366: data->inputstr[inputlen - 1] = '\0';
1.22 nicm 367: break;
368: case MODEKEYEDIT_DELETELINE:
369: *data->inputstr = '\0';
1.21 nicm 370: break;
371: case MODEKEYEDIT_ENTER:
372: switch (data->inputtype) {
373: case WINDOW_COPY_OFF:
374: break;
375: case WINDOW_COPY_SEARCHUP:
376: window_copy_search_up(wp, data->inputstr);
377: data->searchtype = data->inputtype;
378: data->searchstr = xstrdup(data->inputstr);
379: break;
380: case WINDOW_COPY_SEARCHDOWN:
381: window_copy_search_down(wp, data->inputstr);
382: data->searchtype = data->inputtype;
383: data->searchstr = xstrdup(data->inputstr);
384: break;
385: case WINDOW_COPY_GOTOLINE:
386: window_copy_goto_line(wp, data->inputstr);
387: *data->inputstr = '\0';
388: break;
389: }
390: return (1);
391: case MODEKEY_OTHER:
392: if (key < 32 || key > 126)
393: break;
394: inputlen = strlen(data->inputstr) + 2;
395:
396: data->inputstr = xrealloc(data->inputstr, 1, inputlen);
397: data->inputstr[inputlen - 2] = key;
398: data->inputstr[inputlen - 1] = '\0';
399: break;
1.1 nicm 400: default:
401: break;
402: }
1.21 nicm 403:
404: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
405: return (0);
1.1 nicm 406: }
407:
408: void
409: window_copy_mouse(struct window_pane *wp,
410: unused struct client *c, u_char b, u_char x, u_char y)
411: {
412: struct window_copy_mode_data *data = wp->modedata;
413: struct screen *s = &data->screen;
414:
415: if ((b & 3) == 3)
416: return;
417: if (x >= screen_size_x(s))
418: return;
419: if (y >= screen_size_y(s))
420: return;
421:
1.18 nicm 422: window_copy_update_cursor(wp, x, y);
1.1 nicm 423: if (window_copy_update_selection(wp))
424: window_copy_redraw_screen(wp);
425: }
426:
427: void
1.21 nicm 428: window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
429: {
430: struct window_copy_mode_data *data = wp->modedata;
431: struct screen *s = &wp->base;
432: struct grid *gd = s->grid;
433: u_int offset, gap;
434:
435: data->cx = px;
436:
437: gap = gd->sy / 4;
438: if (py < gd->sy) {
439: offset = 0;
440: data->cy = py;
441: } else if (py > gd->hsize + gd->sy - gap) {
442: offset = gd->hsize;
443: data->cy = py - gd->hsize;
444: } else {
445: offset = py + gap - gd->sy;
446: data->cy = py - offset;
447: }
448: data->oy = gd->hsize - offset;
449:
450: window_copy_redraw_screen(wp);
451: }
452:
453: int
454: window_copy_search_compare(
455: struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
456: {
457: const struct grid_cell *gc, *sgc;
458: const struct grid_utf8 *gu, *sgu;
459:
460: gc = grid_peek_cell(gd, px, py);
461: sgc = grid_peek_cell(sgd, spx, 0);
462:
463: if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8))
464: return (0);
465:
466: if (gc->flags & GRID_FLAG_UTF8) {
467: gu = grid_peek_utf8(gd, px, py);
468: sgu = grid_peek_utf8(sgd, spx, 0);
469: if (memcmp(gu->data, sgu->data, UTF8_SIZE) == 0)
470: return (1);
471: } else {
472: if (gc->data == sgc->data)
473: return (1);
474: }
475: return (0);
476: }
477:
478: int
479: window_copy_search_lr(struct grid *gd,
480: struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
481: {
482: u_int ax, bx, px;
483:
484: for (ax = first; ax < last; ax++) {
485: if (ax + sgd->sx >= gd->sx)
486: break;
487: for (bx = 0; bx < sgd->sx; bx++) {
488: px = ax + bx;
489: if (!window_copy_search_compare(gd, px, py, sgd, bx))
490: break;
491: }
492: if (bx == sgd->sx) {
493: *ppx = ax;
494: return (1);
495: }
496: }
497: return (0);
498: }
499:
500: int
501: window_copy_search_rl(struct grid *gd,
502: struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
503: {
504: u_int ax, bx, px;
505:
506: for (ax = last + 1; ax > first; ax--) {
507: for (bx = 0; bx < sgd->sx; bx++) {
508: px = ax - 1 + bx;
509: if (!window_copy_search_compare(gd, px, py, sgd, bx))
510: break;
511: }
512: if (bx == sgd->sx) {
513: *ppx = ax - 1;
514: return (1);
515: }
516: }
517: return (0);
518: }
519:
520: void
521: window_copy_search_up(struct window_pane *wp, const char *searchstr)
522: {
523: struct window_copy_mode_data *data = wp->modedata;
524: struct screen *s = &wp->base, ss;
525: struct screen_write_ctx ctx;
526: struct grid *gd = s->grid, *sgd;
527: struct grid_cell gc;
528: size_t searchlen;
529: u_int i, last, fx, fy, px;
530: int utf8flag, n, wrapped;
531:
532: if (*searchstr == '\0')
533: return;
534: utf8flag = options_get_number(&wp->window->options, "utf8");
535: searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
536:
537: screen_init(&ss, searchlen, 1, 0);
538: screen_write_start(&ctx, NULL, &ss);
539: memcpy(&gc, &grid_default_cell, sizeof gc);
540: screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
541: screen_write_stop(&ctx);
542:
543: fx = data->cx;
544: fy = gd->hsize - data->oy + data->cy;
545:
546: if (fx == 0) {
547: if (fy == 0)
548: return;
549: fx = gd->sx - 1;
550: fy--;
551: } else
552: fx--;
553: n = wrapped = 0;
554:
555: retry:
556: sgd = ss.grid;
557: for (i = fy + 1; i > 0; i--) {
558: last = screen_size_x(s);
559: if (i == fy + 1)
560: last = fx;
561: n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
562: if (n) {
563: window_copy_scroll_to(wp, px, i - 1);
564: break;
565: }
566: }
567: if (!n && !wrapped) {
568: fx = gd->sx - 1;
569: fy = gd->hsize + gd->sy - 1;
570: wrapped = 1;
571: goto retry;
572: }
573:
574: screen_free(&ss);
575: }
576:
577: void
578: window_copy_search_down(struct window_pane *wp, const char *searchstr)
579: {
580: struct window_copy_mode_data *data = wp->modedata;
581: struct screen *s = &wp->base, ss;
582: struct screen_write_ctx ctx;
583: struct grid *gd = s->grid, *sgd;
584: struct grid_cell gc;
585: size_t searchlen;
586: u_int i, first, fx, fy, px;
587: int utf8flag, n, wrapped;
588:
589: if (*searchstr == '\0')
590: return;
591: utf8flag = options_get_number(&wp->window->options, "utf8");
592: searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
593:
594: screen_init(&ss, searchlen, 1, 0);
595: screen_write_start(&ctx, NULL, &ss);
596: memcpy(&gc, &grid_default_cell, sizeof gc);
597: screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
598: screen_write_stop(&ctx);
599:
600: fx = data->cx;
601: fy = gd->hsize - data->oy + data->cy;
602:
603: if (fx == gd->sx - 1) {
604: if (fy == gd->hsize + gd->sy)
605: return;
606: fx = 0;
607: fy++;
608: } else
609: fx++;
610: n = wrapped = 0;
611:
612: retry:
613: sgd = ss.grid;
614: for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
615: first = 0;
616: if (i == fy + 1)
617: first = fx;
618: n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
619: if (n) {
620: window_copy_scroll_to(wp, px, i - 1);
621: break;
622: }
623: }
624: if (!n && !wrapped) {
625: fx = 0;
626: fy = 0;
627: wrapped = 1;
628: goto retry;
629: }
630:
631: screen_free(&ss);
632: }
633:
634: void
635: window_copy_goto_line(struct window_pane *wp, const char *linestr)
636: {
637: struct window_copy_mode_data *data = wp->modedata;
638: const char *errstr;
639: u_int lineno;
640:
641: lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr);
642: if (errstr != NULL)
643: return;
644:
645: data->oy = lineno;
646: window_copy_redraw_screen(wp);
647: }
648:
649: void
1.18 nicm 650: window_copy_write_line(
651: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
1.1 nicm 652: {
653: struct window_copy_mode_data *data = wp->modedata;
654: struct screen *s = &data->screen;
655: struct grid_cell gc;
656: char hdr[32];
1.21 nicm 657: size_t last, xoff = 0, size = 0;
658:
659: memcpy(&gc, &grid_default_cell, sizeof gc);
660: gc.fg = options_get_number(&wp->window->options, "mode-fg");
661: gc.bg = options_get_number(&wp->window->options, "mode-bg");
662: gc.attr |= options_get_number(&wp->window->options, "mode-attr");
1.1 nicm 663:
1.21 nicm 664: last = screen_size_y(s) - 1;
1.1 nicm 665: if (py == 0) {
666: size = xsnprintf(hdr, sizeof hdr,
1.18 nicm 667: "[%u/%u]", data->oy, screen_hsize(&wp->base));
1.1 nicm 668: screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
669: screen_write_puts(ctx, &gc, "%s", hdr);
1.21 nicm 670: } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
671: xoff = size = xsnprintf(hdr, sizeof hdr,
672: "%s: %s", data->inputprompt, data->inputstr);
673: screen_write_cursormove(ctx, 0, last);
674: screen_write_puts(ctx, &gc, "%s", hdr);
1.1 nicm 675: } else
676: size = 0;
677:
1.21 nicm 678: screen_write_cursormove(ctx, xoff, py);
679: screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) -
1.1 nicm 680: data->oy) + py, screen_size_x(s) - size, 1);
1.18 nicm 681:
682: if (py == data->cy && data->cx == screen_size_x(s)) {
683: memcpy(&gc, &grid_default_cell, sizeof gc);
684: screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
685: screen_write_putc(ctx, &gc, '$');
686: }
1.1 nicm 687: }
688:
689: void
690: window_copy_write_lines(
691: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
692: {
693: u_int yy;
694:
695: for (yy = py; yy < py + ny; yy++)
696: window_copy_write_line(wp, ctx, py);
697: }
698:
699: void
700: window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
701: {
702: struct window_copy_mode_data *data = wp->modedata;
703: struct screen_write_ctx ctx;
704: u_int i;
705:
706: screen_write_start(&ctx, wp, NULL);
707: for (i = py; i < py + ny; i++)
708: window_copy_write_line(wp, &ctx, i);
709: screen_write_cursormove(&ctx, data->cx, data->cy);
710: screen_write_stop(&ctx);
711: }
712:
713: void
714: window_copy_redraw_screen(struct window_pane *wp)
715: {
716: struct window_copy_mode_data *data = wp->modedata;
717:
718: window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
719: }
720:
721: void
1.18 nicm 722: window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1.1 nicm 723: {
724: struct window_copy_mode_data *data = wp->modedata;
1.18 nicm 725: struct screen *s = &data->screen;
1.1 nicm 726: struct screen_write_ctx ctx;
1.18 nicm 727: u_int old_cx, old_cy;
1.1 nicm 728:
1.18 nicm 729: old_cx = data->cx; old_cy = data->cy;
730: data->cx = cx; data->cy = cy;
731: if (old_cx == screen_size_x(s))
732: window_copy_redraw_lines(wp, old_cy, 1);
733: if (data->cx == screen_size_x(s))
734: window_copy_redraw_lines(wp, data->cy, 1);
735: else {
736: screen_write_start(&ctx, wp, NULL);
737: screen_write_cursormove(&ctx, data->cx, data->cy);
738: screen_write_stop(&ctx);
739: }
1.1 nicm 740: }
741:
742: void
743: window_copy_start_selection(struct window_pane *wp)
744: {
745: struct window_copy_mode_data *data = wp->modedata;
746: struct screen *s = &data->screen;
747:
1.18 nicm 748: data->selx = data->cx;
1.1 nicm 749: data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
750:
751: s->sel.flag = 1;
752: window_copy_update_selection(wp);
753: }
754:
755: int
756: window_copy_update_selection(struct window_pane *wp)
757: {
758: struct window_copy_mode_data *data = wp->modedata;
759: struct screen *s = &data->screen;
760: struct grid_cell gc;
1.18 nicm 761: u_int sx, sy, ty;
1.1 nicm 762:
763: if (!s->sel.flag)
764: return (0);
765:
766: /* Set colours. */
767: memcpy(&gc, &grid_default_cell, sizeof gc);
1.14 nicm 768: gc.fg = options_get_number(&wp->window->options, "mode-fg");
769: gc.bg = options_get_number(&wp->window->options, "mode-bg");
1.1 nicm 770: gc.attr |= options_get_number(&wp->window->options, "mode-attr");
771:
1.18 nicm 772: /* Find top of screen. */
1.1 nicm 773: ty = screen_hsize(&wp->base) - data->oy;
774:
775: /* Adjust the selection. */
776: sx = data->selx;
777: sy = data->sely;
1.18 nicm 778: if (sy < ty) { /* above screen */
1.1 nicm 779: sx = 0;
780: sy = 0;
1.18 nicm 781: } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */
1.1 nicm 782: sx = screen_size_x(s) - 1;
783: sy = screen_size_y(s) - 1;
1.18 nicm 784: } else
1.1 nicm 785: sy -= ty;
786: sy = screen_hsize(s) + sy;
787:
788: screen_set_selection(
789: s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc);
790: return (1);
791: }
792:
793: void
794: window_copy_copy_selection(struct window_pane *wp, struct client *c)
795: {
796: struct window_copy_mode_data *data = wp->modedata;
797: struct screen *s = &data->screen;
798: char *buf;
799: size_t off;
800: u_int i, xx, yy, sx, sy, ex, ey, limit;
801:
802: if (!s->sel.flag)
803: return;
804:
805: buf = xmalloc(1);
806: off = 0;
807:
808: *buf = '\0';
809:
810: /*
811: * The selection extends from selx,sely to (adjusted) cx,cy on
812: * the base screen.
813: */
814:
815: /* Find start and end. */
1.18 nicm 816: xx = data->cx;
1.1 nicm 817: yy = screen_hsize(&wp->base) + data->cy - data->oy;
1.2 nicm 818: if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1.1 nicm 819: sx = xx; sy = yy;
820: ex = data->selx; ey = data->sely;
821: } else {
822: sx = data->selx; sy = data->sely;
823: ex = xx; ey = yy;
824: }
825:
826: /* Trim ex to end of line. */
827: xx = window_copy_find_length(wp, ey);
828: if (ex > xx)
829: ex = xx;
830:
831: /* Copy the lines. */
832: if (sy == ey)
833: window_copy_copy_line(wp, &buf, &off, sy, sx, ex);
834: else {
1.16 nicm 835: xx = screen_size_x(s);
1.1 nicm 836: window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
837: if (ey - sy > 1) {
1.16 nicm 838: for (i = sy + 1; i < ey; i++)
1.1 nicm 839: window_copy_copy_line(wp, &buf, &off, i, 0, xx);
840: }
841: window_copy_copy_line(wp, &buf, &off, ey, 0, ex);
842: }
843:
844: /* Terminate buffer, overwriting final \n. */
845: if (off != 0)
846: buf[off - 1] = '\0';
847:
848: /* Add the buffer to the stack. */
849: limit = options_get_number(&c->session->options, "buffer-limit");
850: paste_add(&c->session->buffers, buf, limit);
851: }
852:
853: void
854: window_copy_copy_line(struct window_pane *wp,
855: char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
856: {
1.16 nicm 857: struct grid *gd = wp->base.grid;
1.1 nicm 858: const struct grid_cell *gc;
859: const struct grid_utf8 *gu;
1.16 nicm 860: struct grid_line *gl;
861: u_int i, j, xx, wrapped = 0;
1.1 nicm 862:
863: if (sx > ex)
864: return;
865:
1.16 nicm 866: /*
867: * Work out if the line was wrapped at the screen edge and all of it is
868: * on screen.
869: */
870: gl = &gd->linedata[sy];
871: if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
872: wrapped = 1;
873:
874: /* If the line was wrapped, don't strip spaces (use the full length). */
875: if (wrapped)
876: xx = gl->cellsize;
877: else
878: xx = window_copy_find_length(wp, sy);
1.1 nicm 879: if (ex > xx)
880: ex = xx;
881: if (sx > xx)
882: sx = xx;
883:
884: if (sx < ex) {
885: for (i = sx; i < ex; i++) {
1.16 nicm 886: gc = grid_peek_cell(gd, i, sy);
1.1 nicm 887: if (gc->flags & GRID_FLAG_PADDING)
888: continue;
889: if (!(gc->flags & GRID_FLAG_UTF8)) {
890: *buf = xrealloc(*buf, 1, (*off) + 1);
891: (*buf)[(*off)++] = gc->data;
892: } else {
1.16 nicm 893: gu = grid_peek_utf8(gd, i, sy);
1.1 nicm 894: *buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE);
895: for (j = 0; j < UTF8_SIZE; j++) {
896: if (gu->data[j] == 0xff)
897: break;
898: (*buf)[(*off)++] = gu->data[j];
899: }
900: }
901: }
902: }
903:
1.16 nicm 904: /* Only add a newline if the line wasn't wrapped. */
905: if (!wrapped) {
906: *buf = xrealloc(*buf, 1, (*off) + 1);
907: (*buf)[(*off)++] = '\n';
908: }
1.1 nicm 909: }
910:
911: int
912: window_copy_is_space(struct window_pane *wp, u_int px, u_int py)
913: {
914: const struct grid_cell *gc;
915: const char *spaces = " -_@";
916:
917: gc = grid_peek_cell(wp->base.grid, px, py);
918: if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
919: return (0);
920: if (gc->data == 0x00 || gc->data == 0x7f)
921: return (0);
922: return (strchr(spaces, gc->data) != NULL);
923: }
924:
925: u_int
926: window_copy_find_length(struct window_pane *wp, u_int py)
927: {
928: const struct grid_cell *gc;
929: u_int px;
930:
1.4 nicm 931: /*
932: * If the pane has been resized, its grid can contain old overlong
933: * lines. grid_peek_cell does not allow accessing cells beyond the
934: * width of the grid, and screen_write_copy treats them as spaces, so
935: * ignore them here too.
936: */
1.15 nicm 937: px = wp->base.grid->linedata[py].cellsize;
1.4 nicm 938: if (px > screen_size_x(&wp->base))
939: px = screen_size_x(&wp->base);
1.1 nicm 940: while (px > 0) {
941: gc = grid_peek_cell(wp->base.grid, px - 1, py);
942: if (gc->flags & GRID_FLAG_UTF8)
943: break;
944: if (gc->data != ' ')
945: break;
946: px--;
947: }
948: return (px);
949: }
950:
951: void
1.5 nicm 952: window_copy_cursor_start_of_line(struct window_pane *wp)
953: {
954: struct window_copy_mode_data *data = wp->modedata;
955:
1.18 nicm 956: window_copy_update_cursor(wp, 0, data->cy);
1.5 nicm 957: if (window_copy_update_selection(wp))
958: window_copy_redraw_lines(wp, data->cy, 1);
1.6 nicm 959: }
960:
961: void
962: window_copy_cursor_back_to_indentation(struct window_pane *wp)
963: {
964: struct window_copy_mode_data *data = wp->modedata;
965: u_int px, py, xx;
966: const struct grid_cell *gc;
967:
968: px = 0;
969: py = screen_hsize(&wp->base) + data->cy - data->oy;
970: xx = window_copy_find_length(wp, py);
971:
972: /*
973: * Don't use window_copy_is_space because that treats some word
974: * delimiters as spaces.
975: */
976: while (px < xx) {
977: gc = grid_peek_cell(wp->base.grid, px, py);
978: if (gc->flags & GRID_FLAG_UTF8)
979: break;
980: if (gc->data != ' ')
981: break;
982: px++;
983: }
984:
1.18 nicm 985: window_copy_update_cursor(wp, px, data->cy);
986: if (window_copy_update_selection(wp))
987: window_copy_redraw_lines(wp, data->cy, 1);
1.5 nicm 988: }
989:
990: void
991: window_copy_cursor_end_of_line(struct window_pane *wp)
992: {
993: struct window_copy_mode_data *data = wp->modedata;
994: u_int px, py;
995:
996: py = screen_hsize(&wp->base) + data->cy - data->oy;
997: px = window_copy_find_length(wp, py);
998:
1.18 nicm 999: window_copy_update_cursor(wp, px, data->cy);
1000: if (window_copy_update_selection(wp))
1001: window_copy_redraw_lines(wp, data->cy, 1);
1.5 nicm 1002: }
1003:
1004: void
1.1 nicm 1005: window_copy_cursor_left(struct window_pane *wp)
1006: {
1007: struct window_copy_mode_data *data = wp->modedata;
1008:
1009: if (data->cx == 0) {
1.18 nicm 1010: window_copy_cursor_up(wp);
1011: window_copy_cursor_end_of_line(wp);
1.1 nicm 1012: } else {
1.18 nicm 1013: window_copy_update_cursor(wp, data->cx - 1, data->cy);
1.1 nicm 1014: if (window_copy_update_selection(wp))
1015: window_copy_redraw_lines(wp, data->cy, 1);
1016: }
1017: }
1018:
1019: void
1020: window_copy_cursor_right(struct window_pane *wp)
1021: {
1022: struct window_copy_mode_data *data = wp->modedata;
1023: u_int px, py;
1024:
1025: py = screen_hsize(&wp->base) + data->cy - data->oy;
1026: px = window_copy_find_length(wp, py);
1027:
1028: if (data->cx >= px) {
1029: window_copy_cursor_start_of_line(wp);
1030: window_copy_cursor_down(wp);
1031: } else {
1.18 nicm 1032: window_copy_update_cursor(wp, data->cx + 1, data->cy);
1.1 nicm 1033: if (window_copy_update_selection(wp))
1034: window_copy_redraw_lines(wp, data->cy, 1);
1035: }
1036: }
1037:
1038: void
1039: window_copy_cursor_up(struct window_pane *wp)
1040: {
1041: struct window_copy_mode_data *data = wp->modedata;
1042: u_int ox, oy, px, py;
1043:
1044: oy = screen_hsize(&wp->base) + data->cy - data->oy;
1045: ox = window_copy_find_length(wp, oy);
1046:
1047: if (data->cy == 0)
1048: window_copy_scroll_down(wp, 1);
1049: else {
1.18 nicm 1050: window_copy_update_cursor(wp, data->cx, data->cy - 1);
1.1 nicm 1051: if (window_copy_update_selection(wp))
1052: window_copy_redraw_lines(wp, data->cy, 2);
1053: }
1054:
1055: py = screen_hsize(&wp->base) + data->cy - data->oy;
1056: px = window_copy_find_length(wp, py);
1057:
1.18 nicm 1058: if (data->cx >= px || data->cx >= ox)
1.1 nicm 1059: window_copy_cursor_end_of_line(wp);
1060: }
1061:
1062: void
1063: window_copy_cursor_down(struct window_pane *wp)
1064: {
1065: struct window_copy_mode_data *data = wp->modedata;
1066: struct screen *s = &data->screen;
1067: u_int ox, oy, px, py;
1068:
1069: oy = screen_hsize(&wp->base) + data->cy - data->oy;
1070: ox = window_copy_find_length(wp, oy);
1071:
1072: if (data->cy == screen_size_y(s) - 1)
1073: window_copy_scroll_up(wp, 1);
1074: else {
1.18 nicm 1075: window_copy_update_cursor(wp, data->cx, data->cy + 1);
1.1 nicm 1076: if (window_copy_update_selection(wp))
1077: window_copy_redraw_lines(wp, data->cy - 1, 2);
1078: }
1079:
1080: py = screen_hsize(&wp->base) + data->cy - data->oy;
1081: px = window_copy_find_length(wp, py);
1082:
1.18 nicm 1083: if (data->cx >= px || data->cx >= ox)
1.1 nicm 1084: window_copy_cursor_end_of_line(wp);
1085: }
1086:
1087: void
1088: window_copy_cursor_next_word(struct window_pane *wp)
1089: {
1090: struct window_copy_mode_data *data = wp->modedata;
1091: struct screen *s = &data->screen;
1092: u_int px, py, xx, skip;
1093:
1.18 nicm 1094: px = data->cx;
1.1 nicm 1095: py = screen_hsize(&wp->base) + data->cy - data->oy;
1096: xx = window_copy_find_length(wp, py);
1097:
1098: skip = 1;
1099: if (px < xx) {
1100: /* If currently on a space, skip space. */
1101: if (window_copy_is_space(wp, px, py))
1102: skip = 0;
1103: } else
1104: skip = 0;
1105: for (;;) {
1106: if (px >= xx) {
1107: if (skip) {
1108: px = xx;
1109: break;
1110: }
1111:
1112: while (px >= xx) {
1113: if (data->cy == screen_size_y(s) - 1) {
1114: if (data->oy == 0)
1115: goto out;
1116: }
1117:
1118: px = 0;
1119: window_copy_cursor_down(wp);
1120:
1121: py =screen_hsize(
1122: &wp->base) + data->cy - data->oy;
1123: xx = window_copy_find_length(wp, py);
1124: }
1125: }
1126:
1127: if (skip) {
1128: /* Currently skipping non-space (until space). */
1129: if (window_copy_is_space(wp, px, py))
1130: break;
1131: } else {
1132: /* Currently skipping space (until non-space). */
1133: if (!window_copy_is_space(wp, px, py))
1134: skip = 1;
1135: }
1136:
1137: px++;
1138: }
1.18 nicm 1139:
1.1 nicm 1140: out:
1.18 nicm 1141: window_copy_update_cursor(wp, px, data->cy);
1142: if (window_copy_update_selection(wp))
1143: window_copy_redraw_lines(wp, data->cy, 1);
1.1 nicm 1144: }
1145:
1.8 nicm 1146: /* Move to the previous place where a word begins. */
1.1 nicm 1147: void
1148: window_copy_cursor_previous_word(struct window_pane *wp)
1149: {
1150: struct window_copy_mode_data *data = wp->modedata;
1.8 nicm 1151: u_int px, py;
1.1 nicm 1152:
1.18 nicm 1153: px = data->cx;
1.1 nicm 1154: py = screen_hsize(&wp->base) + data->cy - data->oy;
1155:
1.8 nicm 1156: /* Move back to the previous word character. */
1.1 nicm 1157: for (;;) {
1.8 nicm 1158: if (px > 0) {
1159: px--;
1160: if (!window_copy_is_space(wp, px, py))
1.1 nicm 1161: break;
1.8 nicm 1162: } else {
1163: if (data->cy == 0 &&
1164: (screen_hsize(&wp->base) == 0 ||
1165: data->oy >= screen_hsize(&wp->base) - 1))
1166: goto out;
1167: window_copy_cursor_up(wp);
1.1 nicm 1168:
1.8 nicm 1169: py = screen_hsize(
1170: &wp->base) + data->cy - data->oy;
1171: px = window_copy_find_length(wp, py);
1.1 nicm 1172: }
1.8 nicm 1173: }
1.1 nicm 1174:
1.8 nicm 1175: /* Move back to the beginning of this word. */
1176: while (px > 0 && !window_copy_is_space(wp, px - 1, py))
1.1 nicm 1177: px--;
1.8 nicm 1178:
1.1 nicm 1179: out:
1.18 nicm 1180: window_copy_update_cursor(wp, px, data->cy);
1181: if (window_copy_update_selection(wp))
1182: window_copy_redraw_lines(wp, data->cy, 1);
1.1 nicm 1183: }
1184:
1185: void
1186: window_copy_scroll_up(struct window_pane *wp, u_int ny)
1187: {
1188: struct window_copy_mode_data *data = wp->modedata;
1189: struct screen *s = &data->screen;
1190: struct screen_write_ctx ctx;
1191:
1192: if (data->oy < ny)
1193: ny = data->oy;
1194: if (ny == 0)
1195: return;
1196: data->oy -= ny;
1197: window_copy_update_selection(wp);
1198:
1199: screen_write_start(&ctx, wp, NULL);
1200: screen_write_cursormove(&ctx, 0, 0);
1201: screen_write_deleteline(&ctx, ny);
1202: window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1203: window_copy_write_line(wp, &ctx, 0);
1204: window_copy_write_line(wp, &ctx, 1);
1205: if (s->sel.flag && screen_size_y(s) > ny)
1206: window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1207: screen_write_cursormove(&ctx, data->cx, data->cy);
1208: screen_write_stop(&ctx);
1209: }
1210:
1211: void
1212: window_copy_scroll_down(struct window_pane *wp, u_int ny)
1213: {
1214: struct window_copy_mode_data *data = wp->modedata;
1215: struct screen *s = &data->screen;
1216: struct screen_write_ctx ctx;
1217:
1218: if (ny > screen_hsize(&wp->base))
1219: return;
1220:
1221: if (data->oy > screen_hsize(&wp->base) - ny)
1222: ny = screen_hsize(&wp->base) - data->oy;
1223: if (ny == 0)
1224: return;
1225: data->oy += ny;
1226: window_copy_update_selection(wp);
1227:
1228: screen_write_start(&ctx, wp, NULL);
1229: screen_write_cursormove(&ctx, 0, 0);
1230: screen_write_insertline(&ctx, ny);
1231: window_copy_write_lines(wp, &ctx, 0, ny);
1232: if (s->sel.flag && screen_size_y(s) > ny)
1233: window_copy_write_line(wp, &ctx, ny);
1234: else if (ny == 1) /* nuke position */
1235: window_copy_write_line(wp, &ctx, 1);
1236: screen_write_cursormove(&ctx, data->cx, data->cy);
1237: screen_write_stop(&ctx);
1238: }