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