Annotation of src/usr.bin/tmux/window-copy.c, Revision 1.53
1.53 ! nicm 1: /* $OpenBSD$ */
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.50 nicm 31: int window_copy_key_numeric_prefix(struct window_pane *, int);
1.1 nicm 32: void window_copy_mouse(
1.35 nicm 33: struct window_pane *, struct client *, struct mouse_event *);
1.1 nicm 34:
35: void window_copy_redraw_lines(struct window_pane *, u_int, u_int);
36: void window_copy_redraw_screen(struct window_pane *);
37: void window_copy_write_line(
1.35 nicm 38: struct window_pane *, struct screen_write_ctx *, u_int);
1.1 nicm 39: void window_copy_write_lines(
1.35 nicm 40: struct window_pane *, struct screen_write_ctx *, u_int, u_int);
1.1 nicm 41:
1.21 nicm 42: void window_copy_scroll_to(struct window_pane *, u_int, u_int);
43: int window_copy_search_compare(
44: struct grid *, u_int, u_int, struct grid *, u_int);
45: int window_copy_search_lr(
46: struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
47: int window_copy_search_rl(
48: struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
49: void window_copy_search_up(struct window_pane *, const char *);
50: void window_copy_search_down(struct window_pane *, const char *);
51: void window_copy_goto_line(struct window_pane *, const char *);
1.18 nicm 52: void window_copy_update_cursor(struct window_pane *, u_int, u_int);
1.1 nicm 53: void window_copy_start_selection(struct window_pane *);
54: int window_copy_update_selection(struct window_pane *);
55: void window_copy_copy_selection(struct window_pane *, struct client *);
1.47 nicm 56: void window_copy_clear_selection(struct window_pane *);
1.1 nicm 57: void window_copy_copy_line(
58: struct window_pane *, char **, size_t *, u_int, u_int, u_int);
1.41 nicm 59: int window_copy_in_set(struct window_pane *, u_int, u_int, const char *);
1.1 nicm 60: u_int window_copy_find_length(struct window_pane *, u_int);
61: void window_copy_cursor_start_of_line(struct window_pane *);
1.6 nicm 62: void window_copy_cursor_back_to_indentation(struct window_pane *);
1.1 nicm 63: void window_copy_cursor_end_of_line(struct window_pane *);
64: void window_copy_cursor_left(struct window_pane *);
65: void window_copy_cursor_right(struct window_pane *);
1.28 nicm 66: void window_copy_cursor_up(struct window_pane *, int);
67: void window_copy_cursor_down(struct window_pane *, int);
1.52 nicm 68: void window_copy_cursor_jump(struct window_pane *);
69: void window_copy_cursor_jump_back(struct window_pane *);
1.41 nicm 70: void window_copy_cursor_next_word(struct window_pane *, const char *);
71: void window_copy_cursor_next_word_end(struct window_pane *, const char *);
72: void window_copy_cursor_previous_word(struct window_pane *, const char *);
1.1 nicm 73: void window_copy_scroll_up(struct window_pane *, u_int);
74: void window_copy_scroll_down(struct window_pane *, u_int);
1.42 nicm 75: void window_copy_rectangle_toggle(struct window_pane *);
1.1 nicm 76:
77: const struct window_mode window_copy_mode = {
78: window_copy_init,
79: window_copy_free,
80: window_copy_resize,
81: window_copy_key,
82: window_copy_mouse,
83: NULL,
84: };
85:
1.21 nicm 86: enum window_copy_input_type {
87: WINDOW_COPY_OFF,
1.50 nicm 88: WINDOW_COPY_NUMERICPREFIX,
1.21 nicm 89: WINDOW_COPY_SEARCHUP,
90: WINDOW_COPY_SEARCHDOWN,
1.52 nicm 91: WINDOW_COPY_JUMPFORWARD,
92: WINDOW_COPY_JUMPBACK,
1.21 nicm 93: WINDOW_COPY_GOTOLINE,
94: };
95:
1.1 nicm 96: struct window_copy_mode_data {
97: struct screen screen;
98:
1.21 nicm 99: struct mode_key_data mdata;
100:
101: u_int oy;
102:
103: u_int selx;
104: u_int sely;
1.1 nicm 105:
1.42 nicm 106: u_int rectflag; /* are we in rectangle copy mode? */
107:
1.21 nicm 108: u_int cx;
109: u_int cy;
1.1 nicm 110:
1.25 nicm 111: u_int lastcx; /* position in last line with content */
112: u_int lastsx; /* size of last line with content */
113:
1.21 nicm 114: enum window_copy_input_type inputtype;
115: const char *inputprompt;
116: char *inputstr;
1.1 nicm 117:
1.50 nicm 118: u_int numprefix;
119:
1.21 nicm 120: enum window_copy_input_type searchtype;
121: char *searchstr;
1.52 nicm 122:
123: enum window_copy_input_type jumptype;
124: char jumpchar;
1.1 nicm 125: };
126:
127: struct screen *
128: window_copy_init(struct window_pane *wp)
129: {
130: struct window_copy_mode_data *data;
131: struct screen *s;
132: struct screen_write_ctx ctx;
133: u_int i;
1.10 nicm 134: int keys;
1.1 nicm 135:
136: wp->modedata = data = xmalloc(sizeof *data);
137: data->oy = 0;
138: data->cx = wp->base.cx;
139: data->cy = wp->base.cy;
140:
1.25 nicm 141: data->lastcx = 0;
142: data->lastsx = 0;
143:
1.42 nicm 144: data->rectflag = 0;
145:
1.21 nicm 146: data->inputtype = WINDOW_COPY_OFF;
147: data->inputprompt = NULL;
148: data->inputstr = xstrdup("");
1.50 nicm 149: data->numprefix = 0;
1.21 nicm 150:
151: data->searchtype = WINDOW_COPY_OFF;
152: data->searchstr = NULL;
153:
1.46 nicm 154: wp->flags |= PANE_FREEZE;
155: bufferevent_disable(wp->event, EV_READ|EV_WRITE);
156:
1.52 nicm 157: data->jumptype = WINDOW_COPY_OFF;
158: data->jumpchar = '\0';
159:
1.1 nicm 160: s = &data->screen;
161: screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
1.13 nicm 162: if (options_get_number(&wp->window->options, "mode-mouse"))
163: s->mode |= MODE_MOUSE;
1.1 nicm 164:
1.10 nicm 165: keys = options_get_number(&wp->window->options, "mode-keys");
166: if (keys == MODEKEY_EMACS)
1.12 nicm 167: mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
1.10 nicm 168: else
1.12 nicm 169: mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
1.1 nicm 170:
171: s->cx = data->cx;
172: s->cy = data->cy;
173:
174: screen_write_start(&ctx, NULL, s);
175: for (i = 0; i < screen_size_y(s); i++)
176: window_copy_write_line(wp, &ctx, i);
177: screen_write_cursormove(&ctx, data->cx, data->cy);
178: screen_write_stop(&ctx);
179:
180: return (s);
181: }
182:
183: void
184: window_copy_free(struct window_pane *wp)
185: {
186: struct window_copy_mode_data *data = wp->modedata;
1.46 nicm 187:
188: wp->flags &= ~PANE_FREEZE;
189: bufferevent_enable(wp->event, EV_READ|EV_WRITE);
1.1 nicm 190:
1.21 nicm 191: if (data->searchstr != NULL)
192: xfree(data->searchstr);
193: xfree(data->inputstr);
194:
1.1 nicm 195: screen_free(&data->screen);
1.21 nicm 196:
1.1 nicm 197: xfree(data);
198: }
199:
200: void
201: window_copy_pageup(struct window_pane *wp)
202: {
203: struct window_copy_mode_data *data = wp->modedata;
204: struct screen *s = &data->screen;
1.19 nicm 205: u_int n;
1.1 nicm 206:
1.19 nicm 207: n = 1;
208: if (screen_size_y(s) > 2)
209: n = screen_size_y(s) - 2;
210: if (data->oy + n > screen_hsize(&wp->base))
1.1 nicm 211: data->oy = screen_hsize(&wp->base);
212: else
1.19 nicm 213: data->oy += n;
1.1 nicm 214: window_copy_update_selection(wp);
215: window_copy_redraw_screen(wp);
216: }
217:
218: void
219: window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
220: {
221: struct window_copy_mode_data *data = wp->modedata;
222: struct screen *s = &data->screen;
223: struct screen_write_ctx ctx;
224:
225: screen_resize(s, sx, sy);
1.35 nicm 226:
1.18 nicm 227: if (data->cy > sy - 1)
228: data->cy = sy - 1;
229: if (data->cx > sx)
230: data->cx = sx;
231:
1.47 nicm 232: window_copy_clear_selection(wp);
1.18 nicm 233:
1.1 nicm 234: screen_write_start(&ctx, NULL, s);
235: window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
236: screen_write_stop(&ctx);
1.18 nicm 237:
1.17 nicm 238: window_copy_redraw_screen(wp);
1.1 nicm 239: }
240:
241: void
242: window_copy_key(struct window_pane *wp, struct client *c, int key)
243: {
1.48 nicm 244: const char *word_separators;
1.1 nicm 245: struct window_copy_mode_data *data = wp->modedata;
246: struct screen *s = &data->screen;
1.50 nicm 247: u_int n, np;
1.21 nicm 248: int keys;
1.43 nicm 249: enum mode_key_cmd cmd;
1.21 nicm 250:
1.50 nicm 251: np = data->numprefix;
252: if (np == 0)
253: np = 1;
254:
1.52 nicm 255: if (data->inputtype == WINDOW_COPY_JUMPFORWARD
256: || data->inputtype == WINDOW_COPY_JUMPBACK) {
257: /* Ignore keys with modifiers. */
258: if ((key & 0xff00) == 0) {
259: data->jumpchar = key;
260: if (data->inputtype == WINDOW_COPY_JUMPFORWARD) {
261: for (; np != 0; np--)
262: window_copy_cursor_jump(wp);
263: } else {
264: for (; np != 0; np--)
265: window_copy_cursor_jump_back(wp);
266: }
267: }
268: data->jumptype = data->inputtype;
269: data->inputtype = WINDOW_COPY_OFF;
270: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
271: return;
272: } if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
1.50 nicm 273: if (window_copy_key_numeric_prefix(wp, key) == 0)
274: return;
275: data->inputtype = WINDOW_COPY_OFF;
276: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
277: } else if (data->inputtype != WINDOW_COPY_OFF) {
1.21 nicm 278: if (window_copy_key_input(wp, key) != 0)
279: goto input_off;
280: return;
281: }
1.1 nicm 282:
1.43 nicm 283: cmd = mode_key_lookup(&data->mdata, key);
284: switch (cmd) {
1.11 nicm 285: case MODEKEYCOPY_CANCEL:
1.51 nicm 286: window_pane_reset_mode(wp);
287: return;
1.10 nicm 288: case MODEKEYCOPY_LEFT:
1.50 nicm 289: for (; np != 0; np--)
290: window_copy_cursor_left(wp);
291: break;
1.10 nicm 292: case MODEKEYCOPY_RIGHT:
1.50 nicm 293: for (; np != 0; np--)
294: window_copy_cursor_right(wp);
295: break;
1.10 nicm 296: case MODEKEYCOPY_UP:
1.50 nicm 297: for (; np != 0; np--)
298: window_copy_cursor_up(wp, 0);
299: break;
1.10 nicm 300: case MODEKEYCOPY_DOWN:
1.50 nicm 301: for (; np != 0; np--)
302: window_copy_cursor_down(wp, 0);
303: break;
1.28 nicm 304: case MODEKEYCOPY_SCROLLUP:
1.50 nicm 305: for (; np != 0; np--)
306: window_copy_cursor_up(wp, 1);
1.28 nicm 307: break;
308: case MODEKEYCOPY_SCROLLDOWN:
1.50 nicm 309: for (; np != 0; np--)
310: window_copy_cursor_down(wp, 1);
1.28 nicm 311: break;
1.10 nicm 312: case MODEKEYCOPY_PREVIOUSPAGE:
1.50 nicm 313: for (; np != 0; np--)
314: window_copy_pageup(wp);
1.1 nicm 315: break;
1.10 nicm 316: case MODEKEYCOPY_NEXTPAGE:
1.19 nicm 317: n = 1;
318: if (screen_size_y(s) > 2)
319: n = screen_size_y(s) - 2;
1.50 nicm 320: for (; np != 0; np--) {
321: if (data->oy < n)
322: data->oy = 0;
323: else
324: data->oy -= n;
325: }
1.20 nicm 326: window_copy_update_selection(wp);
327: window_copy_redraw_screen(wp);
328: break;
329: case MODEKEYCOPY_HALFPAGEUP:
330: n = screen_size_y(s) / 2;
1.50 nicm 331: for (; np != 0; np--) {
332: if (data->oy + n > screen_hsize(&wp->base))
333: data->oy = screen_hsize(&wp->base);
334: else
335: data->oy += n;
336: }
1.20 nicm 337: window_copy_update_selection(wp);
338: window_copy_redraw_screen(wp);
339: break;
340: case MODEKEYCOPY_HALFPAGEDOWN:
341: n = screen_size_y(s) / 2;
1.50 nicm 342: for (; np != 0; np--) {
343: if (data->oy < n)
344: data->oy = 0;
345: else
346: data->oy -= n;
347: }
1.30 nicm 348: window_copy_update_selection(wp);
349: window_copy_redraw_screen(wp);
350: break;
351: case MODEKEYCOPY_TOPLINE:
352: data->cx = 0;
353: data->cy = 0;
354: window_copy_update_selection(wp);
355: window_copy_redraw_screen(wp);
356: break;
357: case MODEKEYCOPY_MIDDLELINE:
358: data->cx = 0;
359: data->cy = (screen_size_y(s) - 1) / 2;
360: window_copy_update_selection(wp);
361: window_copy_redraw_screen(wp);
362: break;
363: case MODEKEYCOPY_BOTTOMLINE:
364: data->cx = 0;
365: data->cy = screen_size_y(s) - 1;
1.37 nicm 366: window_copy_update_selection(wp);
367: window_copy_redraw_screen(wp);
368: break;
369: case MODEKEYCOPY_HISTORYTOP:
370: data->cx = 0;
371: data->cy = 0;
372: data->oy = screen_hsize(&wp->base);
373: window_copy_update_selection(wp);
374: window_copy_redraw_screen(wp);
375: break;
376: case MODEKEYCOPY_HISTORYBOTTOM:
377: data->cx = 0;
378: data->cy = screen_size_y(s) - 1;
379: data->oy = 0;
1.1 nicm 380: window_copy_update_selection(wp);
381: window_copy_redraw_screen(wp);
382: break;
1.10 nicm 383: case MODEKEYCOPY_STARTSELECTION:
1.35 nicm 384: window_copy_start_selection(wp);
1.7 nicm 385: window_copy_redraw_screen(wp);
1.1 nicm 386: break;
1.10 nicm 387: case MODEKEYCOPY_CLEARSELECTION:
1.47 nicm 388: window_copy_clear_selection(wp);
1.1 nicm 389: window_copy_redraw_screen(wp);
390: break;
1.10 nicm 391: case MODEKEYCOPY_COPYSELECTION:
1.1 nicm 392: if (c != NULL && c->session != NULL) {
393: window_copy_copy_selection(wp, c);
394: window_pane_reset_mode(wp);
1.51 nicm 395: return;
1.1 nicm 396: }
397: break;
1.10 nicm 398: case MODEKEYCOPY_STARTOFLINE:
1.1 nicm 399: window_copy_cursor_start_of_line(wp);
400: break;
1.10 nicm 401: case MODEKEYCOPY_BACKTOINDENTATION:
1.6 nicm 402: window_copy_cursor_back_to_indentation(wp);
403: break;
1.10 nicm 404: case MODEKEYCOPY_ENDOFLINE:
1.1 nicm 405: window_copy_cursor_end_of_line(wp);
406: break;
1.41 nicm 407: case MODEKEYCOPY_NEXTSPACE:
1.50 nicm 408: for (; np != 0; np--)
409: window_copy_cursor_next_word(wp, " ");
1.41 nicm 410: break;
411: case MODEKEYCOPY_NEXTSPACEEND:
1.50 nicm 412: for (; np != 0; np--)
413: window_copy_cursor_next_word_end(wp, " ");
1.41 nicm 414: break;
1.10 nicm 415: case MODEKEYCOPY_NEXTWORD:
1.48 nicm 416: word_separators =
417: options_get_string(&wp->window->options, "word-separators");
1.50 nicm 418: for (; np != 0; np--)
419: window_copy_cursor_next_word(wp, word_separators);
1.1 nicm 420: break;
1.40 nicm 421: case MODEKEYCOPY_NEXTWORDEND:
1.48 nicm 422: word_separators =
423: options_get_string(&wp->window->options, "word-separators");
1.50 nicm 424: for (; np != 0; np--)
425: window_copy_cursor_next_word_end(wp, word_separators);
1.41 nicm 426: break;
427: case MODEKEYCOPY_PREVIOUSSPACE:
1.50 nicm 428: for (; np != 0; np--)
429: window_copy_cursor_previous_word(wp, " ");
1.40 nicm 430: break;
1.10 nicm 431: case MODEKEYCOPY_PREVIOUSWORD:
1.48 nicm 432: word_separators =
433: options_get_string(&wp->window->options, "word-separators");
1.50 nicm 434: for (; np != 0; np--)
435: window_copy_cursor_previous_word(wp, word_separators);
1.1 nicm 436: break;
1.52 nicm 437: case MODEKEYCOPY_JUMP:
438: data->inputtype = WINDOW_COPY_JUMPFORWARD;
439: data->inputprompt = "Jump Forward";
440: *data->inputstr = '\0';
441: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
442: return; /* skip numprefix reset */
443: case MODEKEYCOPY_JUMPAGAIN:
444: if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
445: for (; np != 0; np--)
446: window_copy_cursor_jump(wp);
447: } else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
448: for (; np != 0; np--)
449: window_copy_cursor_jump_back(wp);
450: }
451: break;
452: case MODEKEYCOPY_JUMPREVERSE:
453: if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
454: for (; np != 0; np--)
455: window_copy_cursor_jump_back(wp);
456: } else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
457: for (; np != 0; np--)
458: window_copy_cursor_jump(wp);
459: }
460: break;
461: case MODEKEYCOPY_JUMPBACK:
462: data->inputtype = WINDOW_COPY_JUMPBACK;
463: data->inputprompt = "Jump Back";
464: *data->inputstr = '\0';
465: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
466: return; /* skip numprefix reset */
1.21 nicm 467: case MODEKEYCOPY_SEARCHUP:
468: data->inputtype = WINDOW_COPY_SEARCHUP;
469: data->inputprompt = "Search Up";
470: goto input_on;
471: case MODEKEYCOPY_SEARCHDOWN:
472: data->inputtype = WINDOW_COPY_SEARCHDOWN;
473: data->inputprompt = "Search Down";
474: goto input_on;
475: case MODEKEYCOPY_SEARCHAGAIN:
1.43 nicm 476: case MODEKEYCOPY_SEARCHREVERSE:
1.21 nicm 477: switch (data->searchtype) {
478: case WINDOW_COPY_OFF:
479: case WINDOW_COPY_GOTOLINE:
1.52 nicm 480: case WINDOW_COPY_JUMPFORWARD:
481: case WINDOW_COPY_JUMPBACK:
1.50 nicm 482: case WINDOW_COPY_NUMERICPREFIX:
1.21 nicm 483: break;
484: case WINDOW_COPY_SEARCHUP:
1.50 nicm 485: if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
486: for (; np != 0; np--) {
487: window_copy_search_up(
488: wp, data->searchstr);
489: }
490: } else {
491: for (; np != 0; np--) {
492: window_copy_search_down(
493: wp, data->searchstr);
494: }
495: }
1.21 nicm 496: break;
497: case WINDOW_COPY_SEARCHDOWN:
1.50 nicm 498: if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
499: for (; np != 0; np--) {
500: window_copy_search_down(
501: wp, data->searchstr);
502: }
503: } else {
504: for (; np != 0; np--) {
505: window_copy_search_up(
506: wp, data->searchstr);
507: }
508: }
1.21 nicm 509: break;
510: }
511: break;
512: case MODEKEYCOPY_GOTOLINE:
513: data->inputtype = WINDOW_COPY_GOTOLINE;
514: data->inputprompt = "Goto Line";
515: *data->inputstr = '\0';
516: goto input_on;
1.50 nicm 517: case MODEKEYCOPY_STARTNUMBERPREFIX:
518: key &= 0xff;
519: if (key >= '0' && key <= '9') {
520: data->inputtype = WINDOW_COPY_NUMERICPREFIX;
521: data->numprefix = 0;
522: window_copy_key_numeric_prefix(wp, key);
523: return;
524: }
525: break;
1.42 nicm 526: case MODEKEYCOPY_RECTANGLETOGGLE:
527: window_copy_rectangle_toggle(wp);
1.50 nicm 528: break;
1.21 nicm 529: default:
530: break;
531: }
532:
1.50 nicm 533: data->numprefix = 0;
1.21 nicm 534: return;
535:
536: input_on:
537: keys = options_get_number(&wp->window->options, "mode-keys");
538: if (keys == MODEKEY_EMACS)
539: mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
540: else
541: mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
542:
543: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
544: return;
545:
546: input_off:
547: keys = options_get_number(&wp->window->options, "mode-keys");
548: if (keys == MODEKEY_EMACS)
549: mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
550: else
551: mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
552:
553: data->inputtype = WINDOW_COPY_OFF;
554: data->inputprompt = NULL;
555:
556: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
557: }
558:
559: int
560: window_copy_key_input(struct window_pane *wp, int key)
561: {
562: struct window_copy_mode_data *data = wp->modedata;
563: struct screen *s = &data->screen;
564: size_t inputlen;
1.50 nicm 565: u_int np;
1.21 nicm 566:
567: switch (mode_key_lookup(&data->mdata, key)) {
568: case MODEKEYEDIT_CANCEL:
1.50 nicm 569: data->numprefix = 0;
1.21 nicm 570: return (-1);
571: case MODEKEYEDIT_BACKSPACE:
572: inputlen = strlen(data->inputstr);
573: if (inputlen > 0)
574: data->inputstr[inputlen - 1] = '\0';
1.22 nicm 575: break;
576: case MODEKEYEDIT_DELETELINE:
577: *data->inputstr = '\0';
1.21 nicm 578: break;
579: case MODEKEYEDIT_ENTER:
1.50 nicm 580: np = data->numprefix;
581: if (np == 0)
582: np = 1;
583:
1.21 nicm 584: switch (data->inputtype) {
585: case WINDOW_COPY_OFF:
1.52 nicm 586: case WINDOW_COPY_JUMPFORWARD:
587: case WINDOW_COPY_JUMPBACK:
1.50 nicm 588: case WINDOW_COPY_NUMERICPREFIX:
1.21 nicm 589: break;
590: case WINDOW_COPY_SEARCHUP:
1.50 nicm 591: for (; np != 0; np--)
592: window_copy_search_up(wp, data->inputstr);
1.21 nicm 593: data->searchtype = data->inputtype;
594: data->searchstr = xstrdup(data->inputstr);
595: break;
596: case WINDOW_COPY_SEARCHDOWN:
1.50 nicm 597: for (; np != 0; np--)
598: window_copy_search_down(wp, data->inputstr);
1.21 nicm 599: data->searchtype = data->inputtype;
600: data->searchstr = xstrdup(data->inputstr);
601: break;
602: case WINDOW_COPY_GOTOLINE:
603: window_copy_goto_line(wp, data->inputstr);
604: *data->inputstr = '\0';
605: break;
606: }
1.50 nicm 607: data->numprefix = 0;
1.21 nicm 608: return (1);
609: case MODEKEY_OTHER:
610: if (key < 32 || key > 126)
611: break;
612: inputlen = strlen(data->inputstr) + 2;
1.35 nicm 613:
1.21 nicm 614: data->inputstr = xrealloc(data->inputstr, 1, inputlen);
615: data->inputstr[inputlen - 2] = key;
616: data->inputstr[inputlen - 1] = '\0';
617: break;
1.1 nicm 618: default:
619: break;
620: }
1.21 nicm 621:
622: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
623: return (0);
1.1 nicm 624: }
625:
1.50 nicm 626: int
627: window_copy_key_numeric_prefix(struct window_pane *wp, int key)
628: {
629: struct window_copy_mode_data *data = wp->modedata;
630: struct screen *s = &data->screen;
631:
632: key &= 0xff;
633: if (key < '0' || key > '9')
634: return 1;
635:
636: if (data->numprefix >= 100) /* no more than three digits */
637: return 0;
638: data->numprefix = data->numprefix * 10 + key - '0';
639:
640: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
641: return 0;
642: }
643:
1.34 nicm 644: /* ARGSUSED */
1.1 nicm 645: void
1.29 nicm 646: window_copy_mouse(
647: struct window_pane *wp, unused struct client *c, struct mouse_event *m)
1.1 nicm 648: {
649: struct window_copy_mode_data *data = wp->modedata;
650: struct screen *s = &data->screen;
651:
1.29 nicm 652: if ((m->b & 3) == 3)
1.1 nicm 653: return;
1.29 nicm 654: if (m->x >= screen_size_x(s))
1.1 nicm 655: return;
1.29 nicm 656: if (m->y >= screen_size_y(s))
1.1 nicm 657: return;
658:
1.29 nicm 659: window_copy_update_cursor(wp, m->x, m->y);
1.1 nicm 660: if (window_copy_update_selection(wp))
1.35 nicm 661: window_copy_redraw_screen(wp);
1.1 nicm 662: }
663:
664: void
1.21 nicm 665: window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
666: {
667: struct window_copy_mode_data *data = wp->modedata;
668: struct screen *s = &wp->base;
669: struct grid *gd = s->grid;
670: u_int offset, gap;
671:
672: data->cx = px;
673:
674: gap = gd->sy / 4;
675: if (py < gd->sy) {
676: offset = 0;
677: data->cy = py;
678: } else if (py > gd->hsize + gd->sy - gap) {
679: offset = gd->hsize;
680: data->cy = py - gd->hsize;
681: } else {
682: offset = py + gap - gd->sy;
683: data->cy = py - offset;
684: }
1.35 nicm 685: data->oy = gd->hsize - offset;
1.21 nicm 686:
1.38 nicm 687: window_copy_update_selection(wp);
1.21 nicm 688: window_copy_redraw_screen(wp);
689: }
690:
691: int
692: window_copy_search_compare(
693: struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
694: {
695: const struct grid_cell *gc, *sgc;
696: const struct grid_utf8 *gu, *sgu;
697:
698: gc = grid_peek_cell(gd, px, py);
699: sgc = grid_peek_cell(sgd, spx, 0);
1.35 nicm 700:
1.21 nicm 701: if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8))
702: return (0);
703:
704: if (gc->flags & GRID_FLAG_UTF8) {
705: gu = grid_peek_utf8(gd, px, py);
706: sgu = grid_peek_utf8(sgd, spx, 0);
1.32 nicm 707: if (grid_utf8_compare(gu, sgu))
1.21 nicm 708: return (1);
709: } else {
710: if (gc->data == sgc->data)
711: return (1);
712: }
713: return (0);
714: }
715:
716: int
717: window_copy_search_lr(struct grid *gd,
718: struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
719: {
720: u_int ax, bx, px;
721:
722: for (ax = first; ax < last; ax++) {
723: if (ax + sgd->sx >= gd->sx)
724: break;
725: for (bx = 0; bx < sgd->sx; bx++) {
726: px = ax + bx;
727: if (!window_copy_search_compare(gd, px, py, sgd, bx))
728: break;
729: }
730: if (bx == sgd->sx) {
731: *ppx = ax;
732: return (1);
733: }
734: }
735: return (0);
736: }
737:
738: int
739: window_copy_search_rl(struct grid *gd,
740: struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
741: {
742: u_int ax, bx, px;
743:
744: for (ax = last + 1; ax > first; ax--) {
1.24 nicm 745: if (gd->sx - (ax - 1) < sgd->sx)
746: continue;
1.21 nicm 747: for (bx = 0; bx < sgd->sx; bx++) {
748: px = ax - 1 + bx;
749: if (!window_copy_search_compare(gd, px, py, sgd, bx))
750: break;
751: }
752: if (bx == sgd->sx) {
753: *ppx = ax - 1;
754: return (1);
755: }
756: }
757: return (0);
758: }
759:
760: void
761: window_copy_search_up(struct window_pane *wp, const char *searchstr)
762: {
763: struct window_copy_mode_data *data = wp->modedata;
764: struct screen *s = &wp->base, ss;
765: struct screen_write_ctx ctx;
766: struct grid *gd = s->grid, *sgd;
767: struct grid_cell gc;
768: size_t searchlen;
769: u_int i, last, fx, fy, px;
770: int utf8flag, n, wrapped;
771:
772: if (*searchstr == '\0')
773: return;
774: utf8flag = options_get_number(&wp->window->options, "utf8");
775: searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
776:
777: screen_init(&ss, searchlen, 1, 0);
778: screen_write_start(&ctx, NULL, &ss);
779: memcpy(&gc, &grid_default_cell, sizeof gc);
780: screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
781: screen_write_stop(&ctx);
782:
783: fx = data->cx;
784: fy = gd->hsize - data->oy + data->cy;
785:
786: if (fx == 0) {
787: if (fy == 0)
788: return;
789: fx = gd->sx - 1;
790: fy--;
791: } else
792: fx--;
793: n = wrapped = 0;
794:
795: retry:
796: sgd = ss.grid;
797: for (i = fy + 1; i > 0; i--) {
798: last = screen_size_x(s);
799: if (i == fy + 1)
800: last = fx;
801: n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
802: if (n) {
803: window_copy_scroll_to(wp, px, i - 1);
804: break;
805: }
806: }
807: if (!n && !wrapped) {
808: fx = gd->sx - 1;
809: fy = gd->hsize + gd->sy - 1;
810: wrapped = 1;
811: goto retry;
812: }
813:
814: screen_free(&ss);
815: }
816:
817: void
818: window_copy_search_down(struct window_pane *wp, const char *searchstr)
819: {
820: struct window_copy_mode_data *data = wp->modedata;
821: struct screen *s = &wp->base, ss;
822: struct screen_write_ctx ctx;
823: struct grid *gd = s->grid, *sgd;
824: struct grid_cell gc;
825: size_t searchlen;
826: u_int i, first, fx, fy, px;
827: int utf8flag, n, wrapped;
828:
829: if (*searchstr == '\0')
830: return;
831: utf8flag = options_get_number(&wp->window->options, "utf8");
832: searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
833:
834: screen_init(&ss, searchlen, 1, 0);
835: screen_write_start(&ctx, NULL, &ss);
836: memcpy(&gc, &grid_default_cell, sizeof gc);
837: screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
838: screen_write_stop(&ctx);
839:
840: fx = data->cx;
841: fy = gd->hsize - data->oy + data->cy;
842:
843: if (fx == gd->sx - 1) {
844: if (fy == gd->hsize + gd->sy)
845: return;
846: fx = 0;
847: fy++;
848: } else
849: fx++;
850: n = wrapped = 0;
851:
852: retry:
853: sgd = ss.grid;
854: for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
855: first = 0;
856: if (i == fy + 1)
857: first = fx;
858: n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
859: if (n) {
860: window_copy_scroll_to(wp, px, i - 1);
861: break;
862: }
863: }
864: if (!n && !wrapped) {
865: fx = 0;
866: fy = 0;
867: wrapped = 1;
868: goto retry;
869: }
870:
871: screen_free(&ss);
872: }
873:
874: void
875: window_copy_goto_line(struct window_pane *wp, const char *linestr)
876: {
877: struct window_copy_mode_data *data = wp->modedata;
878: const char *errstr;
879: u_int lineno;
880:
881: lineno = strtonum(linestr, 0, screen_hsize(&wp->base), &errstr);
882: if (errstr != NULL)
883: return;
1.35 nicm 884:
1.21 nicm 885: data->oy = lineno;
1.38 nicm 886: window_copy_update_selection(wp);
1.21 nicm 887: window_copy_redraw_screen(wp);
888: }
889:
890: void
1.18 nicm 891: window_copy_write_line(
892: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
1.1 nicm 893: {
894: struct window_copy_mode_data *data = wp->modedata;
895: struct screen *s = &data->screen;
1.27 nicm 896: struct options *oo = &wp->window->options;
1.1 nicm 897: struct grid_cell gc;
898: char hdr[32];
1.21 nicm 899: size_t last, xoff = 0, size = 0;
900:
901: memcpy(&gc, &grid_default_cell, sizeof gc);
1.27 nicm 902: colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
903: colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
904: gc.attr |= options_get_number(oo, "mode-attr");
1.1 nicm 905:
1.21 nicm 906: last = screen_size_y(s) - 1;
1.1 nicm 907: if (py == 0) {
908: size = xsnprintf(hdr, sizeof hdr,
1.18 nicm 909: "[%u/%u]", data->oy, screen_hsize(&wp->base));
1.1 nicm 910: screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
911: screen_write_puts(ctx, &gc, "%s", hdr);
1.21 nicm 912: } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
1.50 nicm 913: if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
914: xoff = size = xsnprintf(hdr, sizeof hdr,
915: "Repeat: %u", data->numprefix);
916: } else {
917: xoff = size = xsnprintf(hdr, sizeof hdr,
918: "%s: %s", data->inputprompt, data->inputstr);
919: }
1.21 nicm 920: screen_write_cursormove(ctx, 0, last);
921: screen_write_puts(ctx, &gc, "%s", hdr);
1.1 nicm 922: } else
923: size = 0;
924:
1.21 nicm 925: screen_write_cursormove(ctx, xoff, py);
926: screen_write_copy(ctx, &wp->base, xoff, (screen_hsize(&wp->base) -
1.1 nicm 927: data->oy) + py, screen_size_x(s) - size, 1);
1.18 nicm 928:
929: if (py == data->cy && data->cx == screen_size_x(s)) {
930: memcpy(&gc, &grid_default_cell, sizeof gc);
931: screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
932: screen_write_putc(ctx, &gc, '$');
933: }
1.1 nicm 934: }
935:
936: void
937: window_copy_write_lines(
938: struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
939: {
940: u_int yy;
941:
942: for (yy = py; yy < py + ny; yy++)
943: window_copy_write_line(wp, ctx, py);
944: }
945:
946: void
947: window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
948: {
949: struct window_copy_mode_data *data = wp->modedata;
950: struct screen_write_ctx ctx;
951: u_int i;
952:
953: screen_write_start(&ctx, wp, NULL);
954: for (i = py; i < py + ny; i++)
955: window_copy_write_line(wp, &ctx, i);
956: screen_write_cursormove(&ctx, data->cx, data->cy);
957: screen_write_stop(&ctx);
958: }
959:
960: void
961: window_copy_redraw_screen(struct window_pane *wp)
962: {
963: struct window_copy_mode_data *data = wp->modedata;
964:
965: window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
966: }
967:
968: void
1.18 nicm 969: window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1.1 nicm 970: {
971: struct window_copy_mode_data *data = wp->modedata;
1.18 nicm 972: struct screen *s = &data->screen;
1.1 nicm 973: struct screen_write_ctx ctx;
1.18 nicm 974: u_int old_cx, old_cy;
1.1 nicm 975:
1.18 nicm 976: old_cx = data->cx; old_cy = data->cy;
977: data->cx = cx; data->cy = cy;
978: if (old_cx == screen_size_x(s))
979: window_copy_redraw_lines(wp, old_cy, 1);
980: if (data->cx == screen_size_x(s))
981: window_copy_redraw_lines(wp, data->cy, 1);
982: else {
983: screen_write_start(&ctx, wp, NULL);
984: screen_write_cursormove(&ctx, data->cx, data->cy);
985: screen_write_stop(&ctx);
986: }
1.1 nicm 987: }
988:
989: void
990: window_copy_start_selection(struct window_pane *wp)
991: {
992: struct window_copy_mode_data *data = wp->modedata;
993: struct screen *s = &data->screen;
994:
1.18 nicm 995: data->selx = data->cx;
1.1 nicm 996: data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
997:
998: s->sel.flag = 1;
999: window_copy_update_selection(wp);
1000: }
1001:
1002: int
1003: window_copy_update_selection(struct window_pane *wp)
1004: {
1005: struct window_copy_mode_data *data = wp->modedata;
1006: struct screen *s = &data->screen;
1.27 nicm 1007: struct options *oo = &wp->window->options;
1.1 nicm 1008: struct grid_cell gc;
1.42 nicm 1009: u_int sx, sy, ty, cy;
1.1 nicm 1010:
1011: if (!s->sel.flag)
1012: return (0);
1013:
1014: /* Set colours. */
1015: memcpy(&gc, &grid_default_cell, sizeof gc);
1.27 nicm 1016: colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
1017: colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
1018: gc.attr |= options_get_number(oo, "mode-attr");
1.1 nicm 1019:
1.18 nicm 1020: /* Find top of screen. */
1.1 nicm 1021: ty = screen_hsize(&wp->base) - data->oy;
1022:
1023: /* Adjust the selection. */
1024: sx = data->selx;
1025: sy = data->sely;
1.18 nicm 1026: if (sy < ty) { /* above screen */
1.42 nicm 1027: if (!data->rectflag)
1028: sx = 0;
1.1 nicm 1029: sy = 0;
1.18 nicm 1030: } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */
1.42 nicm 1031: if (!data->rectflag)
1032: sx = screen_size_x(s) - 1;
1.1 nicm 1033: sy = screen_size_y(s) - 1;
1.18 nicm 1034: } else
1.1 nicm 1035: sy -= ty;
1036: sy = screen_hsize(s) + sy;
1037:
1.42 nicm 1038: screen_set_selection(s,
1039: sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
1040:
1041: if (data->rectflag) {
1042: /*
1043: * Can't rely on the caller to redraw the right lines for
1044: * rectangle selection - find the highest line and the number
1045: * of lines, and redraw just past that in both directions
1046: */
1047: cy = data->cy;
1048: if (sy < cy)
1049: window_copy_redraw_lines(wp, sy, cy - sy + 1);
1050: else
1051: window_copy_redraw_lines(wp, cy, sy - cy + 1);
1052: }
1053:
1.1 nicm 1054: return (1);
1055: }
1056:
1057: void
1058: window_copy_copy_selection(struct window_pane *wp, struct client *c)
1059: {
1060: struct window_copy_mode_data *data = wp->modedata;
1061: struct screen *s = &data->screen;
1062: char *buf;
1.42 nicm 1063: size_t off;
1064: u_int i, xx, yy, sx, sy, ex, ey, limit;
1065: u_int firstsx, lastex, restex, restsx;
1.1 nicm 1066:
1067: if (!s->sel.flag)
1068: return;
1069:
1070: buf = xmalloc(1);
1071: off = 0;
1072:
1073: *buf = '\0';
1074:
1075: /*
1076: * The selection extends from selx,sely to (adjusted) cx,cy on
1077: * the base screen.
1078: */
1079:
1080: /* Find start and end. */
1.18 nicm 1081: xx = data->cx;
1.1 nicm 1082: yy = screen_hsize(&wp->base) + data->cy - data->oy;
1.2 nicm 1083: if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1.1 nicm 1084: sx = xx; sy = yy;
1085: ex = data->selx; ey = data->sely;
1086: } else {
1087: sx = data->selx; sy = data->sely;
1088: ex = xx; ey = yy;
1089: }
1090:
1091: /* Trim ex to end of line. */
1092: xx = window_copy_find_length(wp, ey);
1093: if (ex > xx)
1094: ex = xx;
1095:
1.42 nicm 1096: /*
1097: * Deal with rectangle-copy if necessary; four situations: start of
1098: * first line (firstsx), end of last line (lastex), start (restsx) and
1099: * end (restex) of all other lines.
1100: */
1101: xx = screen_size_x(s);
1102: if (data->rectflag) {
1103: /*
1104: * Need to ignore the column with the cursor in it, which for
1105: * rectangular copy means knowing which side the cursor is on.
1106: */
1107: if (data->selx < data->cx) {
1108: /* Selection start is on the left. */
1109: lastex = data->cx;
1110: restex = data->cx;
1111: firstsx = data->selx;
1112: restsx = data->selx;
1113: } else {
1114: /* Cursor is on the left. */
1115: lastex = data->selx + 1;
1116: restex = data->selx + 1;
1117: firstsx = data->cx + 1;
1118: restsx = data->cx + 1;
1119: }
1120: } else {
1121: /*
1122: * Like emacs, keep the top-left-most character, and drop the
1123: * bottom-right-most, regardless of copy direction.
1124: */
1125: lastex = ex;
1126: restex = xx;
1127: firstsx = sx;
1128: restsx = 0;
1129: }
1130:
1.1 nicm 1131: /* Copy the lines. */
1132: if (sy == ey)
1.42 nicm 1133: window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex);
1.1 nicm 1134: else {
1.42 nicm 1135: window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex);
1.1 nicm 1136: if (ey - sy > 1) {
1.42 nicm 1137: for (i = sy + 1; i < ey; i++) {
1138: window_copy_copy_line(
1139: wp, &buf, &off, i, restsx, restex);
1140: }
1.1 nicm 1141: }
1.42 nicm 1142: window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
1.1 nicm 1143: }
1144:
1.26 nicm 1145: /* Don't bother if no data. */
1146: if (off == 0) {
1147: xfree(buf);
1148: return;
1149: }
1150: off--; /* remove final \n */
1.1 nicm 1151:
1152: /* Add the buffer to the stack. */
1153: limit = options_get_number(&c->session->options, "buffer-limit");
1.26 nicm 1154: paste_add(&c->session->buffers, buf, off, limit);
1.1 nicm 1155: }
1156:
1157: void
1158: window_copy_copy_line(struct window_pane *wp,
1159: char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
1160: {
1.16 nicm 1161: struct grid *gd = wp->base.grid;
1.35 nicm 1162: const struct grid_cell *gc;
1163: const struct grid_utf8 *gu;
1.16 nicm 1164: struct grid_line *gl;
1.32 nicm 1165: u_int i, xx, wrapped = 0;
1166: size_t size;
1.1 nicm 1167:
1168: if (sx > ex)
1169: return;
1170:
1.16 nicm 1171: /*
1172: * Work out if the line was wrapped at the screen edge and all of it is
1173: * on screen.
1174: */
1175: gl = &gd->linedata[sy];
1.35 nicm 1176: if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
1.16 nicm 1177: wrapped = 1;
1178:
1179: /* If the line was wrapped, don't strip spaces (use the full length). */
1180: if (wrapped)
1181: xx = gl->cellsize;
1182: else
1183: xx = window_copy_find_length(wp, sy);
1.1 nicm 1184: if (ex > xx)
1185: ex = xx;
1186: if (sx > xx)
1187: sx = xx;
1188:
1189: if (sx < ex) {
1190: for (i = sx; i < ex; i++) {
1.16 nicm 1191: gc = grid_peek_cell(gd, i, sy);
1.1 nicm 1192: if (gc->flags & GRID_FLAG_PADDING)
1193: continue;
1194: if (!(gc->flags & GRID_FLAG_UTF8)) {
1195: *buf = xrealloc(*buf, 1, (*off) + 1);
1196: (*buf)[(*off)++] = gc->data;
1197: } else {
1.16 nicm 1198: gu = grid_peek_utf8(gd, i, sy);
1.32 nicm 1199: size = grid_utf8_size(gu);
1200: *buf = xrealloc(*buf, 1, (*off) + size);
1201: *off += grid_utf8_copy(gu, *buf + *off, size);
1.1 nicm 1202: }
1203: }
1204: }
1205:
1.16 nicm 1206: /* Only add a newline if the line wasn't wrapped. */
1.44 nicm 1207: if (!wrapped || ex != xx) {
1.16 nicm 1208: *buf = xrealloc(*buf, 1, (*off) + 1);
1209: (*buf)[(*off)++] = '\n';
1210: }
1.1 nicm 1211: }
1212:
1.47 nicm 1213: void
1214: window_copy_clear_selection(struct window_pane *wp)
1215: {
1216: struct window_copy_mode_data *data = wp->modedata;
1217: u_int px, py;
1218:
1219: screen_clear_selection(&data->screen);
1220:
1221: py = screen_hsize(&wp->base) + data->cy - data->oy;
1222: px = window_copy_find_length(wp, py);
1223: if (data->cx > px)
1224: window_copy_update_cursor(wp, px, data->cy);
1225: }
1226:
1.1 nicm 1227: int
1.41 nicm 1228: window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
1.1 nicm 1229: {
1230: const struct grid_cell *gc;
1231:
1232: gc = grid_peek_cell(wp->base.grid, px, py);
1233: if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
1234: return (0);
1235: if (gc->data == 0x00 || gc->data == 0x7f)
1236: return (0);
1.41 nicm 1237: return (strchr(set, gc->data) != NULL);
1.1 nicm 1238: }
1239:
1240: u_int
1241: window_copy_find_length(struct window_pane *wp, u_int py)
1242: {
1243: const struct grid_cell *gc;
1244: u_int px;
1245:
1.4 nicm 1246: /*
1247: * If the pane has been resized, its grid can contain old overlong
1248: * lines. grid_peek_cell does not allow accessing cells beyond the
1249: * width of the grid, and screen_write_copy treats them as spaces, so
1250: * ignore them here too.
1251: */
1.15 nicm 1252: px = wp->base.grid->linedata[py].cellsize;
1.4 nicm 1253: if (px > screen_size_x(&wp->base))
1254: px = screen_size_x(&wp->base);
1.1 nicm 1255: while (px > 0) {
1256: gc = grid_peek_cell(wp->base.grid, px - 1, py);
1257: if (gc->flags & GRID_FLAG_UTF8)
1258: break;
1259: if (gc->data != ' ')
1260: break;
1261: px--;
1262: }
1263: return (px);
1264: }
1265:
1266: void
1.5 nicm 1267: window_copy_cursor_start_of_line(struct window_pane *wp)
1268: {
1269: struct window_copy_mode_data *data = wp->modedata;
1270:
1.18 nicm 1271: window_copy_update_cursor(wp, 0, data->cy);
1.5 nicm 1272: if (window_copy_update_selection(wp))
1273: window_copy_redraw_lines(wp, data->cy, 1);
1.6 nicm 1274: }
1275:
1276: void
1277: window_copy_cursor_back_to_indentation(struct window_pane *wp)
1278: {
1279: struct window_copy_mode_data *data = wp->modedata;
1280: u_int px, py, xx;
1281: const struct grid_cell *gc;
1282:
1283: px = 0;
1284: py = screen_hsize(&wp->base) + data->cy - data->oy;
1285: xx = window_copy_find_length(wp, py);
1286:
1287: while (px < xx) {
1288: gc = grid_peek_cell(wp->base.grid, px, py);
1289: if (gc->flags & GRID_FLAG_UTF8)
1290: break;
1291: if (gc->data != ' ')
1292: break;
1293: px++;
1294: }
1295:
1.18 nicm 1296: window_copy_update_cursor(wp, px, data->cy);
1297: if (window_copy_update_selection(wp))
1298: window_copy_redraw_lines(wp, data->cy, 1);
1.5 nicm 1299: }
1300:
1301: void
1302: window_copy_cursor_end_of_line(struct window_pane *wp)
1303: {
1304: struct window_copy_mode_data *data = wp->modedata;
1.49 nicm 1305: struct screen *base_s = &wp->base;
1306: struct grid *gd = base_s->grid;
1.5 nicm 1307: u_int px, py;
1308:
1.49 nicm 1309: py = screen_hsize(base_s) + data->cy - data->oy;
1.5 nicm 1310: px = window_copy_find_length(wp, py);
1311:
1.49 nicm 1312: if (data->cx == px) {
1313: if (data->screen.sel.flag && data->rectflag)
1314: px = screen_size_x(&wp->base);
1315: if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1316: while (py < gd->sy + gd->hsize &&
1317: gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1318: window_copy_cursor_down(wp, 0);
1319: py = screen_hsize(base_s) + data->cy - data->oy;
1320: }
1321: px = window_copy_find_length(wp, py);
1322: }
1323: }
1.18 nicm 1324: window_copy_update_cursor(wp, px, data->cy);
1.49 nicm 1325:
1.18 nicm 1326: if (window_copy_update_selection(wp))
1327: window_copy_redraw_lines(wp, data->cy, 1);
1.5 nicm 1328: }
1329:
1330: void
1.1 nicm 1331: window_copy_cursor_left(struct window_pane *wp)
1332: {
1333: struct window_copy_mode_data *data = wp->modedata;
1334:
1335: if (data->cx == 0) {
1.28 nicm 1336: window_copy_cursor_up(wp, 0);
1.18 nicm 1337: window_copy_cursor_end_of_line(wp);
1.1 nicm 1338: } else {
1.18 nicm 1339: window_copy_update_cursor(wp, data->cx - 1, data->cy);
1.1 nicm 1340: if (window_copy_update_selection(wp))
1341: window_copy_redraw_lines(wp, data->cy, 1);
1342: }
1343: }
1344:
1345: void
1346: window_copy_cursor_right(struct window_pane *wp)
1347: {
1348: struct window_copy_mode_data *data = wp->modedata;
1349: u_int px, py;
1350:
1.47 nicm 1351: if (data->screen.sel.flag && data->rectflag)
1352: px = screen_size_x(&data->screen);
1353: else {
1354: py = screen_hsize(&wp->base) + data->cy - data->oy;
1355: px = window_copy_find_length(wp, py);
1356: }
1.1 nicm 1357:
1358: if (data->cx >= px) {
1359: window_copy_cursor_start_of_line(wp);
1.28 nicm 1360: window_copy_cursor_down(wp, 0);
1.1 nicm 1361: } else {
1.18 nicm 1362: window_copy_update_cursor(wp, data->cx + 1, data->cy);
1.1 nicm 1363: if (window_copy_update_selection(wp))
1364: window_copy_redraw_lines(wp, data->cy, 1);
1365: }
1366: }
1367:
1368: void
1.28 nicm 1369: window_copy_cursor_up(struct window_pane *wp, int scroll_only)
1.1 nicm 1370: {
1371: struct window_copy_mode_data *data = wp->modedata;
1.36 nicm 1372: struct screen *s = &data->screen;
1.1 nicm 1373: u_int ox, oy, px, py;
1374:
1375: oy = screen_hsize(&wp->base) + data->cy - data->oy;
1376: ox = window_copy_find_length(wp, oy);
1.25 nicm 1377: if (ox != 0) {
1378: data->lastcx = data->cx;
1379: data->lastsx = ox;
1380: }
1.1 nicm 1381:
1.25 nicm 1382: data->cx = data->lastcx;
1.28 nicm 1383: if (scroll_only || data->cy == 0) {
1.1 nicm 1384: window_copy_scroll_down(wp, 1);
1.36 nicm 1385: if (scroll_only) {
1386: if (data->cy == screen_size_y(s) - 1)
1387: window_copy_redraw_lines(wp, data->cy, 1);
1388: else
1389: window_copy_redraw_lines(wp, data->cy, 2);
1390: }
1.28 nicm 1391: } else {
1.18 nicm 1392: window_copy_update_cursor(wp, data->cx, data->cy - 1);
1.36 nicm 1393: if (window_copy_update_selection(wp)) {
1394: if (data->cy == screen_size_y(s) - 1)
1395: window_copy_redraw_lines(wp, data->cy, 1);
1396: else
1397: window_copy_redraw_lines(wp, data->cy, 2);
1398: }
1.1 nicm 1399: }
1400:
1.47 nicm 1401: if (!data->screen.sel.flag || !data->rectflag) {
1402: py = screen_hsize(&wp->base) + data->cy - data->oy;
1403: px = window_copy_find_length(wp, py);
1.49 nicm 1404: if ((data->cx >= data->lastsx && data->cx != px) ||
1405: data->cx > px)
1.47 nicm 1406: window_copy_cursor_end_of_line(wp);
1407: }
1.1 nicm 1408: }
1409:
1410: void
1.28 nicm 1411: window_copy_cursor_down(struct window_pane *wp, int scroll_only)
1.1 nicm 1412: {
1413: struct window_copy_mode_data *data = wp->modedata;
1414: struct screen *s = &data->screen;
1415: u_int ox, oy, px, py;
1416:
1417: oy = screen_hsize(&wp->base) + data->cy - data->oy;
1418: ox = window_copy_find_length(wp, oy);
1.25 nicm 1419: if (ox != 0) {
1420: data->lastcx = data->cx;
1421: data->lastsx = ox;
1422: }
1.1 nicm 1423:
1.25 nicm 1424: data->cx = data->lastcx;
1.28 nicm 1425: if (scroll_only || data->cy == screen_size_y(s) - 1) {
1.1 nicm 1426: window_copy_scroll_up(wp, 1);
1.31 nicm 1427: if (scroll_only && data->cy > 0)
1.28 nicm 1428: window_copy_redraw_lines(wp, data->cy - 1, 2);
1429: } else {
1.18 nicm 1430: window_copy_update_cursor(wp, data->cx, data->cy + 1);
1.1 nicm 1431: if (window_copy_update_selection(wp))
1432: window_copy_redraw_lines(wp, data->cy - 1, 2);
1433: }
1434:
1.47 nicm 1435: if (!data->screen.sel.flag || !data->rectflag) {
1436: py = screen_hsize(&wp->base) + data->cy - data->oy;
1437: px = window_copy_find_length(wp, py);
1.49 nicm 1438: if ((data->cx >= data->lastsx && data->cx != px) ||
1439: data->cx > px)
1.47 nicm 1440: window_copy_cursor_end_of_line(wp);
1.52 nicm 1441: }
1442: }
1443:
1444: void
1445: window_copy_cursor_jump(struct window_pane *wp)
1446: {
1447: struct window_copy_mode_data *data = wp->modedata;
1448: struct screen *base_s = &wp->base;
1449: const struct grid_cell *gc;
1450: uint px, py, xx;
1451:
1452: px = data->cx + 1;
1453: py = screen_hsize(base_s) + data->cy - data->oy;
1454: xx = window_copy_find_length(wp, py);
1455:
1456: while (px < xx) {
1457: gc = grid_peek_cell(base_s->grid, px, py);
1458: if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
1459: && gc->data == data->jumpchar) {
1460:
1461: window_copy_update_cursor(wp, px, data->cy);
1462: if (window_copy_update_selection(wp))
1463: window_copy_redraw_lines(wp, data->cy, 1);
1464: return;
1465: }
1466: px++;
1467: }
1468: }
1469:
1470: void
1471: window_copy_cursor_jump_back(struct window_pane *wp)
1472: {
1473: struct window_copy_mode_data *data = wp->modedata;
1474: struct screen *base_s = &wp->base;
1475: const struct grid_cell *gc;
1476: uint px, py;
1477:
1478: px = data->cx;
1479: py = screen_hsize(base_s) + data->cy - data->oy;
1480:
1481: if (px > 0)
1482: px--;
1483:
1484: for (;;) {
1485: gc = grid_peek_cell(base_s->grid, px, py);
1486: if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
1487: && gc->data == data->jumpchar) {
1488:
1489: window_copy_update_cursor(wp, px, data->cy);
1490: if (window_copy_update_selection(wp))
1491: window_copy_redraw_lines(wp, data->cy, 1);
1492: return;
1493: }
1494: if (px == 0)
1495: break;
1496: px--;
1.47 nicm 1497: }
1.1 nicm 1498: }
1499:
1500: void
1.41 nicm 1501: window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
1.40 nicm 1502: {
1503: struct window_copy_mode_data *data = wp->modedata;
1504: struct screen *base_s = &wp->base;
1505: u_int px, py, xx, yy;
1.48 nicm 1506: int expected = 0;
1.40 nicm 1507:
1508: px = data->cx;
1509: py = screen_hsize(base_s) + data->cy - data->oy;
1510: xx = window_copy_find_length(wp, py);
1511: yy = screen_hsize(base_s) + screen_size_y(base_s) - 1;
1512:
1.48 nicm 1513: /*
1514: * First skip past any nonword characters and then any word characters.
1515: *
1516: * expected is initially set to 0 for the former and then 1 for the
1517: * latter.
1518: */
1519: do {
1520: while (px > xx ||
1521: window_copy_in_set(wp, px, py, separators) == expected) {
1522: /* Move down if we're past the end of the line. */
1523: if (px > xx) {
1524: if (py == yy)
1525: return;
1526: window_copy_cursor_down(wp, 0);
1527: px = 0;
1528:
1529: py = screen_hsize(base_s) + data->cy - data->oy;
1530: xx = window_copy_find_length(wp, py);
1531: } else
1532: px++;
1533: }
1534: expected = !expected;
1535: } while (expected == 1);
1.40 nicm 1536:
1537: window_copy_update_cursor(wp, px, data->cy);
1538: if (window_copy_update_selection(wp))
1539: window_copy_redraw_lines(wp, data->cy, 1);
1540: }
1541:
1542: void
1.41 nicm 1543: window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators)
1.1 nicm 1544: {
1545: struct window_copy_mode_data *data = wp->modedata;
1.39 nicm 1546: struct screen *base_s = &wp->base;
1547: u_int px, py, xx, yy;
1.48 nicm 1548: int expected = 1;
1.1 nicm 1549:
1.18 nicm 1550: px = data->cx;
1.39 nicm 1551: py = screen_hsize(base_s) + data->cy - data->oy;
1.1 nicm 1552: xx = window_copy_find_length(wp, py);
1.39 nicm 1553: yy = screen_hsize(base_s) + screen_size_y(base_s) - 1;
1.1 nicm 1554:
1.48 nicm 1555: /*
1556: * First skip past any word characters, then any nonword characters.
1557: *
1558: * expected is initially set to 1 for the former and then 0 for the
1559: * latter.
1560: */
1561: do {
1562: while (px > xx ||
1563: window_copy_in_set(wp, px, py, separators) == expected) {
1564: /* Move down if we're past the end of the line. */
1565: if (px > xx) {
1566: if (py == yy)
1567: return;
1568: window_copy_cursor_down(wp, 0);
1569: px = 0;
1570:
1571: py = screen_hsize(base_s) + data->cy - data->oy;
1572: xx = window_copy_find_length(wp, py);
1573: } else
1574: px++;
1575: }
1576: expected = !expected;
1577: } while (expected == 0);
1.18 nicm 1578:
1579: window_copy_update_cursor(wp, px, data->cy);
1580: if (window_copy_update_selection(wp))
1581: window_copy_redraw_lines(wp, data->cy, 1);
1.1 nicm 1582: }
1583:
1.8 nicm 1584: /* Move to the previous place where a word begins. */
1.1 nicm 1585: void
1.41 nicm 1586: window_copy_cursor_previous_word(struct window_pane *wp, const char *separators)
1.1 nicm 1587: {
1588: struct window_copy_mode_data *data = wp->modedata;
1.8 nicm 1589: u_int px, py;
1.1 nicm 1590:
1.18 nicm 1591: px = data->cx;
1.1 nicm 1592: py = screen_hsize(&wp->base) + data->cy - data->oy;
1593:
1.8 nicm 1594: /* Move back to the previous word character. */
1.1 nicm 1595: for (;;) {
1.8 nicm 1596: if (px > 0) {
1597: px--;
1.41 nicm 1598: if (!window_copy_in_set(wp, px, py, separators))
1.1 nicm 1599: break;
1.8 nicm 1600: } else {
1601: if (data->cy == 0 &&
1602: (screen_hsize(&wp->base) == 0 ||
1603: data->oy >= screen_hsize(&wp->base) - 1))
1604: goto out;
1.28 nicm 1605: window_copy_cursor_up(wp, 0);
1.1 nicm 1606:
1.39 nicm 1607: py = screen_hsize(&wp->base) + data->cy - data->oy;
1.8 nicm 1608: px = window_copy_find_length(wp, py);
1.1 nicm 1609: }
1.8 nicm 1610: }
1.1 nicm 1611:
1.8 nicm 1612: /* Move back to the beginning of this word. */
1.41 nicm 1613: while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
1.1 nicm 1614: px--;
1.8 nicm 1615:
1.1 nicm 1616: out:
1.18 nicm 1617: window_copy_update_cursor(wp, px, data->cy);
1618: if (window_copy_update_selection(wp))
1619: window_copy_redraw_lines(wp, data->cy, 1);
1.1 nicm 1620: }
1621:
1622: void
1623: window_copy_scroll_up(struct window_pane *wp, u_int ny)
1624: {
1625: struct window_copy_mode_data *data = wp->modedata;
1626: struct screen *s = &data->screen;
1627: struct screen_write_ctx ctx;
1628:
1629: if (data->oy < ny)
1630: ny = data->oy;
1631: if (ny == 0)
1632: return;
1633: data->oy -= ny;
1634:
1635: screen_write_start(&ctx, wp, NULL);
1636: screen_write_cursormove(&ctx, 0, 0);
1637: screen_write_deleteline(&ctx, ny);
1638: window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
1639: window_copy_write_line(wp, &ctx, 0);
1.31 nicm 1640: if (screen_size_y(s) > 1)
1641: window_copy_write_line(wp, &ctx, 1);
1642: if (screen_size_y(s) > 3)
1643: window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
1.42 nicm 1644: if (s->sel.flag && screen_size_y(s) > ny) {
1645: window_copy_update_selection(wp);
1.1 nicm 1646: window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
1.42 nicm 1647: }
1.1 nicm 1648: screen_write_cursormove(&ctx, data->cx, data->cy);
1.42 nicm 1649: window_copy_update_selection(wp);
1.1 nicm 1650: screen_write_stop(&ctx);
1651: }
1652:
1653: void
1654: window_copy_scroll_down(struct window_pane *wp, u_int ny)
1655: {
1656: struct window_copy_mode_data *data = wp->modedata;
1657: struct screen *s = &data->screen;
1658: struct screen_write_ctx ctx;
1659:
1660: if (ny > screen_hsize(&wp->base))
1661: return;
1662:
1663: if (data->oy > screen_hsize(&wp->base) - ny)
1664: ny = screen_hsize(&wp->base) - data->oy;
1665: if (ny == 0)
1666: return;
1667: data->oy += ny;
1668:
1669: screen_write_start(&ctx, wp, NULL);
1670: screen_write_cursormove(&ctx, 0, 0);
1671: screen_write_insertline(&ctx, ny);
1672: window_copy_write_lines(wp, &ctx, 0, ny);
1.42 nicm 1673: if (s->sel.flag && screen_size_y(s) > ny) {
1674: window_copy_update_selection(wp);
1.1 nicm 1675: window_copy_write_line(wp, &ctx, ny);
1.42 nicm 1676: } else if (ny == 1) /* nuke position */
1.1 nicm 1677: window_copy_write_line(wp, &ctx, 1);
1678: screen_write_cursormove(&ctx, data->cx, data->cy);
1.42 nicm 1679: window_copy_update_selection(wp);
1.1 nicm 1680: screen_write_stop(&ctx);
1681: }
1.42 nicm 1682:
1683: void
1684: window_copy_rectangle_toggle(struct window_pane *wp)
1685: {
1686: struct window_copy_mode_data *data = wp->modedata;
1.47 nicm 1687: u_int px, py;
1.42 nicm 1688:
1689: data->rectflag = !data->rectflag;
1.47 nicm 1690:
1691: py = screen_hsize(&wp->base) + data->cy - data->oy;
1692: px = window_copy_find_length(wp, py);
1693: if (data->cx > px)
1694: window_copy_update_cursor(wp, px, data->cy);
1.42 nicm 1695:
1696: window_copy_update_selection(wp);
1697: window_copy_redraw_screen(wp);
1698: }