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