Annotation of src/usr.bin/tmux/window-copy.c, Revision 1.24
1.24 ! nicm 1: /* $OpenBSD: window-copy.c,v 1.23 2009/08/18 09:51:51 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--) {
1.24 ! nicm 507: if (gd->sx - (ax - 1) < sgd->sx)
! 508: continue;
1.21 nicm 509: for (bx = 0; bx < sgd->sx; bx++) {
510: px = ax - 1 + bx;
511: if (!window_copy_search_compare(gd, px, py, sgd, bx))
512: break;
513: }
514: if (bx == sgd->sx) {
515: *ppx = ax - 1;
516: return (1);
517: }
518: }
519: return (0);
520: }
521:
522: void
523: window_copy_search_up(struct window_pane *wp, const char *searchstr)
524: {
525: struct window_copy_mode_data *data = wp->modedata;
526: struct screen *s = &wp->base, ss;
527: struct screen_write_ctx ctx;
528: struct grid *gd = s->grid, *sgd;
529: struct grid_cell gc;
530: size_t searchlen;
531: u_int i, last, fx, fy, px;
532: int utf8flag, n, wrapped;
533:
534: if (*searchstr == '\0')
535: return;
536: utf8flag = options_get_number(&wp->window->options, "utf8");
537: searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
538:
539: screen_init(&ss, searchlen, 1, 0);
540: screen_write_start(&ctx, NULL, &ss);
541: memcpy(&gc, &grid_default_cell, sizeof gc);
542: screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
543: screen_write_stop(&ctx);
544:
545: fx = data->cx;
546: fy = gd->hsize - data->oy + data->cy;
547:
548: if (fx == 0) {
549: if (fy == 0)
550: return;
551: fx = gd->sx - 1;
552: fy--;
553: } else
554: fx--;
555: n = wrapped = 0;
556:
557: retry:
558: sgd = ss.grid;
559: for (i = fy + 1; i > 0; i--) {
560: last = screen_size_x(s);
561: if (i == fy + 1)
562: last = fx;
563: n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
564: if (n) {
565: window_copy_scroll_to(wp, px, i - 1);
566: break;
567: }
568: }
569: if (!n && !wrapped) {
570: fx = gd->sx - 1;
571: fy = gd->hsize + gd->sy - 1;
572: wrapped = 1;
573: goto retry;
574: }
575:
576: screen_free(&ss);
577: }
578:
579: void
580: window_copy_search_down(struct window_pane *wp, const char *searchstr)
581: {
582: struct window_copy_mode_data *data = wp->modedata;
583: struct screen *s = &wp->base, ss;
584: struct screen_write_ctx ctx;
585: struct grid *gd = s->grid, *sgd;
586: struct grid_cell gc;
587: size_t searchlen;
588: u_int i, first, fx, fy, px;
589: int utf8flag, n, wrapped;
590:
591: if (*searchstr == '\0')
592: return;
593: utf8flag = options_get_number(&wp->window->options, "utf8");
594: searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
595:
596: screen_init(&ss, searchlen, 1, 0);
597: screen_write_start(&ctx, NULL, &ss);
598: memcpy(&gc, &grid_default_cell, sizeof gc);
599: screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
600: screen_write_stop(&ctx);
601:
602: fx = data->cx;
603: fy = gd->hsize - data->oy + data->cy;
604:
605: if (fx == gd->sx - 1) {
606: if (fy == gd->hsize + gd->sy)
607: return;
608: fx = 0;
609: fy++;
610: } else
611: fx++;
612: n = wrapped = 0;
613:
614: retry:
615: sgd = ss.grid;
616: for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
617: first = 0;
618: if (i == fy + 1)
619: first = fx;
620: n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
621: if (n) {
622: window_copy_scroll_to(wp, px, i - 1);
623: break;
624: }
625: }
626: if (!n && !wrapped) {
627: fx = 0;
628: fy = 0;
629: wrapped = 1;
630: goto retry;
631: }
632:
633: screen_free(&ss);
634: }
635:
636: void
637: window_copy_goto_line(struct window_pane *wp, const char *linestr)
638: {
639: struct window_copy_mode_data *data = wp->modedata;
640: const char *errstr;
641: u_int lineno;
642:
643: lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr);
644: if (errstr != NULL)
645: return;
646:
647: data->oy = lineno;
648: window_copy_redraw_screen(wp);
649: }
650:
651: void
1.18 nicm 652: window_copy_write_line(
653: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
1.1 nicm 654: {
655: struct window_copy_mode_data *data = wp->modedata;
656: struct screen *s = &data->screen;
657: struct grid_cell gc;
658: char hdr[32];
1.21 nicm 659: size_t last, xoff = 0, size = 0;
660:
661: memcpy(&gc, &grid_default_cell, sizeof gc);
662: gc.fg = options_get_number(&wp->window->options, "mode-fg");
663: gc.bg = options_get_number(&wp->window->options, "mode-bg");
664: gc.attr |= options_get_number(&wp->window->options, "mode-attr");
1.1 nicm 665:
1.21 nicm 666: last = screen_size_y(s) - 1;
1.1 nicm 667: if (py == 0) {
668: size = xsnprintf(hdr, sizeof hdr,
1.18 nicm 669: "[%u/%u]", data->oy, screen_hsize(&wp->base));
1.1 nicm 670: screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
671: screen_write_puts(ctx, &gc, "%s", hdr);
1.21 nicm 672: } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
673: xoff = size = xsnprintf(hdr, sizeof hdr,
674: "%s: %s", data->inputprompt, data->inputstr);
675: screen_write_cursormove(ctx, 0, last);
676: screen_write_puts(ctx, &gc, "%s", hdr);
1.1 nicm 677: } else
678: size = 0;
679:
1.21 nicm 680: screen_write_cursormove(ctx, xoff, py);
681: screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) -
1.1 nicm 682: data->oy) + py, screen_size_x(s) - size, 1);
1.18 nicm 683:
684: if (py == data->cy && data->cx == screen_size_x(s)) {
685: memcpy(&gc, &grid_default_cell, sizeof gc);
686: screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
687: screen_write_putc(ctx, &gc, '$');
688: }
1.1 nicm 689: }
690:
691: void
692: window_copy_write_lines(
693: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
694: {
695: u_int yy;
696:
697: for (yy = py; yy < py + ny; yy++)
698: window_copy_write_line(wp, ctx, py);
699: }
700:
701: void
702: window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
703: {
704: struct window_copy_mode_data *data = wp->modedata;
705: struct screen_write_ctx ctx;
706: u_int i;
707:
708: screen_write_start(&ctx, wp, NULL);
709: for (i = py; i < py + ny; i++)
710: window_copy_write_line(wp, &ctx, i);
711: screen_write_cursormove(&ctx, data->cx, data->cy);
712: screen_write_stop(&ctx);
713: }
714:
715: void
716: window_copy_redraw_screen(struct window_pane *wp)
717: {
718: struct window_copy_mode_data *data = wp->modedata;
719:
720: window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
721: }
722:
723: void
1.18 nicm 724: window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1.1 nicm 725: {
726: struct window_copy_mode_data *data = wp->modedata;
1.18 nicm 727: struct screen *s = &data->screen;
1.1 nicm 728: struct screen_write_ctx ctx;
1.18 nicm 729: u_int old_cx, old_cy;
1.1 nicm 730:
1.18 nicm 731: old_cx = data->cx; old_cy = data->cy;
732: data->cx = cx; data->cy = cy;
733: if (old_cx == screen_size_x(s))
734: window_copy_redraw_lines(wp, old_cy, 1);
735: if (data->cx == screen_size_x(s))
736: window_copy_redraw_lines(wp, data->cy, 1);
737: else {
738: screen_write_start(&ctx, wp, NULL);
739: screen_write_cursormove(&ctx, data->cx, data->cy);
740: screen_write_stop(&ctx);
741: }
1.1 nicm 742: }
743:
744: void
745: window_copy_start_selection(struct window_pane *wp)
746: {
747: struct window_copy_mode_data *data = wp->modedata;
748: struct screen *s = &data->screen;
749:
1.18 nicm 750: data->selx = data->cx;
1.1 nicm 751: data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
752:
753: s->sel.flag = 1;
754: window_copy_update_selection(wp);
755: }
756:
757: int
758: window_copy_update_selection(struct window_pane *wp)
759: {
760: struct window_copy_mode_data *data = wp->modedata;
761: struct screen *s = &data->screen;
762: struct grid_cell gc;
1.18 nicm 763: u_int sx, sy, ty;
1.1 nicm 764:
765: if (!s->sel.flag)
766: return (0);
767:
768: /* Set colours. */
769: memcpy(&gc, &grid_default_cell, sizeof gc);
1.14 nicm 770: gc.fg = options_get_number(&wp->window->options, "mode-fg");
771: gc.bg = options_get_number(&wp->window->options, "mode-bg");
1.1 nicm 772: gc.attr |= options_get_number(&wp->window->options, "mode-attr");
773:
1.18 nicm 774: /* Find top of screen. */
1.1 nicm 775: ty = screen_hsize(&wp->base) - data->oy;
776:
777: /* Adjust the selection. */
778: sx = data->selx;
779: sy = data->sely;
1.18 nicm 780: if (sy < ty) { /* above screen */
1.1 nicm 781: sx = 0;
782: sy = 0;
1.18 nicm 783: } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */
1.1 nicm 784: sx = screen_size_x(s) - 1;
785: sy = screen_size_y(s) - 1;
1.18 nicm 786: } else
1.1 nicm 787: sy -= ty;
788: sy = screen_hsize(s) + sy;
789:
790: screen_set_selection(
791: s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc);
792: return (1);
793: }
794:
795: void
796: window_copy_copy_selection(struct window_pane *wp, struct client *c)
797: {
798: struct window_copy_mode_data *data = wp->modedata;
799: struct screen *s = &data->screen;
800: char *buf;
801: size_t off;
802: u_int i, xx, yy, sx, sy, ex, ey, limit;
803:
804: if (!s->sel.flag)
805: return;
806:
807: buf = xmalloc(1);
808: off = 0;
809:
810: *buf = '\0';
811:
812: /*
813: * The selection extends from selx,sely to (adjusted) cx,cy on
814: * the base screen.
815: */
816:
817: /* Find start and end. */
1.18 nicm 818: xx = data->cx;
1.1 nicm 819: yy = screen_hsize(&wp->base) + data->cy - data->oy;
1.2 nicm 820: if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1.1 nicm 821: sx = xx; sy = yy;
822: ex = data->selx; ey = data->sely;
823: } else {
824: sx = data->selx; sy = data->sely;
825: ex = xx; ey = yy;
826: }
827:
828: /* Trim ex to end of line. */
829: xx = window_copy_find_length(wp, ey);
830: if (ex > xx)
831: ex = xx;
832:
833: /* Copy the lines. */
834: if (sy == ey)
835: window_copy_copy_line(wp, &buf, &off, sy, sx, ex);
836: else {
1.16 nicm 837: xx = screen_size_x(s);
1.1 nicm 838: window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
839: if (ey - sy > 1) {
1.16 nicm 840: for (i = sy + 1; i < ey; i++)
1.1 nicm 841: window_copy_copy_line(wp, &buf, &off, i, 0, xx);
842: }
843: window_copy_copy_line(wp, &buf, &off, ey, 0, ex);
844: }
845:
846: /* Terminate buffer, overwriting final \n. */
847: if (off != 0)
848: buf[off - 1] = '\0';
849:
850: /* Add the buffer to the stack. */
851: limit = options_get_number(&c->session->options, "buffer-limit");
852: paste_add(&c->session->buffers, buf, limit);
853: }
854:
855: void
856: window_copy_copy_line(struct window_pane *wp,
857: char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
858: {
1.16 nicm 859: struct grid *gd = wp->base.grid;
1.1 nicm 860: const struct grid_cell *gc;
861: const struct grid_utf8 *gu;
1.16 nicm 862: struct grid_line *gl;
863: u_int i, j, xx, wrapped = 0;
1.1 nicm 864:
865: if (sx > ex)
866: return;
867:
1.16 nicm 868: /*
869: * Work out if the line was wrapped at the screen edge and all of it is
870: * on screen.
871: */
872: gl = &gd->linedata[sy];
873: if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
874: wrapped = 1;
875:
876: /* If the line was wrapped, don't strip spaces (use the full length). */
877: if (wrapped)
878: xx = gl->cellsize;
879: else
880: xx = window_copy_find_length(wp, sy);
1.1 nicm 881: if (ex > xx)
882: ex = xx;
883: if (sx > xx)
884: sx = xx;
885:
886: if (sx < ex) {
887: for (i = sx; i < ex; i++) {
1.16 nicm 888: gc = grid_peek_cell(gd, i, sy);
1.1 nicm 889: if (gc->flags & GRID_FLAG_PADDING)
890: continue;
891: if (!(gc->flags & GRID_FLAG_UTF8)) {
892: *buf = xrealloc(*buf, 1, (*off) + 1);
893: (*buf)[(*off)++] = gc->data;
894: } else {
1.16 nicm 895: gu = grid_peek_utf8(gd, i, sy);
1.1 nicm 896: *buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE);
897: for (j = 0; j < UTF8_SIZE; j++) {
898: if (gu->data[j] == 0xff)
899: break;
900: (*buf)[(*off)++] = gu->data[j];
901: }
902: }
903: }
904: }
905:
1.16 nicm 906: /* Only add a newline if the line wasn't wrapped. */
907: if (!wrapped) {
908: *buf = xrealloc(*buf, 1, (*off) + 1);
909: (*buf)[(*off)++] = '\n';
910: }
1.1 nicm 911: }
912:
913: int
914: window_copy_is_space(struct window_pane *wp, u_int px, u_int py)
915: {
916: const struct grid_cell *gc;
917: const char *spaces = " -_@";
918:
919: gc = grid_peek_cell(wp->base.grid, px, py);
920: if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
921: return (0);
922: if (gc->data == 0x00 || gc->data == 0x7f)
923: return (0);
924: return (strchr(spaces, gc->data) != NULL);
925: }
926:
927: u_int
928: window_copy_find_length(struct window_pane *wp, u_int py)
929: {
930: const struct grid_cell *gc;
931: u_int px;
932:
1.4 nicm 933: /*
934: * If the pane has been resized, its grid can contain old overlong
935: * lines. grid_peek_cell does not allow accessing cells beyond the
936: * width of the grid, and screen_write_copy treats them as spaces, so
937: * ignore them here too.
938: */
1.15 nicm 939: px = wp->base.grid->linedata[py].cellsize;
1.4 nicm 940: if (px > screen_size_x(&wp->base))
941: px = screen_size_x(&wp->base);
1.1 nicm 942: while (px > 0) {
943: gc = grid_peek_cell(wp->base.grid, px - 1, py);
944: if (gc->flags & GRID_FLAG_UTF8)
945: break;
946: if (gc->data != ' ')
947: break;
948: px--;
949: }
950: return (px);
951: }
952:
953: void
1.5 nicm 954: window_copy_cursor_start_of_line(struct window_pane *wp)
955: {
956: struct window_copy_mode_data *data = wp->modedata;
957:
1.18 nicm 958: window_copy_update_cursor(wp, 0, data->cy);
1.5 nicm 959: if (window_copy_update_selection(wp))
960: window_copy_redraw_lines(wp, data->cy, 1);
1.6 nicm 961: }
962:
963: void
964: window_copy_cursor_back_to_indentation(struct window_pane *wp)
965: {
966: struct window_copy_mode_data *data = wp->modedata;
967: u_int px, py, xx;
968: const struct grid_cell *gc;
969:
970: px = 0;
971: py = screen_hsize(&wp->base) + data->cy - data->oy;
972: xx = window_copy_find_length(wp, py);
973:
974: /*
975: * Don't use window_copy_is_space because that treats some word
976: * delimiters as spaces.
977: */
978: while (px < xx) {
979: gc = grid_peek_cell(wp->base.grid, px, py);
980: if (gc->flags & GRID_FLAG_UTF8)
981: break;
982: if (gc->data != ' ')
983: break;
984: px++;
985: }
986:
1.18 nicm 987: window_copy_update_cursor(wp, px, data->cy);
988: if (window_copy_update_selection(wp))
989: window_copy_redraw_lines(wp, data->cy, 1);
1.5 nicm 990: }
991:
992: void
993: window_copy_cursor_end_of_line(struct window_pane *wp)
994: {
995: struct window_copy_mode_data *data = wp->modedata;
996: u_int px, py;
997:
998: py = screen_hsize(&wp->base) + data->cy - data->oy;
999: px = window_copy_find_length(wp, py);
1000:
1.18 nicm 1001: window_copy_update_cursor(wp, px, data->cy);
1002: if (window_copy_update_selection(wp))
1003: window_copy_redraw_lines(wp, data->cy, 1);
1.5 nicm 1004: }
1005:
1006: void
1.1 nicm 1007: window_copy_cursor_left(struct window_pane *wp)
1008: {
1009: struct window_copy_mode_data *data = wp->modedata;
1010:
1011: if (data->cx == 0) {
1.18 nicm 1012: window_copy_cursor_up(wp);
1013: window_copy_cursor_end_of_line(wp);
1.1 nicm 1014: } else {
1.18 nicm 1015: window_copy_update_cursor(wp, data->cx - 1, data->cy);
1.1 nicm 1016: if (window_copy_update_selection(wp))
1017: window_copy_redraw_lines(wp, data->cy, 1);
1018: }
1019: }
1020:
1021: void
1022: window_copy_cursor_right(struct window_pane *wp)
1023: {
1024: struct window_copy_mode_data *data = wp->modedata;
1025: u_int px, py;
1026:
1027: py = screen_hsize(&wp->base) + data->cy - data->oy;
1028: px = window_copy_find_length(wp, py);
1029:
1030: if (data->cx >= px) {
1031: window_copy_cursor_start_of_line(wp);
1032: window_copy_cursor_down(wp);
1033: } else {
1.18 nicm 1034: window_copy_update_cursor(wp, data->cx + 1, data->cy);
1.1 nicm 1035: if (window_copy_update_selection(wp))
1036: window_copy_redraw_lines(wp, data->cy, 1);
1037: }
1038: }
1039:
1040: void
1041: window_copy_cursor_up(struct window_pane *wp)
1042: {
1043: struct window_copy_mode_data *data = wp->modedata;
1044: u_int ox, oy, px, py;
1045:
1046: oy = screen_hsize(&wp->base) + data->cy - data->oy;
1047: ox = window_copy_find_length(wp, oy);
1048:
1049: if (data->cy == 0)
1050: window_copy_scroll_down(wp, 1);
1051: else {
1.18 nicm 1052: window_copy_update_cursor(wp, data->cx, data->cy - 1);
1.1 nicm 1053: if (window_copy_update_selection(wp))
1054: window_copy_redraw_lines(wp, data->cy, 2);
1055: }
1056:
1057: py = screen_hsize(&wp->base) + data->cy - data->oy;
1058: px = window_copy_find_length(wp, py);
1059:
1.18 nicm 1060: if (data->cx >= px || data->cx >= ox)
1.1 nicm 1061: window_copy_cursor_end_of_line(wp);
1062: }
1063:
1064: void
1065: window_copy_cursor_down(struct window_pane *wp)
1066: {
1067: struct window_copy_mode_data *data = wp->modedata;
1068: struct screen *s = &data->screen;
1069: u_int ox, oy, px, py;
1070:
1071: oy = screen_hsize(&wp->base) + data->cy - data->oy;
1072: ox = window_copy_find_length(wp, oy);
1073:
1074: if (data->cy == screen_size_y(s) - 1)
1075: window_copy_scroll_up(wp, 1);
1076: else {
1.18 nicm 1077: window_copy_update_cursor(wp, data->cx, data->cy + 1);
1.1 nicm 1078: if (window_copy_update_selection(wp))
1079: window_copy_redraw_lines(wp, data->cy - 1, 2);
1080: }
1081:
1082: py = screen_hsize(&wp->base) + data->cy - data->oy;
1083: px = window_copy_find_length(wp, py);
1084:
1.18 nicm 1085: if (data->cx >= px || data->cx >= ox)
1.1 nicm 1086: window_copy_cursor_end_of_line(wp);
1087: }
1088:
1089: void
1090: window_copy_cursor_next_word(struct window_pane *wp)
1091: {
1092: struct window_copy_mode_data *data = wp->modedata;
1093: struct screen *s = &data->screen;
1094: u_int px, py, xx, skip;
1095:
1.18 nicm 1096: px = data->cx;
1.1 nicm 1097: py = screen_hsize(&wp->base) + data->cy - data->oy;
1098: xx = window_copy_find_length(wp, py);
1099:
1100: skip = 1;
1101: if (px < xx) {
1102: /* If currently on a space, skip space. */
1103: if (window_copy_is_space(wp, px, py))
1104: skip = 0;
1105: } else
1106: skip = 0;
1107: for (;;) {
1108: if (px >= xx) {
1109: if (skip) {
1110: px = xx;
1111: break;
1112: }
1113:
1114: while (px >= xx) {
1115: if (data->cy == screen_size_y(s) - 1) {
1116: if (data->oy == 0)
1117: goto out;
1118: }
1119:
1120: px = 0;
1121: window_copy_cursor_down(wp);
1122:
1123: py =screen_hsize(
1124: &wp->base) + data->cy - data->oy;
1125: xx = window_copy_find_length(wp, py);
1126: }
1127: }
1128:
1129: if (skip) {
1130: /* Currently skipping non-space (until space). */
1131: if (window_copy_is_space(wp, px, py))
1132: break;
1133: } else {
1134: /* Currently skipping space (until non-space). */
1135: if (!window_copy_is_space(wp, px, py))
1136: skip = 1;
1137: }
1138:
1139: px++;
1140: }
1.18 nicm 1141:
1.1 nicm 1142: out:
1.18 nicm 1143: window_copy_update_cursor(wp, px, data->cy);
1144: if (window_copy_update_selection(wp))
1145: window_copy_redraw_lines(wp, data->cy, 1);
1.1 nicm 1146: }
1147:
1.8 nicm 1148: /* Move to the previous place where a word begins. */
1.1 nicm 1149: void
1150: window_copy_cursor_previous_word(struct window_pane *wp)
1151: {
1152: struct window_copy_mode_data *data = wp->modedata;
1.8 nicm 1153: u_int px, py;
1.1 nicm 1154:
1.18 nicm 1155: px = data->cx;
1.1 nicm 1156: py = screen_hsize(&wp->base) + data->cy - data->oy;
1157:
1.8 nicm 1158: /* Move back to the previous word character. */
1.1 nicm 1159: for (;;) {
1.8 nicm 1160: if (px > 0) {
1161: px--;
1162: if (!window_copy_is_space(wp, px, py))
1.1 nicm 1163: break;
1.8 nicm 1164: } else {
1165: if (data->cy == 0 &&
1166: (screen_hsize(&wp->base) == 0 ||
1167: data->oy >= screen_hsize(&wp->base) - 1))
1168: goto out;
1169: window_copy_cursor_up(wp);
1.1 nicm 1170:
1.8 nicm 1171: py = screen_hsize(
1172: &wp->base) + data->cy - data->oy;
1173: px = window_copy_find_length(wp, py);
1.1 nicm 1174: }
1.8 nicm 1175: }
1.1 nicm 1176:
1.8 nicm 1177: /* Move back to the beginning of this word. */
1178: while (px > 0 && !window_copy_is_space(wp, px - 1, py))
1.1 nicm 1179: px--;
1.8 nicm 1180:
1.1 nicm 1181: out:
1.18 nicm 1182: window_copy_update_cursor(wp, px, data->cy);
1183: if (window_copy_update_selection(wp))
1184: window_copy_redraw_lines(wp, data->cy, 1);
1.1 nicm 1185: }
1186:
1187: void
1188: window_copy_scroll_up(struct window_pane *wp, u_int ny)
1189: {
1190: struct window_copy_mode_data *data = wp->modedata;
1191: struct screen *s = &data->screen;
1192: struct screen_write_ctx ctx;
1193:
1194: if (data->oy < ny)
1195: ny = data->oy;
1196: if (ny == 0)
1197: return;
1198: data->oy -= ny;
1199: window_copy_update_selection(wp);
1200:
1201: screen_write_start(&ctx, wp, NULL);
1202: screen_write_cursormove(&ctx, 0, 0);
1203: screen_write_deleteline(&ctx, ny);
1204: window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1205: window_copy_write_line(wp, &ctx, 0);
1206: window_copy_write_line(wp, &ctx, 1);
1207: if (s->sel.flag && screen_size_y(s) > ny)
1208: window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1209: screen_write_cursormove(&ctx, data->cx, data->cy);
1210: screen_write_stop(&ctx);
1211: }
1212:
1213: void
1214: window_copy_scroll_down(struct window_pane *wp, u_int ny)
1215: {
1216: struct window_copy_mode_data *data = wp->modedata;
1217: struct screen *s = &data->screen;
1218: struct screen_write_ctx ctx;
1219:
1220: if (ny > screen_hsize(&wp->base))
1221: return;
1222:
1223: if (data->oy > screen_hsize(&wp->base) - ny)
1224: ny = screen_hsize(&wp->base) - data->oy;
1225: if (ny == 0)
1226: return;
1227: data->oy += ny;
1228: window_copy_update_selection(wp);
1229:
1230: screen_write_start(&ctx, wp, NULL);
1231: screen_write_cursormove(&ctx, 0, 0);
1232: screen_write_insertline(&ctx, ny);
1233: window_copy_write_lines(wp, &ctx, 0, ny);
1234: if (s->sel.flag && screen_size_y(s) > ny)
1235: window_copy_write_line(wp, &ctx, ny);
1236: else if (ny == 1) /* nuke position */
1237: window_copy_write_line(wp, &ctx, 1);
1238: screen_write_cursormove(&ctx, data->cx, data->cy);
1239: screen_write_stop(&ctx);
1240: }