Annotation of embedaddon/tmux/window-copy.c, revision 1.1

1.1     ! misho       1: /* $OpenBSD$ */
        !             2: 
        !             3: /*
        !             4:  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
        !             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: 
        !            21: #include <ctype.h>
        !            22: #include <stdlib.h>
        !            23: #include <string.h>
        !            24: 
        !            25: #include "tmux.h"
        !            26: 
        !            27: static const char *window_copy_key_table(struct window_pane *);
        !            28: static void    window_copy_command(struct window_pane *, struct client *,
        !            29:                    struct session *, struct args *, struct mouse_event *);
        !            30: static struct screen *window_copy_init(struct window_pane *);
        !            31: static void    window_copy_free(struct window_pane *);
        !            32: static int     window_copy_pagedown(struct window_pane *, int);
        !            33: static void    window_copy_next_paragraph(struct window_pane *);
        !            34: static void    window_copy_previous_paragraph(struct window_pane *);
        !            35: static void    window_copy_resize(struct window_pane *, u_int, u_int);
        !            36: 
        !            37: static void    window_copy_redraw_selection(struct window_pane *, u_int);
        !            38: static void    window_copy_redraw_lines(struct window_pane *, u_int, u_int);
        !            39: static void    window_copy_redraw_screen(struct window_pane *);
        !            40: static void    window_copy_write_line(struct window_pane *,
        !            41:                    struct screen_write_ctx *, u_int);
        !            42: static void    window_copy_write_lines(struct window_pane *,
        !            43:                    struct screen_write_ctx *, u_int, u_int);
        !            44: 
        !            45: static void    window_copy_scroll_to(struct window_pane *, u_int, u_int);
        !            46: static int     window_copy_search_compare(struct grid *, u_int, u_int,
        !            47:                    struct grid *, u_int, int);
        !            48: static int     window_copy_search_lr(struct grid *, struct grid *, u_int *,
        !            49:                    u_int, u_int, u_int, int);
        !            50: static int     window_copy_search_rl(struct grid *, struct grid *, u_int *,
        !            51:                    u_int, u_int, u_int, int);
        !            52: static int     window_copy_search_marks(struct window_pane *, struct screen *);
        !            53: static void    window_copy_clear_marks(struct window_pane *);
        !            54: static void    window_copy_move_left(struct screen *, u_int *, u_int *);
        !            55: static void    window_copy_move_right(struct screen *, u_int *, u_int *);
        !            56: static int     window_copy_is_lowercase(const char *);
        !            57: static int     window_copy_search_jump(struct window_pane *, struct grid *,
        !            58:                    struct grid *, u_int, u_int, u_int, int, int, int);
        !            59: static int     window_copy_search(struct window_pane *, int, int);
        !            60: static int     window_copy_search_up(struct window_pane *, int);
        !            61: static int     window_copy_search_down(struct window_pane *, int);
        !            62: static void    window_copy_goto_line(struct window_pane *, const char *);
        !            63: static void    window_copy_update_cursor(struct window_pane *, u_int, u_int);
        !            64: static void    window_copy_start_selection(struct window_pane *);
        !            65: static int     window_copy_adjust_selection(struct window_pane *, u_int *,
        !            66:                    u_int *);
        !            67: static int     window_copy_update_selection(struct window_pane *, int);
        !            68: static void    window_copy_synchronize_cursor(struct window_pane *wp);
        !            69: static void    *window_copy_get_selection(struct window_pane *, size_t *);
        !            70: static void    window_copy_copy_buffer(struct window_pane *, const char *,
        !            71:                    void *, size_t);
        !            72: static void    window_copy_copy_pipe(struct window_pane *, struct session *,
        !            73:                    const char *, const char *);
        !            74: static void    window_copy_copy_selection(struct window_pane *, const char *);
        !            75: static void    window_copy_append_selection(struct window_pane *,
        !            76:                    const char *);
        !            77: static void    window_copy_clear_selection(struct window_pane *);
        !            78: static void    window_copy_copy_line(struct window_pane *, char **, size_t *,
        !            79:                    u_int, u_int, u_int);
        !            80: static int     window_copy_in_set(struct window_pane *, u_int, u_int,
        !            81:                    const char *);
        !            82: static u_int   window_copy_find_length(struct window_pane *, u_int);
        !            83: static void    window_copy_cursor_start_of_line(struct window_pane *);
        !            84: static void    window_copy_cursor_back_to_indentation(struct window_pane *);
        !            85: static void    window_copy_cursor_end_of_line(struct window_pane *);
        !            86: static void    window_copy_other_end(struct window_pane *);
        !            87: static void    window_copy_cursor_left(struct window_pane *);
        !            88: static void    window_copy_cursor_right(struct window_pane *);
        !            89: static void    window_copy_cursor_up(struct window_pane *, int);
        !            90: static void    window_copy_cursor_down(struct window_pane *, int);
        !            91: static void    window_copy_cursor_jump(struct window_pane *);
        !            92: static void    window_copy_cursor_jump_back(struct window_pane *);
        !            93: static void    window_copy_cursor_jump_to(struct window_pane *, int);
        !            94: static void    window_copy_cursor_jump_to_back(struct window_pane *, int);
        !            95: static void    window_copy_cursor_next_word(struct window_pane *,
        !            96:                    const char *);
        !            97: static void    window_copy_cursor_next_word_end(struct window_pane *,
        !            98:                    const char *);
        !            99: static void    window_copy_cursor_previous_word(struct window_pane *,
        !           100:                    const char *);
        !           101: static void    window_copy_scroll_up(struct window_pane *, u_int);
        !           102: static void    window_copy_scroll_down(struct window_pane *, u_int);
        !           103: static void    window_copy_rectangle_toggle(struct window_pane *);
        !           104: static void    window_copy_move_mouse(struct mouse_event *);
        !           105: static void    window_copy_drag_update(struct client *, struct mouse_event *);
        !           106: 
        !           107: const struct window_mode window_copy_mode = {
        !           108:        .init = window_copy_init,
        !           109:        .free = window_copy_free,
        !           110:        .resize = window_copy_resize,
        !           111:        .key_table = window_copy_key_table,
        !           112:        .command = window_copy_command,
        !           113: };
        !           114: 
        !           115: enum {
        !           116:        WINDOW_COPY_OFF,
        !           117:        WINDOW_COPY_SEARCHUP,
        !           118:        WINDOW_COPY_SEARCHDOWN,
        !           119:        WINDOW_COPY_JUMPFORWARD,
        !           120:        WINDOW_COPY_JUMPBACKWARD,
        !           121:        WINDOW_COPY_JUMPTOFORWARD,
        !           122:        WINDOW_COPY_JUMPTOBACKWARD,
        !           123: };
        !           124: 
        !           125: enum {
        !           126:        WINDOW_COPY_REL_POS_ABOVE,
        !           127:        WINDOW_COPY_REL_POS_ON_SCREEN,
        !           128:        WINDOW_COPY_REL_POS_BELOW,
        !           129: };
        !           130: 
        !           131: /*
        !           132:  * Copy mode's visible screen (the "screen" field) is filled from one of two
        !           133:  * sources: the original contents of the pane (used when we actually enter via
        !           134:  * the "copy-mode" command, to copy the contents of the current pane), or else
        !           135:  * a series of lines containing the output from an output-writing tmux command
        !           136:  * (such as any of the "show-*" or "list-*" commands).
        !           137:  *
        !           138:  * In either case, the full content of the copy-mode grid is pointed at by the
        !           139:  * "backing" field, and is copied into "screen" as needed (that is, when
        !           140:  * scrolling occurs). When copy-mode is backed by a pane, backing points
        !           141:  * directly at that pane's screen structure (&wp->base); when backed by a list
        !           142:  * of output-lines from a command, it points at a newly-allocated screen
        !           143:  * structure (which is deallocated when the mode ends).
        !           144:  */
        !           145: struct window_copy_mode_data {
        !           146:        struct screen    screen;
        !           147: 
        !           148:        struct screen   *backing;
        !           149:        int              backing_written; /* backing display started */
        !           150: 
        !           151:        u_int            oy; /* number of lines scrolled up */
        !           152: 
        !           153:        u_int            selx; /* beginning of selection */
        !           154:        u_int            sely;
        !           155: 
        !           156:        u_int            endselx; /* end of selection */
        !           157:        u_int            endsely;
        !           158: 
        !           159:        enum {
        !           160:                CURSORDRAG_NONE,        /* selection is independent of cursor */
        !           161:                CURSORDRAG_ENDSEL,      /* end is synchronized with cursor */
        !           162:                CURSORDRAG_SEL,         /* start is synchronized with cursor */
        !           163:        } cursordrag;
        !           164: 
        !           165:        int              rectflag;      /* in rectangle copy mode? */
        !           166:        int              scroll_exit;   /* exit on scroll to end? */
        !           167: 
        !           168:        u_int            cx;
        !           169:        u_int            cy;
        !           170: 
        !           171:        u_int            lastcx; /* position in last line w/ content */
        !           172:        u_int            lastsx; /* size of last line w/ content */
        !           173: 
        !           174:        int              searchtype;
        !           175:        char            *searchstr;
        !           176:        bitstr_t        *searchmark;
        !           177:        u_int            searchcount;
        !           178:        int              searchthis;
        !           179:        int              searchx;
        !           180:        int              searchy;
        !           181:        int              searcho;
        !           182: 
        !           183:        int              jumptype;
        !           184:        char             jumpchar;
        !           185: };
        !           186: 
        !           187: static struct screen *
        !           188: window_copy_init(struct window_pane *wp)
        !           189: {
        !           190:        struct window_copy_mode_data    *data;
        !           191:        struct screen                   *s;
        !           192: 
        !           193:        wp->modedata = data = xmalloc(sizeof *data);
        !           194: 
        !           195:        data->oy = 0;
        !           196:        data->cx = 0;
        !           197:        data->cy = 0;
        !           198: 
        !           199:        data->cursordrag = CURSORDRAG_NONE;
        !           200: 
        !           201:        data->lastcx = 0;
        !           202:        data->lastsx = 0;
        !           203: 
        !           204:        data->backing_written = 0;
        !           205: 
        !           206:        data->rectflag = 0;
        !           207:        data->scroll_exit = 0;
        !           208: 
        !           209:        data->searchtype = WINDOW_COPY_OFF;
        !           210:        data->searchstr = NULL;
        !           211:        data->searchmark = NULL;
        !           212:        data->searchx = data->searchy = data->searcho = -1;
        !           213: 
        !           214:        if (wp->fd != -1)
        !           215:                bufferevent_disable(wp->event, EV_READ|EV_WRITE);
        !           216: 
        !           217:        data->jumptype = WINDOW_COPY_OFF;
        !           218:        data->jumpchar = '\0';
        !           219: 
        !           220:        s = &data->screen;
        !           221:        screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
        !           222:        s->sel.modekeys = options_get_number(wp->window->options, "mode-keys");
        !           223: 
        !           224:        data->backing = NULL;
        !           225: 
        !           226:        return (s);
        !           227: }
        !           228: 
        !           229: void
        !           230: window_copy_init_from_pane(struct window_pane *wp, int scroll_exit)
        !           231: {
        !           232:        struct window_copy_mode_data    *data = wp->modedata;
        !           233:        struct screen                   *s = &data->screen;
        !           234:        struct screen_write_ctx          ctx;
        !           235:        u_int                            i;
        !           236: 
        !           237:        if (wp->mode != &window_copy_mode)
        !           238:                fatalx("not in copy mode");
        !           239: 
        !           240:        data->backing = &wp->base;
        !           241:        data->cx = data->backing->cx;
        !           242:        data->cy = data->backing->cy;
        !           243:        data->scroll_exit = scroll_exit;
        !           244: 
        !           245:        s->cx = data->cx;
        !           246:        s->cy = data->cy;
        !           247: 
        !           248:        screen_write_start(&ctx, NULL, s);
        !           249:        for (i = 0; i < screen_size_y(s); i++)
        !           250:                window_copy_write_line(wp, &ctx, i);
        !           251:        screen_write_cursormove(&ctx, data->cx, data->cy);
        !           252:        screen_write_stop(&ctx);
        !           253: }
        !           254: 
        !           255: void
        !           256: window_copy_init_for_output(struct window_pane *wp)
        !           257: {
        !           258:        struct window_copy_mode_data    *data = wp->modedata;
        !           259: 
        !           260:        data->backing = xmalloc(sizeof *data->backing);
        !           261:        screen_init(data->backing, screen_size_x(&wp->base),
        !           262:            screen_size_y(&wp->base), UINT_MAX);
        !           263: }
        !           264: 
        !           265: static void
        !           266: window_copy_free(struct window_pane *wp)
        !           267: {
        !           268:        struct window_copy_mode_data    *data = wp->modedata;
        !           269: 
        !           270:        if (wp->fd != -1)
        !           271:                bufferevent_enable(wp->event, EV_READ|EV_WRITE);
        !           272: 
        !           273:        free(data->searchmark);
        !           274:        free(data->searchstr);
        !           275: 
        !           276:        if (data->backing != &wp->base) {
        !           277:                screen_free(data->backing);
        !           278:                free(data->backing);
        !           279:        }
        !           280:        screen_free(&data->screen);
        !           281: 
        !           282:        free(data);
        !           283: }
        !           284: 
        !           285: void
        !           286: window_copy_add(struct window_pane *wp, const char *fmt, ...)
        !           287: {
        !           288:        va_list ap;
        !           289: 
        !           290:        va_start(ap, fmt);
        !           291:        window_copy_vadd(wp, fmt, ap);
        !           292:        va_end(ap);
        !           293: }
        !           294: 
        !           295: void
        !           296: window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
        !           297: {
        !           298:        struct window_copy_mode_data    *data = wp->modedata;
        !           299:        struct screen                   *backing = data->backing;
        !           300:        struct screen_write_ctx          back_ctx, ctx;
        !           301:        struct grid_cell                 gc;
        !           302:        u_int                            old_hsize, old_cy;
        !           303: 
        !           304:        if (backing == &wp->base)
        !           305:                return;
        !           306: 
        !           307:        memcpy(&gc, &grid_default_cell, sizeof gc);
        !           308: 
        !           309:        old_hsize = screen_hsize(data->backing);
        !           310:        screen_write_start(&back_ctx, NULL, backing);
        !           311:        if (data->backing_written) {
        !           312:                /*
        !           313:                 * On the second or later line, do a CRLF before writing
        !           314:                 * (so it's on a new line).
        !           315:                 */
        !           316:                screen_write_carriagereturn(&back_ctx);
        !           317:                screen_write_linefeed(&back_ctx, 0);
        !           318:        } else
        !           319:                data->backing_written = 1;
        !           320:        old_cy = backing->cy;
        !           321:        screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap);
        !           322:        screen_write_stop(&back_ctx);
        !           323: 
        !           324:        data->oy += screen_hsize(data->backing) - old_hsize;
        !           325: 
        !           326:        screen_write_start(&ctx, wp, &data->screen);
        !           327: 
        !           328:        /*
        !           329:         * If the history has changed, draw the top line.
        !           330:         * (If there's any history at all, it has changed.)
        !           331:         */
        !           332:        if (screen_hsize(data->backing))
        !           333:                window_copy_redraw_lines(wp, 0, 1);
        !           334: 
        !           335:        /* Write the new lines. */
        !           336:        window_copy_redraw_lines(wp, old_cy, backing->cy - old_cy + 1);
        !           337: 
        !           338:        screen_write_stop(&ctx);
        !           339: }
        !           340: 
        !           341: void
        !           342: window_copy_pageup(struct window_pane *wp, int half_page)
        !           343: {
        !           344:        struct window_copy_mode_data    *data = wp->modedata;
        !           345:        struct screen                   *s = &data->screen;
        !           346:        u_int                            n, ox, oy, px, py;
        !           347: 
        !           348:        oy = screen_hsize(data->backing) + data->cy - data->oy;
        !           349:        ox = window_copy_find_length(wp, oy);
        !           350: 
        !           351:        if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
        !           352:                window_copy_other_end(wp);
        !           353: 
        !           354:        if (data->cx != ox) {
        !           355:                data->lastcx = data->cx;
        !           356:                data->lastsx = ox;
        !           357:        }
        !           358:        data->cx = data->lastcx;
        !           359: 
        !           360:        n = 1;
        !           361:        if (screen_size_y(s) > 2) {
        !           362:                if (half_page)
        !           363:                        n = screen_size_y(s) / 2;
        !           364:                else
        !           365:                        n = screen_size_y(s) - 2;
        !           366:        }
        !           367: 
        !           368:        if (data->oy + n > screen_hsize(data->backing))
        !           369:                data->oy = screen_hsize(data->backing);
        !           370:        else
        !           371:                data->oy += n;
        !           372: 
        !           373:        if (!data->screen.sel.flag || !data->rectflag) {
        !           374:                py = screen_hsize(data->backing) + data->cy - data->oy;
        !           375:                px = window_copy_find_length(wp, py);
        !           376:                if ((data->cx >= data->lastsx && data->cx != px) ||
        !           377:                    data->cx > px)
        !           378:                        window_copy_cursor_end_of_line(wp);
        !           379:        }
        !           380: 
        !           381:        window_copy_update_selection(wp, 1);
        !           382:        window_copy_redraw_screen(wp);
        !           383: }
        !           384: 
        !           385: static int
        !           386: window_copy_pagedown(struct window_pane *wp, int half_page)
        !           387: {
        !           388:        struct window_copy_mode_data    *data = wp->modedata;
        !           389:        struct screen                   *s = &data->screen;
        !           390:        u_int                            n, ox, oy, px, py;
        !           391: 
        !           392:        oy = screen_hsize(data->backing) + data->cy - data->oy;
        !           393:        ox = window_copy_find_length(wp, oy);
        !           394: 
        !           395:        if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely)
        !           396:                window_copy_other_end(wp);
        !           397: 
        !           398:        if (data->cx != ox) {
        !           399:                data->lastcx = data->cx;
        !           400:                data->lastsx = ox;
        !           401:        }
        !           402:        data->cx = data->lastcx;
        !           403: 
        !           404:        n = 1;
        !           405:        if (screen_size_y(s) > 2) {
        !           406:                if (half_page)
        !           407:                        n = screen_size_y(s) / 2;
        !           408:                else
        !           409:                        n = screen_size_y(s) - 2;
        !           410:        }
        !           411: 
        !           412:        if (data->oy < n)
        !           413:                data->oy = 0;
        !           414:        else
        !           415:                data->oy -= n;
        !           416: 
        !           417:        if (!data->screen.sel.flag || !data->rectflag) {
        !           418:                py = screen_hsize(data->backing) + data->cy - data->oy;
        !           419:                px = window_copy_find_length(wp, py);
        !           420:                if ((data->cx >= data->lastsx && data->cx != px) ||
        !           421:                    data->cx > px)
        !           422:                        window_copy_cursor_end_of_line(wp);
        !           423:        }
        !           424: 
        !           425:        if (data->scroll_exit && data->oy == 0)
        !           426:                return (1);
        !           427:        window_copy_update_selection(wp, 1);
        !           428:        window_copy_redraw_screen(wp);
        !           429:        return (0);
        !           430: }
        !           431: 
        !           432: static void
        !           433: window_copy_previous_paragraph(struct window_pane *wp)
        !           434: {
        !           435:        struct window_copy_mode_data    *data = wp->modedata;
        !           436:        u_int                            oy;
        !           437: 
        !           438:        oy = screen_hsize(data->backing) + data->cy - data->oy;
        !           439: 
        !           440:        while (oy > 0 && window_copy_find_length(wp, oy) == 0)
        !           441:                oy--;
        !           442: 
        !           443:        while (oy > 0 && window_copy_find_length(wp, oy) > 0)
        !           444:                oy--;
        !           445: 
        !           446:        window_copy_scroll_to(wp, 0, oy);
        !           447: }
        !           448: 
        !           449: static void
        !           450: window_copy_next_paragraph(struct window_pane *wp)
        !           451: {
        !           452:        struct window_copy_mode_data    *data = wp->modedata;
        !           453:        struct screen                   *s = &data->screen;
        !           454:        u_int                            maxy, ox, oy;
        !           455: 
        !           456:        oy = screen_hsize(data->backing) + data->cy - data->oy;
        !           457:        maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
        !           458: 
        !           459:        while (oy < maxy && window_copy_find_length(wp, oy) == 0)
        !           460:                oy++;
        !           461: 
        !           462:        while (oy < maxy && window_copy_find_length(wp, oy) > 0)
        !           463:                oy++;
        !           464: 
        !           465:        ox = window_copy_find_length(wp, oy);
        !           466:        window_copy_scroll_to(wp, ox, oy);
        !           467: }
        !           468: 
        !           469: static void
        !           470: window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
        !           471: {
        !           472:        struct window_copy_mode_data    *data = wp->modedata;
        !           473:        struct screen                   *s = &data->screen;
        !           474:        struct screen_write_ctx          ctx;
        !           475: 
        !           476:        screen_resize(s, sx, sy, 1);
        !           477:        if (data->backing != &wp->base)
        !           478:                screen_resize(data->backing, sx, sy, 1);
        !           479: 
        !           480:        if (data->cy > sy - 1)
        !           481:                data->cy = sy - 1;
        !           482:        if (data->cx > sx)
        !           483:                data->cx = sx;
        !           484:        if (data->oy > screen_hsize(data->backing))
        !           485:                data->oy = screen_hsize(data->backing);
        !           486: 
        !           487:        window_copy_clear_selection(wp);
        !           488: 
        !           489:        screen_write_start(&ctx, NULL, s);
        !           490:        window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
        !           491:        screen_write_stop(&ctx);
        !           492: 
        !           493:        if (data->searchmark != NULL)
        !           494:                window_copy_search_marks(wp, NULL);
        !           495:        data->searchx = data->cx;
        !           496:        data->searchy = data->cy;
        !           497:        data->searcho = data->oy;
        !           498: 
        !           499:        window_copy_redraw_screen(wp);
        !           500: }
        !           501: 
        !           502: static const char *
        !           503: window_copy_key_table(struct window_pane *wp)
        !           504: {
        !           505:        if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
        !           506:                return ("copy-mode-vi");
        !           507:        return ("copy-mode");
        !           508: }
        !           509: 
        !           510: static void
        !           511: window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
        !           512:     struct args *args, struct mouse_event *m)
        !           513: {
        !           514:        struct window_copy_mode_data    *data = wp->modedata;
        !           515:        struct screen                   *sn = &data->screen;
        !           516:        const char                      *command, *argument, *ws;
        !           517:        u_int                            np = wp->modeprefix;
        !           518:        int                              cancel = 0, redraw = 0;
        !           519:        char                             prefix;
        !           520: 
        !           521:        if (args->argc == 0)
        !           522:                return;
        !           523:        command = args->argv[0];
        !           524: 
        !           525:        if (m != NULL && m->valid)
        !           526:                window_copy_move_mouse(m);
        !           527: 
        !           528:        if (args->argc == 1) {
        !           529:                if (strcmp(command, "append-selection") == 0) {
        !           530:                        if (s != NULL)
        !           531:                                window_copy_append_selection(wp, NULL);
        !           532:                        window_copy_clear_selection(wp);
        !           533:                        redraw = 1;
        !           534:                }
        !           535:                if (strcmp(command, "append-selection-and-cancel") == 0) {
        !           536:                        if (s != NULL)
        !           537:                                window_copy_append_selection(wp, NULL);
        !           538:                        window_copy_clear_selection(wp);
        !           539:                        redraw = 1;
        !           540:                        cancel = 1;
        !           541:                }
        !           542:                if (strcmp(command, "back-to-indentation") == 0)
        !           543:                        window_copy_cursor_back_to_indentation(wp);
        !           544:                if (strcmp(command, "begin-selection") == 0) {
        !           545:                        if (m != NULL)
        !           546:                                window_copy_start_drag(c, m);
        !           547:                        else {
        !           548:                                sn->sel.lineflag = LINE_SEL_NONE;
        !           549:                                window_copy_start_selection(wp);
        !           550:                                redraw = 1;
        !           551:                        }
        !           552:                }
        !           553:                if (strcmp(command, "stop-selection") == 0)
        !           554:                        data->cursordrag = CURSORDRAG_NONE;
        !           555:                if (strcmp(command, "bottom-line") == 0) {
        !           556:                        data->cx = 0;
        !           557:                        data->cy = screen_size_y(sn) - 1;
        !           558:                        window_copy_update_selection(wp, 1);
        !           559:                        redraw = 1;
        !           560:                }
        !           561:                if (strcmp(command, "cancel") == 0)
        !           562:                        cancel = 1;
        !           563:                if (strcmp(command, "clear-selection") == 0) {
        !           564:                        window_copy_clear_selection(wp);
        !           565:                        redraw = 1;
        !           566:                }
        !           567:                if (strcmp(command, "copy-end-of-line") == 0) {
        !           568:                        window_copy_start_selection(wp);
        !           569:                        for (; np > 1; np--)
        !           570:                                window_copy_cursor_down(wp, 0);
        !           571:                        window_copy_cursor_end_of_line(wp);
        !           572:                        redraw = 1;
        !           573: 
        !           574:                        if (s != NULL) {
        !           575:                                window_copy_copy_selection(wp, NULL);
        !           576:                                cancel = 1;
        !           577:                        }
        !           578:                }
        !           579:                if (strcmp(command, "copy-line") == 0) {
        !           580:                        window_copy_cursor_start_of_line(wp);
        !           581:                        window_copy_start_selection(wp);
        !           582:                        for (; np > 1; np--)
        !           583:                                window_copy_cursor_down(wp, 0);
        !           584:                        window_copy_cursor_end_of_line(wp);
        !           585:                        redraw = 1;
        !           586: 
        !           587:                        if (s != NULL) {
        !           588:                                window_copy_copy_selection(wp, NULL);
        !           589:                                cancel = 1;
        !           590:                        }
        !           591:                }
        !           592:                if (strcmp(command, "copy-selection") == 0) {
        !           593:                        if (s != NULL)
        !           594:                                window_copy_copy_selection(wp, NULL);
        !           595:                        window_copy_clear_selection(wp);
        !           596:                        redraw = 1;
        !           597:                }
        !           598:                if (strcmp(command, "copy-selection-and-cancel") == 0) {
        !           599:                        if (s != NULL)
        !           600:                                window_copy_copy_selection(wp, NULL);
        !           601:                        window_copy_clear_selection(wp);
        !           602:                        redraw = 1;
        !           603:                        cancel = 1;
        !           604:                }
        !           605:                if (strcmp(command, "cursor-down") == 0) {
        !           606:                        for (; np != 0; np--)
        !           607:                                window_copy_cursor_down(wp, 0);
        !           608:                }
        !           609:                if (strcmp(command, "cursor-left") == 0) {
        !           610:                        for (; np != 0; np--)
        !           611:                                window_copy_cursor_left(wp);
        !           612:                }
        !           613:                if (strcmp(command, "cursor-right") == 0) {
        !           614:                        for (; np != 0; np--)
        !           615:                                window_copy_cursor_right(wp);
        !           616:                }
        !           617:                if (strcmp(command, "cursor-up") == 0) {
        !           618:                        for (; np != 0; np--)
        !           619:                                window_copy_cursor_up(wp, 0);
        !           620:                }
        !           621:                if (strcmp(command, "end-of-line") == 0)
        !           622:                        window_copy_cursor_end_of_line(wp);
        !           623:                if (strcmp(command, "halfpage-down") == 0) {
        !           624:                        for (; np != 0; np--) {
        !           625:                                if (window_copy_pagedown(wp, 1)) {
        !           626:                                        cancel = 1;
        !           627:                                        break;
        !           628:                                }
        !           629:                        }
        !           630:                }
        !           631:                if (strcmp(command, "halfpage-up") == 0) {
        !           632:                        for (; np != 0; np--)
        !           633:                                window_copy_pageup(wp, 1);
        !           634:                }
        !           635:                if (strcmp(command, "history-bottom") == 0) {
        !           636:                        data->cx = 0;
        !           637:                        data->cy = screen_size_y(sn) - 1;
        !           638:                        data->oy = 0;
        !           639:                        window_copy_update_selection(wp, 1);
        !           640:                        redraw = 1;
        !           641:                }
        !           642:                if (strcmp(command, "history-top") == 0) {
        !           643:                        data->cx = 0;
        !           644:                        data->cy = 0;
        !           645:                        data->oy = screen_hsize(data->backing);
        !           646:                        window_copy_update_selection(wp, 1);
        !           647:                        redraw = 1;
        !           648:                }
        !           649:                if (strcmp(command, "jump-again") == 0) {
        !           650:                        switch (data->jumptype) {
        !           651:                        case WINDOW_COPY_JUMPFORWARD:
        !           652:                                for (; np != 0; np--)
        !           653:                                        window_copy_cursor_jump(wp);
        !           654:                                break;
        !           655:                        case WINDOW_COPY_JUMPBACKWARD:
        !           656:                                for (; np != 0; np--)
        !           657:                                        window_copy_cursor_jump_back(wp);
        !           658:                                break;
        !           659:                        case WINDOW_COPY_JUMPTOFORWARD:
        !           660:                                for (; np != 0; np--)
        !           661:                                        window_copy_cursor_jump_to(wp, 1);
        !           662:                                break;
        !           663:                        case WINDOW_COPY_JUMPTOBACKWARD:
        !           664:                                for (; np != 0; np--)
        !           665:                                        window_copy_cursor_jump_to_back(wp, 1);
        !           666:                                break;
        !           667:                        }
        !           668:                }
        !           669:                if (strcmp(command, "jump-reverse") == 0) {
        !           670:                        switch (data->jumptype) {
        !           671:                        case WINDOW_COPY_JUMPFORWARD:
        !           672:                                for (; np != 0; np--)
        !           673:                                        window_copy_cursor_jump_back(wp);
        !           674:                                break;
        !           675:                        case WINDOW_COPY_JUMPBACKWARD:
        !           676:                                for (; np != 0; np--)
        !           677:                                        window_copy_cursor_jump(wp);
        !           678:                                break;
        !           679:                        case WINDOW_COPY_JUMPTOFORWARD:
        !           680:                                for (; np != 0; np--)
        !           681:                                        window_copy_cursor_jump_to_back(wp, 1);
        !           682:                                break;
        !           683:                        case WINDOW_COPY_JUMPTOBACKWARD:
        !           684:                                for (; np != 0; np--)
        !           685:                                        window_copy_cursor_jump_to(wp, 1);
        !           686:                                break;
        !           687:                        }
        !           688:                }
        !           689:                if (strcmp(command, "middle-line") == 0) {
        !           690:                        data->cx = 0;
        !           691:                        data->cy = (screen_size_y(sn) - 1) / 2;
        !           692:                        window_copy_update_selection(wp, 1);
        !           693:                        redraw = 1;
        !           694:                }
        !           695:                if (strcmp(command, "next-paragraph") == 0) {
        !           696:                        for (; np != 0; np--)
        !           697:                                window_copy_next_paragraph(wp);
        !           698:                }
        !           699:                if (strcmp(command, "next-space") == 0) {
        !           700:                        for (; np != 0; np--)
        !           701:                                window_copy_cursor_next_word(wp, " ");
        !           702:                }
        !           703:                if (strcmp(command, "next-space-end") == 0) {
        !           704:                        for (; np != 0; np--)
        !           705:                                window_copy_cursor_next_word_end(wp, " ");
        !           706:                }
        !           707:                if (strcmp(command, "next-word") == 0) {
        !           708:                        ws = options_get_string(s->options, "word-separators");
        !           709:                        for (; np != 0; np--)
        !           710:                                window_copy_cursor_next_word(wp, ws);
        !           711:                }
        !           712:                if (strcmp(command, "next-word-end") == 0) {
        !           713:                        ws = options_get_string(s->options, "word-separators");
        !           714:                        for (; np != 0; np--)
        !           715:                                window_copy_cursor_next_word_end(wp, ws);
        !           716:                }
        !           717:                if (strcmp(command, "other-end") == 0) {
        !           718:                        if ((np % 2) != 0)
        !           719:                                window_copy_other_end(wp);
        !           720:                }
        !           721:                if (strcmp(command, "page-down") == 0) {
        !           722:                        for (; np != 0; np--) {
        !           723:                                if (window_copy_pagedown(wp, 0)) {
        !           724:                                        cancel = 1;
        !           725:                                        break;
        !           726:                                }
        !           727:                        }
        !           728:                }
        !           729:                if (strcmp(command, "page-up") == 0) {
        !           730:                        for (; np != 0; np--)
        !           731:                                window_copy_pageup(wp, 0);
        !           732:                }
        !           733:                if (strcmp(command, "previous-paragraph") == 0) {
        !           734:                        for (; np != 0; np--)
        !           735:                                window_copy_previous_paragraph(wp);
        !           736:                }
        !           737:                if (strcmp(command, "previous-space") == 0) {
        !           738:                        for (; np != 0; np--)
        !           739:                                window_copy_cursor_previous_word(wp, " ");
        !           740:                }
        !           741:                if (strcmp(command, "previous-word") == 0) {
        !           742:                        ws = options_get_string(s->options, "word-separators");
        !           743:                        for (; np != 0; np--)
        !           744:                                window_copy_cursor_previous_word(wp, ws);
        !           745:                }
        !           746:                if (strcmp(command, "rectangle-toggle") == 0) {
        !           747:                        sn->sel.lineflag = LINE_SEL_NONE;
        !           748:                        window_copy_rectangle_toggle(wp);
        !           749:                }
        !           750:                if (strcmp(command, "scroll-down") == 0) {
        !           751:                        for (; np != 0; np--)
        !           752:                                window_copy_cursor_down(wp, 1);
        !           753:                        if (data->scroll_exit && data->oy == 0)
        !           754:                                cancel = 1;
        !           755:                }
        !           756:                if (strcmp(command, "scroll-up") == 0) {
        !           757:                        for (; np != 0; np--)
        !           758:                                window_copy_cursor_up(wp, 1);
        !           759:                }
        !           760:                if (strcmp(command, "search-again") == 0) {
        !           761:                        if (data->searchtype == WINDOW_COPY_SEARCHUP) {
        !           762:                                for (; np != 0; np--)
        !           763:                                        window_copy_search_up(wp, 1);
        !           764:                        } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
        !           765:                                for (; np != 0; np--)
        !           766:                                        window_copy_search_down(wp, 1);
        !           767:                        }
        !           768:                }
        !           769:                if (strcmp(command, "search-reverse") == 0) {
        !           770:                        if (data->searchtype == WINDOW_COPY_SEARCHUP) {
        !           771:                                for (; np != 0; np--)
        !           772:                                        window_copy_search_down(wp, 1);
        !           773:                        } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
        !           774:                                for (; np != 0; np--)
        !           775:                                        window_copy_search_up(wp, 1);
        !           776:                        }
        !           777:                }
        !           778:                if (strcmp(command, "select-line") == 0) {
        !           779:                        sn->sel.lineflag = LINE_SEL_LEFT_RIGHT;
        !           780:                        data->rectflag = 0;
        !           781:                        window_copy_cursor_start_of_line(wp);
        !           782:                        window_copy_start_selection(wp);
        !           783:                        for (; np > 1; np--)
        !           784:                                window_copy_cursor_down(wp, 0);
        !           785:                        window_copy_cursor_end_of_line(wp);
        !           786:                        redraw = 1;
        !           787:                }
        !           788:                if (strcmp(command, "select-word") == 0) {
        !           789:                        sn->sel.lineflag = LINE_SEL_LEFT_RIGHT;
        !           790:                        data->rectflag = 0;
        !           791:                        ws = options_get_string(s->options, "word-separators");
        !           792:                        window_copy_cursor_previous_word(wp, ws);
        !           793:                        window_copy_start_selection(wp);
        !           794:                        window_copy_cursor_next_word_end(wp, ws);
        !           795:                        redraw = 1;
        !           796:                }
        !           797:                if (strcmp(command, "start-of-line") == 0)
        !           798:                        window_copy_cursor_start_of_line(wp);
        !           799:                if (strcmp(command, "top-line") == 0) {
        !           800:                        data->cx = 0;
        !           801:                        data->cy = 0;
        !           802:                        window_copy_update_selection(wp, 1);
        !           803:                        redraw = 1;
        !           804:                }
        !           805:        } else if (args->argc == 2 && *args->argv[1] != '\0') {
        !           806:                argument = args->argv[1];
        !           807:                if (strcmp(command, "copy-pipe") == 0) {
        !           808:                        if (s != NULL)
        !           809:                                window_copy_copy_pipe(wp, s, NULL, argument);
        !           810:                }
        !           811:                if (strcmp(command, "copy-pipe-and-cancel") == 0) {
        !           812:                        if (s != NULL) {
        !           813:                                window_copy_copy_pipe(wp, s, NULL, argument);
        !           814:                                cancel = 1;
        !           815:                        }
        !           816:                }
        !           817:                if (strcmp(command, "goto-line") == 0)
        !           818:                        window_copy_goto_line(wp, argument);
        !           819:                if (strcmp(command, "jump-backward") == 0) {
        !           820:                        data->jumptype = WINDOW_COPY_JUMPBACKWARD;
        !           821:                        data->jumpchar = *argument;
        !           822:                        for (; np != 0; np--)
        !           823:                                window_copy_cursor_jump_back(wp);
        !           824:                }
        !           825:                if (strcmp(command, "jump-forward") == 0) {
        !           826:                        data->jumptype = WINDOW_COPY_JUMPFORWARD;
        !           827:                        data->jumpchar = *argument;
        !           828:                        for (; np != 0; np--)
        !           829:                                window_copy_cursor_jump(wp);
        !           830:                }
        !           831:                if (strcmp(command, "jump-to-backward") == 0) {
        !           832:                        data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
        !           833:                        data->jumpchar = *argument;
        !           834:                        for (; np != 0; np--)
        !           835:                                window_copy_cursor_jump_to_back(wp, 1);
        !           836:                }
        !           837:                if (strcmp(command, "jump-to-forward") == 0) {
        !           838:                        data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
        !           839:                        data->jumpchar = *argument;
        !           840:                        for (; np != 0; np--)
        !           841:                                window_copy_cursor_jump_to(wp, 1);
        !           842:                }
        !           843:                if (strcmp(command, "search-backward") == 0) {
        !           844:                        data->searchtype = WINDOW_COPY_SEARCHUP;
        !           845:                        free(data->searchstr);
        !           846:                        data->searchstr = xstrdup(argument);
        !           847:                        for (; np != 0; np--)
        !           848:                                window_copy_search_up(wp, 1);
        !           849:                }
        !           850:                if (strcmp(command, "search-forward") == 0) {
        !           851:                        data->searchtype = WINDOW_COPY_SEARCHDOWN;
        !           852:                        free(data->searchstr);
        !           853:                        data->searchstr = xstrdup(argument);
        !           854:                        for (; np != 0; np--)
        !           855:                                window_copy_search_down(wp, 1);
        !           856:                }
        !           857:                if (strcmp(command, "search-backward-incremental") == 0) {
        !           858:                        prefix = *argument++;
        !           859:                        if (data->searchx == -1 || data->searchy == -1) {
        !           860:                                data->searchx = data->cx;
        !           861:                                data->searchy = data->cy;
        !           862:                                data->searcho = data->oy;
        !           863:                        } else if (data->searchstr != NULL &&
        !           864:                            strcmp(argument, data->searchstr) != 0) {
        !           865:                                data->cx = data->searchx;
        !           866:                                data->cy = data->searchy;
        !           867:                                data->oy = data->searcho;
        !           868:                                redraw = 1;
        !           869:                        }
        !           870:                        if (*argument == '\0') {
        !           871:                                window_copy_clear_marks(wp);
        !           872:                                redraw = 1;
        !           873:                        } else if (prefix == '=' || prefix == '-') {
        !           874:                                data->searchtype = WINDOW_COPY_SEARCHUP;
        !           875:                                free(data->searchstr);
        !           876:                                data->searchstr = xstrdup(argument);
        !           877:                                if (!window_copy_search_up(wp, 1)) {
        !           878:                                        window_copy_clear_marks(wp);
        !           879:                                        redraw = 1;
        !           880:                                }
        !           881:                        } else if (prefix == '+') {
        !           882:                                data->searchtype = WINDOW_COPY_SEARCHDOWN;
        !           883:                                free(data->searchstr);
        !           884:                                data->searchstr = xstrdup(argument);
        !           885:                                if (!window_copy_search_down(wp, 1)) {
        !           886:                                        window_copy_clear_marks(wp);
        !           887:                                        redraw = 1;
        !           888:                                }
        !           889:                        }
        !           890:                }
        !           891:                if (strcmp(command, "search-forward-incremental") == 0) {
        !           892:                        prefix = *argument++;
        !           893:                        if (data->searchx == -1 || data->searchy == -1) {
        !           894:                                data->searchx = data->cx;
        !           895:                                data->searchy = data->cy;
        !           896:                                data->searcho = data->oy;
        !           897:                        } else if (data->searchstr != NULL &&
        !           898:                            strcmp(argument, data->searchstr) != 0) {
        !           899:                                data->cx = data->searchx;
        !           900:                                data->cy = data->searchy;
        !           901:                                data->oy = data->searcho;
        !           902:                                redraw = 1;
        !           903:                        }
        !           904:                        if (*argument == '\0') {
        !           905:                                window_copy_clear_marks(wp);
        !           906:                                redraw = 1;
        !           907:                        } else if (prefix == '=' || prefix == '+') {
        !           908:                                data->searchtype = WINDOW_COPY_SEARCHDOWN;
        !           909:                                free(data->searchstr);
        !           910:                                data->searchstr = xstrdup(argument);
        !           911:                                if (!window_copy_search_down(wp, 1)) {
        !           912:                                        window_copy_clear_marks(wp);
        !           913:                                        redraw = 1;
        !           914:                                }
        !           915:                        } else if (prefix == '-') {
        !           916:                                data->searchtype = WINDOW_COPY_SEARCHUP;
        !           917:                                free(data->searchstr);
        !           918:                                data->searchstr = xstrdup(argument);
        !           919:                                if (!window_copy_search_up(wp, 1)) {
        !           920:                                        window_copy_clear_marks(wp);
        !           921:                                        redraw = 1;
        !           922:                                }
        !           923:                        }
        !           924:                }
        !           925:        }
        !           926: 
        !           927:        if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
        !           928:                window_copy_clear_marks(wp);
        !           929:                redraw = 1;
        !           930:                data->searchx = data->searchy = -1;
        !           931:        }
        !           932: 
        !           933:        if (cancel)
        !           934:                window_pane_reset_mode(wp);
        !           935:        else if (redraw)
        !           936:                window_copy_redraw_screen(wp);
        !           937:        wp->modeprefix = 1;
        !           938: }
        !           939: 
        !           940: static void
        !           941: window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
        !           942: {
        !           943:        struct window_copy_mode_data    *data = wp->modedata;
        !           944:        struct grid                     *gd = data->backing->grid;
        !           945:        u_int                            offset, gap;
        !           946: 
        !           947:        data->cx = px;
        !           948: 
        !           949:        gap = gd->sy / 4;
        !           950:        if (py < gd->sy) {
        !           951:                offset = 0;
        !           952:                data->cy = py;
        !           953:        } else if (py > gd->hsize + gd->sy - gap) {
        !           954:                offset = gd->hsize;
        !           955:                data->cy = py - gd->hsize;
        !           956:        } else {
        !           957:                offset = py + gap - gd->sy;
        !           958:                data->cy = py - offset;
        !           959:        }
        !           960:        data->oy = gd->hsize - offset;
        !           961: 
        !           962:        window_copy_update_selection(wp, 1);
        !           963:        window_copy_redraw_screen(wp);
        !           964: }
        !           965: 
        !           966: static int
        !           967: window_copy_search_compare(struct grid *gd, u_int px, u_int py,
        !           968:     struct grid *sgd, u_int spx, int cis)
        !           969: {
        !           970:        struct grid_cell         gc, sgc;
        !           971:        const struct utf8_data  *ud, *sud;
        !           972: 
        !           973:        grid_get_cell(gd, px, py, &gc);
        !           974:        ud = &gc.data;
        !           975:        grid_get_cell(sgd, spx, 0, &sgc);
        !           976:        sud = &sgc.data;
        !           977: 
        !           978:        if (ud->size != sud->size || ud->width != sud->width)
        !           979:                return (0);
        !           980: 
        !           981:        if (cis && ud->size == 1)
        !           982:                return (tolower(ud->data[0]) == sud->data[0]);
        !           983: 
        !           984:        return (memcmp(ud->data, sud->data, ud->size) == 0);
        !           985: }
        !           986: 
        !           987: static int
        !           988: window_copy_search_lr(struct grid *gd,
        !           989:     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
        !           990: {
        !           991:        u_int   ax, bx, px;
        !           992:        int     matched;
        !           993: 
        !           994:        for (ax = first; ax < last; ax++) {
        !           995:                if (ax + sgd->sx >= gd->sx)
        !           996:                        break;
        !           997:                for (bx = 0; bx < sgd->sx; bx++) {
        !           998:                        px = ax + bx;
        !           999:                        matched = window_copy_search_compare(gd, px, py, sgd,
        !          1000:                            bx, cis);
        !          1001:                        if (!matched)
        !          1002:                                break;
        !          1003:                }
        !          1004:                if (bx == sgd->sx) {
        !          1005:                        *ppx = ax;
        !          1006:                        return (1);
        !          1007:                }
        !          1008:        }
        !          1009:        return (0);
        !          1010: }
        !          1011: 
        !          1012: static int
        !          1013: window_copy_search_rl(struct grid *gd,
        !          1014:     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
        !          1015: {
        !          1016:        u_int   ax, bx, px;
        !          1017:        int     matched;
        !          1018: 
        !          1019:        for (ax = last + 1; ax > first; ax--) {
        !          1020:                if (gd->sx - (ax - 1) < sgd->sx)
        !          1021:                        continue;
        !          1022:                for (bx = 0; bx < sgd->sx; bx++) {
        !          1023:                        px = ax - 1 + bx;
        !          1024:                        matched = window_copy_search_compare(gd, px, py, sgd,
        !          1025:                            bx, cis);
        !          1026:                        if (!matched)
        !          1027:                                break;
        !          1028:                }
        !          1029:                if (bx == sgd->sx) {
        !          1030:                        *ppx = ax - 1;
        !          1031:                        return (1);
        !          1032:                }
        !          1033:        }
        !          1034:        return (0);
        !          1035: }
        !          1036: 
        !          1037: static void
        !          1038: window_copy_move_left(struct screen *s, u_int *fx, u_int *fy)
        !          1039: {
        !          1040:        if (*fx == 0) { /* left */
        !          1041:                if (*fy == 0) /* top */
        !          1042:                        return;
        !          1043:                *fx = screen_size_x(s) - 1;
        !          1044:                *fy = *fy - 1;
        !          1045:        } else
        !          1046:                *fx = *fx - 1;
        !          1047: }
        !          1048: 
        !          1049: static void
        !          1050: window_copy_move_right(struct screen *s, u_int *fx, u_int *fy)
        !          1051: {
        !          1052:        if (*fx == screen_size_x(s) - 1) { /* right */
        !          1053:                if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */
        !          1054:                        return;
        !          1055:                *fx = 0;
        !          1056:                *fy = *fy + 1;
        !          1057:        } else
        !          1058:                *fx = *fx + 1;
        !          1059: }
        !          1060: 
        !          1061: static int
        !          1062: window_copy_is_lowercase(const char *ptr)
        !          1063: {
        !          1064:        while (*ptr != '\0') {
        !          1065:                if (*ptr != tolower((u_char)*ptr))
        !          1066:                        return (0);
        !          1067:                ++ptr;
        !          1068:        }
        !          1069:        return (1);
        !          1070: }
        !          1071: 
        !          1072: /*
        !          1073:  * Search for text stored in sgd starting from position fx,fy up to endline. If
        !          1074:  * found, jump to it. If cis then ignore case. The direction is 0 for searching
        !          1075:  * up, down otherwise. If wrap then go to begin/end of grid and try again if
        !          1076:  * not found.
        !          1077:  */
        !          1078: static int
        !          1079: window_copy_search_jump(struct window_pane *wp, struct grid *gd,
        !          1080:     struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
        !          1081:     int direction)
        !          1082: {
        !          1083:        u_int   i, px;
        !          1084:        int     found;
        !          1085: 
        !          1086:        found = 0;
        !          1087:        if (direction) {
        !          1088:                for (i = fy; i <= endline; i++) {
        !          1089:                        found = window_copy_search_lr(gd, sgd, &px, i, fx,
        !          1090:                            gd->sx, cis);
        !          1091:                        if (found)
        !          1092:                                break;
        !          1093:                        fx = 0;
        !          1094:                }
        !          1095:        } else {
        !          1096:                for (i = fy + 1; endline < i; i--) {
        !          1097:                        found = window_copy_search_rl(gd, sgd, &px, i - 1, 0,
        !          1098:                            fx, cis);
        !          1099:                        if (found) {
        !          1100:                                i--;
        !          1101:                                break;
        !          1102:                        }
        !          1103:                        fx = gd->sx;
        !          1104:                }
        !          1105:        }
        !          1106: 
        !          1107:        if (found) {
        !          1108:                window_copy_scroll_to(wp, px, i);
        !          1109:                return (1);
        !          1110:        }
        !          1111:        if (wrap) {
        !          1112:                return (window_copy_search_jump(wp, gd, sgd,
        !          1113:                    direction ? 0 : gd->sx - 1,
        !          1114:                    direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
        !          1115:                    direction));
        !          1116:        }
        !          1117:        return (0);
        !          1118: }
        !          1119: 
        !          1120: /*
        !          1121:  * Search in for text searchstr. If direction is 0 then search up, otherwise
        !          1122:  * down. If moveflag is 0 then look for string at the current cursor position
        !          1123:  * as well.
        !          1124:  */
        !          1125: static int
        !          1126: window_copy_search(struct window_pane *wp, int direction, int moveflag)
        !          1127: {
        !          1128:        struct window_copy_mode_data    *data = wp->modedata;
        !          1129:        struct screen                   *s = data->backing, ss;
        !          1130:        struct screen_write_ctx          ctx;
        !          1131:        struct grid                     *gd = s->grid;
        !          1132:        u_int                            fx, fy, endline;
        !          1133:        int                              wrapflag, cis, found;
        !          1134: 
        !          1135:        fx = data->cx;
        !          1136:        fy = screen_hsize(data->backing) - data->oy + data->cy;
        !          1137: 
        !          1138:        screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0);
        !          1139:        screen_write_start(&ctx, NULL, &ss);
        !          1140:        screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr);
        !          1141:        screen_write_stop(&ctx);
        !          1142: 
        !          1143:        if (moveflag) {
        !          1144:                if (direction)
        !          1145:                        window_copy_move_right(s, &fx, &fy);
        !          1146:                else
        !          1147:                        window_copy_move_left(s, &fx, &fy);
        !          1148:        }
        !          1149:        window_copy_clear_selection(wp);
        !          1150: 
        !          1151:        wrapflag = options_get_number(wp->window->options, "wrap-search");
        !          1152:        cis = window_copy_is_lowercase(data->searchstr);
        !          1153: 
        !          1154:        if (direction)
        !          1155:                endline = gd->hsize + gd->sy - 1;
        !          1156:        else
        !          1157:                endline = 0;
        !          1158:        found = window_copy_search_jump(wp, gd, ss.grid, fx, fy, endline, cis,
        !          1159:            wrapflag, direction);
        !          1160: 
        !          1161:        if (window_copy_search_marks(wp, &ss))
        !          1162:                window_copy_redraw_screen(wp);
        !          1163: 
        !          1164:        screen_free(&ss);
        !          1165:        return (found);
        !          1166: }
        !          1167: 
        !          1168: static int
        !          1169: window_copy_search_marks(struct window_pane *wp, struct screen *ssp)
        !          1170: {
        !          1171:        struct window_copy_mode_data    *data = wp->modedata;
        !          1172:        struct screen                   *s = data->backing, ss;
        !          1173:        struct screen_write_ctx          ctx;
        !          1174:        struct grid                     *gd = s->grid;
        !          1175:        int                              found, cis, which = -1;
        !          1176:        u_int                            px, py, b, nfound = 0, width;
        !          1177: 
        !          1178:        if (ssp == NULL) {
        !          1179:                width = screen_write_strlen("%s", data->searchstr);
        !          1180:                screen_init(&ss, width, 1, 0);
        !          1181:                screen_write_start(&ctx, NULL, &ss);
        !          1182:                screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
        !          1183:                    data->searchstr);
        !          1184:                screen_write_stop(&ctx);
        !          1185:                ssp = &ss;
        !          1186:        } else
        !          1187:                width = screen_size_x(ssp);
        !          1188: 
        !          1189:        cis = window_copy_is_lowercase(data->searchstr);
        !          1190: 
        !          1191:        free(data->searchmark);
        !          1192:        data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx);
        !          1193: 
        !          1194:        for (py = 0; py < gd->hsize + gd->sy; py++) {
        !          1195:                px = 0;
        !          1196:                for (;;) {
        !          1197:                        found = window_copy_search_lr(gd, ssp->grid, &px, py,
        !          1198:                            px, gd->sx, cis);
        !          1199:                        if (!found)
        !          1200:                                break;
        !          1201: 
        !          1202:                        nfound++;
        !          1203:                        if (px == data->cx && py == gd->hsize + data->cy - data->oy)
        !          1204:                                which = nfound;
        !          1205: 
        !          1206:                        b = (py * gd->sx) + px;
        !          1207:                        bit_nset(data->searchmark, b, b + width - 1);
        !          1208: 
        !          1209:                        px++;
        !          1210:                }
        !          1211:        }
        !          1212: 
        !          1213:        if (which != -1)
        !          1214:                data->searchthis = 1 + nfound - which;
        !          1215:        else
        !          1216:                data->searchthis = -1;
        !          1217:        data->searchcount = nfound;
        !          1218: 
        !          1219:        if (ssp == &ss)
        !          1220:                screen_free(&ss);
        !          1221:        return (nfound);
        !          1222: }
        !          1223: 
        !          1224: static void
        !          1225: window_copy_clear_marks(struct window_pane *wp)
        !          1226: {
        !          1227:        struct window_copy_mode_data    *data = wp->modedata;
        !          1228: 
        !          1229:        free(data->searchmark);
        !          1230:        data->searchmark = NULL;
        !          1231: }
        !          1232: 
        !          1233: static int
        !          1234: window_copy_search_up(struct window_pane *wp, int moveflag)
        !          1235: {
        !          1236:        return (window_copy_search(wp, 0, moveflag));
        !          1237: }
        !          1238: 
        !          1239: static int
        !          1240: window_copy_search_down(struct window_pane *wp, int moveflag)
        !          1241: {
        !          1242:        return (window_copy_search(wp, 1, moveflag));
        !          1243: }
        !          1244: 
        !          1245: static void
        !          1246: window_copy_goto_line(struct window_pane *wp, const char *linestr)
        !          1247: {
        !          1248:        struct window_copy_mode_data    *data = wp->modedata;
        !          1249:        const char                      *errstr;
        !          1250:        u_int                            lineno;
        !          1251: 
        !          1252:        lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
        !          1253:        if (errstr != NULL)
        !          1254:                return;
        !          1255: 
        !          1256:        data->oy = lineno;
        !          1257:        window_copy_update_selection(wp, 1);
        !          1258:        window_copy_redraw_screen(wp);
        !          1259: }
        !          1260: 
        !          1261: static void
        !          1262: window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
        !          1263:     u_int py)
        !          1264: {
        !          1265:        struct window_copy_mode_data    *data = wp->modedata;
        !          1266:        struct screen                   *s = &data->screen;
        !          1267:        struct options                  *oo = wp->window->options;
        !          1268:        struct grid_cell                 gc;
        !          1269:        char                             hdr[512];
        !          1270:        size_t                           size = 0;
        !          1271: 
        !          1272:        style_apply(&gc, oo, "mode-style");
        !          1273:        gc.flags |= GRID_FLAG_NOPALETTE;
        !          1274: 
        !          1275:        if (py == 0) {
        !          1276:                if (data->searchmark == NULL) {
        !          1277:                        size = xsnprintf(hdr, sizeof hdr,
        !          1278:                            "[%u/%u]", data->oy, screen_hsize(data->backing));
        !          1279:                } else {
        !          1280:                        if (data->searchthis == -1) {
        !          1281:                                size = xsnprintf(hdr, sizeof hdr,
        !          1282:                                    "(%u results) [%d/%u]", data->searchcount,
        !          1283:                                    data->oy, screen_hsize(data->backing));
        !          1284:                        } else {
        !          1285:                                size = xsnprintf(hdr, sizeof hdr,
        !          1286:                                    "(%u/%u results) [%d/%u]", data->searchthis,
        !          1287:                                    data->searchcount, data->oy,
        !          1288:                                    screen_hsize(data->backing));
        !          1289:                        }
        !          1290:                }
        !          1291:                if (size > screen_size_x(s))
        !          1292:                        size = screen_size_x(s);
        !          1293:                screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
        !          1294:                screen_write_puts(ctx, &gc, "%s", hdr);
        !          1295:        } else
        !          1296:                size = 0;
        !          1297: 
        !          1298:        if (size < screen_size_x(s)) {
        !          1299:                screen_write_cursormove(ctx, 0, py);
        !          1300:                screen_write_copy(ctx, data->backing, 0,
        !          1301:                    (screen_hsize(data->backing) - data->oy) + py,
        !          1302:                    screen_size_x(s) - size, 1, data->searchmark, &gc);
        !          1303:        }
        !          1304: 
        !          1305:        if (py == data->cy && data->cx == screen_size_x(s)) {
        !          1306:                memcpy(&gc, &grid_default_cell, sizeof gc);
        !          1307:                screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
        !          1308:                screen_write_putc(ctx, &gc, '$');
        !          1309:        }
        !          1310: }
        !          1311: 
        !          1312: static void
        !          1313: window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx,
        !          1314:     u_int py, u_int ny)
        !          1315: {
        !          1316:        u_int   yy;
        !          1317: 
        !          1318:        for (yy = py; yy < py + ny; yy++)
        !          1319:                window_copy_write_line(wp, ctx, py);
        !          1320: }
        !          1321: 
        !          1322: static void
        !          1323: window_copy_redraw_selection(struct window_pane *wp, u_int old_y)
        !          1324: {
        !          1325:        struct window_copy_mode_data    *data = wp->modedata;
        !          1326:        u_int                            new_y, start, end;
        !          1327: 
        !          1328:        new_y = data->cy;
        !          1329:        if (old_y <= new_y) {
        !          1330:                start = old_y;
        !          1331:                end = new_y;
        !          1332:        } else {
        !          1333:                start = new_y;
        !          1334:                end = old_y;
        !          1335:        }
        !          1336:        window_copy_redraw_lines(wp, start, end - start + 1);
        !          1337: }
        !          1338: 
        !          1339: static void
        !          1340: window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
        !          1341: {
        !          1342:        struct window_copy_mode_data    *data = wp->modedata;
        !          1343:        struct screen_write_ctx          ctx;
        !          1344:        u_int                            i;
        !          1345: 
        !          1346:        screen_write_start(&ctx, wp, NULL);
        !          1347:        for (i = py; i < py + ny; i++)
        !          1348:                window_copy_write_line(wp, &ctx, i);
        !          1349:        screen_write_cursormove(&ctx, data->cx, data->cy);
        !          1350:        screen_write_stop(&ctx);
        !          1351: }
        !          1352: 
        !          1353: static void
        !          1354: window_copy_redraw_screen(struct window_pane *wp)
        !          1355: {
        !          1356:        struct window_copy_mode_data    *data = wp->modedata;
        !          1357: 
        !          1358:        window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
        !          1359: }
        !          1360: 
        !          1361: static void
        !          1362: window_copy_synchronize_cursor(struct window_pane *wp)
        !          1363: {
        !          1364:        struct window_copy_mode_data    *data = wp->modedata;
        !          1365:        u_int                            xx, yy;
        !          1366: 
        !          1367:        xx = data->cx;
        !          1368:        yy = screen_hsize(data->backing) + data->cy - data->oy;
        !          1369: 
        !          1370:        switch (data->cursordrag) {
        !          1371:        case CURSORDRAG_ENDSEL:
        !          1372:                data->endselx = xx;
        !          1373:                data->endsely = yy;
        !          1374:                break;
        !          1375:        case CURSORDRAG_SEL:
        !          1376:                data->selx = xx;
        !          1377:                data->sely = yy;
        !          1378:                break;
        !          1379:        case CURSORDRAG_NONE:
        !          1380:                break;
        !          1381:        }
        !          1382: }
        !          1383: 
        !          1384: static void
        !          1385: window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
        !          1386: {
        !          1387:        struct window_copy_mode_data    *data = wp->modedata;
        !          1388:        struct screen                   *s = &data->screen;
        !          1389:        struct screen_write_ctx          ctx;
        !          1390:        u_int                            old_cx, old_cy;
        !          1391: 
        !          1392:        old_cx = data->cx; old_cy = data->cy;
        !          1393:        data->cx = cx; data->cy = cy;
        !          1394:        if (old_cx == screen_size_x(s))
        !          1395:                window_copy_redraw_lines(wp, old_cy, 1);
        !          1396:        if (data->cx == screen_size_x(s))
        !          1397:                window_copy_redraw_lines(wp, data->cy, 1);
        !          1398:        else {
        !          1399:                screen_write_start(&ctx, wp, NULL);
        !          1400:                screen_write_cursormove(&ctx, data->cx, data->cy);
        !          1401:                screen_write_stop(&ctx);
        !          1402:        }
        !          1403: }
        !          1404: 
        !          1405: static void
        !          1406: window_copy_start_selection(struct window_pane *wp)
        !          1407: {
        !          1408:        struct window_copy_mode_data    *data = wp->modedata;
        !          1409:        struct screen                   *s = &data->screen;
        !          1410: 
        !          1411:        data->selx = data->cx;
        !          1412:        data->sely = screen_hsize(data->backing) + data->cy - data->oy;
        !          1413: 
        !          1414:        data->endselx = data->selx;
        !          1415:        data->endsely = data->sely;
        !          1416: 
        !          1417:        data->cursordrag = CURSORDRAG_ENDSEL;
        !          1418: 
        !          1419:        s->sel.flag = 1;
        !          1420:        window_copy_update_selection(wp, 1);
        !          1421: }
        !          1422: 
        !          1423: static int
        !          1424: window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
        !          1425: {
        !          1426:        struct window_copy_mode_data    *data = wp->modedata;
        !          1427:        struct screen                   *s = &data->screen;
        !          1428:        u_int                            sx, sy, ty;
        !          1429:        int                              relpos;
        !          1430: 
        !          1431:        sx = *selx;
        !          1432:        sy = *sely;
        !          1433: 
        !          1434:        ty = screen_hsize(data->backing) - data->oy;
        !          1435:        if (sy < ty) {
        !          1436:                relpos = WINDOW_COPY_REL_POS_ABOVE;
        !          1437:                if (!data->rectflag)
        !          1438:                        sx = 0;
        !          1439:                sy = 0;
        !          1440:        } else if (sy > ty + screen_size_y(s) - 1) {
        !          1441:                relpos = WINDOW_COPY_REL_POS_BELOW;
        !          1442:                if (!data->rectflag)
        !          1443:                        sx = screen_size_x(s) - 1;
        !          1444:                sy = screen_size_y(s) - 1;
        !          1445:        } else {
        !          1446:                relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
        !          1447:                sy -= ty;
        !          1448:        }
        !          1449: 
        !          1450:        *selx = sx;
        !          1451:        *sely = screen_hsize(s) + sy;
        !          1452:        return (relpos);
        !          1453: }
        !          1454: 
        !          1455: static int
        !          1456: window_copy_update_selection(struct window_pane *wp, int may_redraw)
        !          1457: {
        !          1458:        struct window_copy_mode_data    *data = wp->modedata;
        !          1459:        struct screen                   *s = &data->screen;
        !          1460:        struct options                  *oo = wp->window->options;
        !          1461:        struct grid_cell                 gc;
        !          1462:        u_int                            sx, sy, cy, endsx, endsy;
        !          1463:        int                              startrelpos, endrelpos;
        !          1464: 
        !          1465:        if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
        !          1466:                return (0);
        !          1467: 
        !          1468:        window_copy_synchronize_cursor(wp);
        !          1469: 
        !          1470:        /* Adjust the selection. */
        !          1471:        sx = data->selx;
        !          1472:        sy = data->sely;
        !          1473:        startrelpos = window_copy_adjust_selection(wp, &sx, &sy);
        !          1474: 
        !          1475:        /* Adjust the end of selection. */
        !          1476:        endsx = data->endselx;
        !          1477:        endsy = data->endsely;
        !          1478:        endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy);
        !          1479: 
        !          1480:        /* Selection is outside of the current screen */
        !          1481:        if (startrelpos == endrelpos &&
        !          1482:            startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
        !          1483:                screen_hide_selection(s);
        !          1484:                return (0);
        !          1485:        }
        !          1486: 
        !          1487:        /* Set colours and selection. */
        !          1488:        style_apply(&gc, oo, "mode-style");
        !          1489:        gc.flags |= GRID_FLAG_NOPALETTE;
        !          1490:        screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, &gc);
        !          1491: 
        !          1492:        if (data->rectflag && may_redraw) {
        !          1493:                /*
        !          1494:                 * Can't rely on the caller to redraw the right lines for
        !          1495:                 * rectangle selection - find the highest line and the number
        !          1496:                 * of lines, and redraw just past that in both directions
        !          1497:                 */
        !          1498:                cy = data->cy;
        !          1499:                if (sy < cy)
        !          1500:                        window_copy_redraw_lines(wp, sy, cy - sy + 1);
        !          1501:                else
        !          1502:                        window_copy_redraw_lines(wp, cy, sy - cy + 1);
        !          1503:        }
        !          1504: 
        !          1505:        return (1);
        !          1506: }
        !          1507: 
        !          1508: static void *
        !          1509: window_copy_get_selection(struct window_pane *wp, size_t *len)
        !          1510: {
        !          1511:        struct window_copy_mode_data    *data = wp->modedata;
        !          1512:        struct screen                   *s = &data->screen;
        !          1513:        char                            *buf;
        !          1514:        size_t                           off;
        !          1515:        u_int                            i, xx, yy, sx, sy, ex, ey, ey_last;
        !          1516:        u_int                            firstsx, lastex, restex, restsx;
        !          1517:        int                              keys;
        !          1518: 
        !          1519:        if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
        !          1520:                return (NULL);
        !          1521: 
        !          1522:        buf = xmalloc(1);
        !          1523:        off = 0;
        !          1524: 
        !          1525:        *buf = '\0';
        !          1526: 
        !          1527:        /*
        !          1528:         * The selection extends from selx,sely to (adjusted) cx,cy on
        !          1529:         * the base screen.
        !          1530:         */
        !          1531: 
        !          1532:        /* Find start and end. */
        !          1533:        xx = data->endselx;
        !          1534:        yy = data->endsely;
        !          1535:        if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
        !          1536:                sx = xx; sy = yy;
        !          1537:                ex = data->selx; ey = data->sely;
        !          1538:        } else {
        !          1539:                sx = data->selx; sy = data->sely;
        !          1540:                ex = xx; ey = yy;
        !          1541:        }
        !          1542: 
        !          1543:        /* Trim ex to end of line. */
        !          1544:        ey_last = window_copy_find_length(wp, ey);
        !          1545:        if (ex > ey_last)
        !          1546:                ex = ey_last;
        !          1547: 
        !          1548:        /*
        !          1549:         * Deal with rectangle-copy if necessary; four situations: start of
        !          1550:         * first line (firstsx), end of last line (lastex), start (restsx) and
        !          1551:         * end (restex) of all other lines.
        !          1552:         */
        !          1553:        xx = screen_size_x(s);
        !          1554: 
        !          1555:        /*
        !          1556:         * Behave according to mode-keys. If it is emacs, copy like emacs,
        !          1557:         * keeping the top-left-most character, and dropping the
        !          1558:         * bottom-right-most, regardless of copy direction. If it is vi, also
        !          1559:         * keep bottom-right-most character.
        !          1560:         */
        !          1561:        keys = options_get_number(wp->window->options, "mode-keys");
        !          1562:        if (data->rectflag) {
        !          1563:                /*
        !          1564:                 * Need to ignore the column with the cursor in it, which for
        !          1565:                 * rectangular copy means knowing which side the cursor is on.
        !          1566:                 */
        !          1567:                if (data->selx < data->cx) {
        !          1568:                        /* Selection start is on the left. */
        !          1569:                        if (keys == MODEKEY_EMACS) {
        !          1570:                                lastex = data->cx;
        !          1571:                                restex = data->cx;
        !          1572:                        }
        !          1573:                        else {
        !          1574:                                lastex = data->cx + 1;
        !          1575:                                restex = data->cx + 1;
        !          1576:                        }
        !          1577:                        firstsx = data->selx;
        !          1578:                        restsx = data->selx;
        !          1579:                } else {
        !          1580:                        /* Cursor is on the left. */
        !          1581:                        lastex = data->selx + 1;
        !          1582:                        restex = data->selx + 1;
        !          1583:                        firstsx = data->cx;
        !          1584:                        restsx = data->cx;
        !          1585:                }
        !          1586:        } else {
        !          1587:                if (keys == MODEKEY_EMACS)
        !          1588:                        lastex = ex;
        !          1589:                else
        !          1590:                        lastex = ex + 1;
        !          1591:                restex = xx;
        !          1592:                firstsx = sx;
        !          1593:                restsx = 0;
        !          1594:        }
        !          1595: 
        !          1596:        /* Copy the lines. */
        !          1597:        for (i = sy; i <= ey; i++) {
        !          1598:                window_copy_copy_line(wp, &buf, &off, i,
        !          1599:                    (i == sy ? firstsx : restsx),
        !          1600:                    (i == ey ? lastex : restex));
        !          1601:        }
        !          1602: 
        !          1603:        /* Don't bother if no data. */
        !          1604:        if (off == 0) {
        !          1605:                free(buf);
        !          1606:                return (NULL);
        !          1607:        }
        !          1608:        if (keys == MODEKEY_EMACS || lastex <= ey_last)
        !          1609:                off -= 1; /* remove final \n (unless at end in vi mode) */
        !          1610:        *len = off;
        !          1611:        return (buf);
        !          1612: }
        !          1613: 
        !          1614: static void
        !          1615: window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf,
        !          1616:     size_t len)
        !          1617: {
        !          1618:        struct screen_write_ctx ctx;
        !          1619: 
        !          1620:        if (options_get_number(global_options, "set-clipboard")) {
        !          1621:                screen_write_start(&ctx, wp, NULL);
        !          1622:                screen_write_setselection(&ctx, buf, len);
        !          1623:                screen_write_stop(&ctx);
        !          1624:        }
        !          1625: 
        !          1626:        if (paste_set(buf, len, bufname, NULL) != 0)
        !          1627:                free(buf);
        !          1628: }
        !          1629: 
        !          1630: static void
        !          1631: window_copy_copy_pipe(struct window_pane *wp, struct session *s,
        !          1632:     const char *bufname, const char *arg)
        !          1633: {
        !          1634:        void            *buf;
        !          1635:        size_t           len;
        !          1636:        struct job      *job;
        !          1637:        char            *expanded;
        !          1638: 
        !          1639:        buf = window_copy_get_selection(wp, &len);
        !          1640:        if (buf == NULL)
        !          1641:                return;
        !          1642:        expanded = format_single(NULL, arg, NULL, s, NULL, wp);
        !          1643: 
        !          1644:        job = job_run(expanded, s, NULL, NULL, NULL, NULL);
        !          1645:        bufferevent_write(job->event, buf, len);
        !          1646: 
        !          1647:        free(expanded);
        !          1648:        window_copy_copy_buffer(wp, bufname, buf, len);
        !          1649: }
        !          1650: 
        !          1651: static void
        !          1652: window_copy_copy_selection(struct window_pane *wp, const char *bufname)
        !          1653: {
        !          1654:        void    *buf;
        !          1655:        size_t   len;
        !          1656: 
        !          1657:        buf = window_copy_get_selection(wp, &len);
        !          1658:        if (buf == NULL)
        !          1659:                return;
        !          1660: 
        !          1661:        window_copy_copy_buffer(wp, bufname, buf, len);
        !          1662: }
        !          1663: 
        !          1664: static void
        !          1665: window_copy_append_selection(struct window_pane *wp, const char *bufname)
        !          1666: {
        !          1667:        char                            *buf;
        !          1668:        struct paste_buffer             *pb;
        !          1669:        const char                      *bufdata;
        !          1670:        size_t                           len, bufsize;
        !          1671:        struct screen_write_ctx          ctx;
        !          1672: 
        !          1673:        buf = window_copy_get_selection(wp, &len);
        !          1674:        if (buf == NULL)
        !          1675:                return;
        !          1676: 
        !          1677:        if (options_get_number(global_options, "set-clipboard")) {
        !          1678:                screen_write_start(&ctx, wp, NULL);
        !          1679:                screen_write_setselection(&ctx, buf, len);
        !          1680:                screen_write_stop(&ctx);
        !          1681:        }
        !          1682: 
        !          1683:        if (bufname == NULL || *bufname == '\0')
        !          1684:                pb = paste_get_top(&bufname);
        !          1685:        else
        !          1686:                pb = paste_get_name(bufname);
        !          1687:        if (pb != NULL) {
        !          1688:                bufdata = paste_buffer_data(pb, &bufsize);
        !          1689:                buf = xrealloc(buf, len + bufsize);
        !          1690:                memmove(buf + bufsize, buf, len);
        !          1691:                memcpy(buf, bufdata, bufsize);
        !          1692:                len += bufsize;
        !          1693:        }
        !          1694:        if (paste_set(buf, len, bufname, NULL) != 0)
        !          1695:                free(buf);
        !          1696: }
        !          1697: 
        !          1698: static void
        !          1699: window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
        !          1700:     u_int sx, u_int ex)
        !          1701: {
        !          1702:        struct window_copy_mode_data    *data = wp->modedata;
        !          1703:        struct grid                     *gd = data->backing->grid;
        !          1704:        struct grid_cell                 gc;
        !          1705:        struct grid_line                *gl;
        !          1706:        struct utf8_data                 ud;
        !          1707:        u_int                            i, xx, wrapped = 0;
        !          1708:        const char                      *s;
        !          1709: 
        !          1710:        if (sx > ex)
        !          1711:                return;
        !          1712: 
        !          1713:        /*
        !          1714:         * Work out if the line was wrapped at the screen edge and all of it is
        !          1715:         * on screen.
        !          1716:         */
        !          1717:        gl = &gd->linedata[sy];
        !          1718:        if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
        !          1719:                wrapped = 1;
        !          1720: 
        !          1721:        /* If the line was wrapped, don't strip spaces (use the full length). */
        !          1722:        if (wrapped)
        !          1723:                xx = gl->cellsize;
        !          1724:        else
        !          1725:                xx = window_copy_find_length(wp, sy);
        !          1726:        if (ex > xx)
        !          1727:                ex = xx;
        !          1728:        if (sx > xx)
        !          1729:                sx = xx;
        !          1730: 
        !          1731:        if (sx < ex) {
        !          1732:                for (i = sx; i < ex; i++) {
        !          1733:                        grid_get_cell(gd, i, sy, &gc);
        !          1734:                        if (gc.flags & GRID_FLAG_PADDING)
        !          1735:                                continue;
        !          1736:                        utf8_copy(&ud, &gc.data);
        !          1737:                        if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
        !          1738:                                s = tty_acs_get(NULL, ud.data[0]);
        !          1739:                                if (s != NULL && strlen(s) <= sizeof ud.data) {
        !          1740:                                        ud.size = strlen(s);
        !          1741:                                        memcpy(ud.data, s, ud.size);
        !          1742:                                }
        !          1743:                        }
        !          1744: 
        !          1745:                        *buf = xrealloc(*buf, (*off) + ud.size);
        !          1746:                        memcpy(*buf + *off, ud.data, ud.size);
        !          1747:                        *off += ud.size;
        !          1748:                }
        !          1749:        }
        !          1750: 
        !          1751:        /* Only add a newline if the line wasn't wrapped. */
        !          1752:        if (!wrapped || ex != xx) {
        !          1753:                *buf = xrealloc(*buf, (*off) + 1);
        !          1754:                (*buf)[(*off)++] = '\n';
        !          1755:        }
        !          1756: }
        !          1757: 
        !          1758: static void
        !          1759: window_copy_clear_selection(struct window_pane *wp)
        !          1760: {
        !          1761:        struct window_copy_mode_data   *data = wp->modedata;
        !          1762:        u_int                           px, py;
        !          1763: 
        !          1764:        screen_clear_selection(&data->screen);
        !          1765: 
        !          1766:        data->cursordrag = CURSORDRAG_NONE;
        !          1767: 
        !          1768:        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          1769:        px = window_copy_find_length(wp, py);
        !          1770:        if (data->cx > px)
        !          1771:                window_copy_update_cursor(wp, px, data->cy);
        !          1772: }
        !          1773: 
        !          1774: static int
        !          1775: window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
        !          1776: {
        !          1777:        struct window_copy_mode_data    *data = wp->modedata;
        !          1778:        struct grid_cell                 gc;
        !          1779:        const struct utf8_data          *ud;
        !          1780: 
        !          1781:        grid_get_cell(data->backing->grid, px, py, &gc);
        !          1782: 
        !          1783:        ud = &gc.data;
        !          1784:        if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING))
        !          1785:                return (0);
        !          1786:        if (*ud->data == 0x00 || *ud->data == 0x7f)
        !          1787:                return (0);
        !          1788:        return (strchr(set, *ud->data) != NULL);
        !          1789: }
        !          1790: 
        !          1791: static u_int
        !          1792: window_copy_find_length(struct window_pane *wp, u_int py)
        !          1793: {
        !          1794:        struct window_copy_mode_data    *data = wp->modedata;
        !          1795:        struct screen                   *s = data->backing;
        !          1796:        struct grid_cell                 gc;
        !          1797:        u_int                            px;
        !          1798: 
        !          1799:        /*
        !          1800:         * If the pane has been resized, its grid can contain old overlong
        !          1801:         * lines. grid_peek_cell does not allow accessing cells beyond the
        !          1802:         * width of the grid, and screen_write_copy treats them as spaces, so
        !          1803:         * ignore them here too.
        !          1804:         */
        !          1805:        px = s->grid->linedata[py].cellsize;
        !          1806:        if (px > screen_size_x(s))
        !          1807:                px = screen_size_x(s);
        !          1808:        while (px > 0) {
        !          1809:                grid_get_cell(s->grid, px - 1, py, &gc);
        !          1810:                if (gc.data.size != 1 || *gc.data.data != ' ')
        !          1811:                        break;
        !          1812:                px--;
        !          1813:        }
        !          1814:        return (px);
        !          1815: }
        !          1816: 
        !          1817: static void
        !          1818: window_copy_cursor_start_of_line(struct window_pane *wp)
        !          1819: {
        !          1820:        struct window_copy_mode_data    *data = wp->modedata;
        !          1821:        struct screen                   *back_s = data->backing;
        !          1822:        struct screen                   *s = &data->screen;
        !          1823:        struct grid                     *gd = back_s->grid;
        !          1824:        u_int                            py;
        !          1825: 
        !          1826:        if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) {
        !          1827:                py = screen_hsize(back_s) + data->cy - data->oy;
        !          1828:                while (py > 0 &&
        !          1829:                    gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
        !          1830:                        window_copy_cursor_up(wp, 0);
        !          1831:                        py = screen_hsize(back_s) + data->cy - data->oy;
        !          1832:                }
        !          1833:        }
        !          1834:        window_copy_update_cursor(wp, 0, data->cy);
        !          1835:        if (window_copy_update_selection(wp, 1))
        !          1836:                window_copy_redraw_lines(wp, data->cy, 1);
        !          1837: }
        !          1838: 
        !          1839: static void
        !          1840: window_copy_cursor_back_to_indentation(struct window_pane *wp)
        !          1841: {
        !          1842:        struct window_copy_mode_data    *data = wp->modedata;
        !          1843:        u_int                            px, py, xx;
        !          1844:        struct grid_cell                 gc;
        !          1845: 
        !          1846:        px = 0;
        !          1847:        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          1848:        xx = window_copy_find_length(wp, py);
        !          1849: 
        !          1850:        while (px < xx) {
        !          1851:                grid_get_cell(data->backing->grid, px, py, &gc);
        !          1852:                if (gc.data.size != 1 || *gc.data.data != ' ')
        !          1853:                        break;
        !          1854:                px++;
        !          1855:        }
        !          1856: 
        !          1857:        window_copy_update_cursor(wp, px, data->cy);
        !          1858:        if (window_copy_update_selection(wp, 1))
        !          1859:                window_copy_redraw_lines(wp, data->cy, 1);
        !          1860: }
        !          1861: 
        !          1862: static void
        !          1863: window_copy_cursor_end_of_line(struct window_pane *wp)
        !          1864: {
        !          1865:        struct window_copy_mode_data    *data = wp->modedata;
        !          1866:        struct screen                   *back_s = data->backing;
        !          1867:        struct screen                   *s = &data->screen;
        !          1868:        struct grid                     *gd = back_s->grid;
        !          1869:        u_int                            px, py;
        !          1870: 
        !          1871:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          1872:        px = window_copy_find_length(wp, py);
        !          1873: 
        !          1874:        if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) {
        !          1875:                if (data->screen.sel.flag && data->rectflag)
        !          1876:                        px = screen_size_x(back_s);
        !          1877:                if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
        !          1878:                        while (py < gd->sy + gd->hsize &&
        !          1879:                            gd->linedata[py].flags & GRID_LINE_WRAPPED) {
        !          1880:                                window_copy_cursor_down(wp, 0);
        !          1881:                                py = screen_hsize(back_s)
        !          1882:                                     + data->cy - data->oy;
        !          1883:                        }
        !          1884:                        px = window_copy_find_length(wp, py);
        !          1885:                }
        !          1886:        }
        !          1887:        window_copy_update_cursor(wp, px, data->cy);
        !          1888: 
        !          1889:        if (window_copy_update_selection(wp, 1))
        !          1890:                window_copy_redraw_lines(wp, data->cy, 1);
        !          1891: }
        !          1892: 
        !          1893: static void
        !          1894: window_copy_other_end(struct window_pane *wp)
        !          1895: {
        !          1896:        struct window_copy_mode_data    *data = wp->modedata;
        !          1897:        struct screen                   *s = &data->screen;
        !          1898:        u_int                            selx, sely, cy, yy, hsize;
        !          1899: 
        !          1900:        if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
        !          1901:                return;
        !          1902: 
        !          1903:        if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
        !          1904:                s->sel.lineflag = LINE_SEL_RIGHT_LEFT;
        !          1905:        else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
        !          1906:                s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
        !          1907: 
        !          1908:        switch (data->cursordrag) {
        !          1909:                case CURSORDRAG_NONE:
        !          1910:                case CURSORDRAG_SEL:
        !          1911:                        data->cursordrag = CURSORDRAG_ENDSEL;
        !          1912:                        break;
        !          1913:                case CURSORDRAG_ENDSEL:
        !          1914:                        data->cursordrag = CURSORDRAG_SEL;
        !          1915:                        break;
        !          1916:        }
        !          1917: 
        !          1918:        selx = data->endselx;
        !          1919:        sely = data->endsely;
        !          1920:        if (data->cursordrag == CURSORDRAG_SEL) {
        !          1921:                selx = data->selx;
        !          1922:                sely = data->sely;
        !          1923:        }
        !          1924: 
        !          1925:        cy = data->cy;
        !          1926:        yy = screen_hsize(data->backing) + data->cy - data->oy;
        !          1927: 
        !          1928:        data->cx = selx;
        !          1929: 
        !          1930:        hsize = screen_hsize(data->backing);
        !          1931:        if (sely < hsize - data->oy) { /* above */
        !          1932:                data->oy = hsize - sely;
        !          1933:                data->cy = 0;
        !          1934:        } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
        !          1935:                data->oy = hsize - sely + screen_size_y(s) - 1;
        !          1936:                data->cy = screen_size_y(s) - 1;
        !          1937:        } else
        !          1938:                data->cy = cy + sely - yy;
        !          1939: 
        !          1940:        window_copy_update_selection(wp, 1);
        !          1941:        window_copy_redraw_screen(wp);
        !          1942: }
        !          1943: 
        !          1944: static void
        !          1945: window_copy_cursor_left(struct window_pane *wp)
        !          1946: {
        !          1947:        struct window_copy_mode_data    *data = wp->modedata;
        !          1948:        u_int                            py, cx;
        !          1949:        struct grid_cell                 gc;
        !          1950: 
        !          1951:        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          1952:        cx = data->cx;
        !          1953:        while (cx > 0) {
        !          1954:                grid_get_cell(data->backing->grid, cx, py, &gc);
        !          1955:                if (~gc.flags & GRID_FLAG_PADDING)
        !          1956:                        break;
        !          1957:                cx--;
        !          1958:        }
        !          1959:        if (cx == 0 && py > 0) {
        !          1960:                window_copy_cursor_up(wp, 0);
        !          1961:                window_copy_cursor_end_of_line(wp);
        !          1962:        } else if (cx > 0) {
        !          1963:                window_copy_update_cursor(wp, cx - 1, data->cy);
        !          1964:                if (window_copy_update_selection(wp, 1))
        !          1965:                        window_copy_redraw_lines(wp, data->cy, 1);
        !          1966:        }
        !          1967: }
        !          1968: 
        !          1969: static void
        !          1970: window_copy_cursor_right(struct window_pane *wp)
        !          1971: {
        !          1972:        struct window_copy_mode_data    *data = wp->modedata;
        !          1973:        u_int                            px, py, yy, cx, cy;
        !          1974:        struct grid_cell                 gc;
        !          1975: 
        !          1976:        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          1977:        yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1;
        !          1978:        if (data->screen.sel.flag && data->rectflag)
        !          1979:                px = screen_size_x(&data->screen);
        !          1980:        else
        !          1981:                px = window_copy_find_length(wp, py);
        !          1982: 
        !          1983:        if (data->cx >= px && py < yy) {
        !          1984:                window_copy_cursor_start_of_line(wp);
        !          1985:                window_copy_cursor_down(wp, 0);
        !          1986:        } else if (data->cx < px) {
        !          1987:                cx = data->cx + 1;
        !          1988:                cy = screen_hsize(data->backing) + data->cy - data->oy;
        !          1989:                while (cx < px) {
        !          1990:                        grid_get_cell(data->backing->grid, cx, cy, &gc);
        !          1991:                        if (~gc.flags & GRID_FLAG_PADDING)
        !          1992:                                break;
        !          1993:                        cx++;
        !          1994:                }
        !          1995:                window_copy_update_cursor(wp, cx, data->cy);
        !          1996:                if (window_copy_update_selection(wp, 1))
        !          1997:                        window_copy_redraw_lines(wp, data->cy, 1);
        !          1998:        }
        !          1999: }
        !          2000: 
        !          2001: static void
        !          2002: window_copy_cursor_up(struct window_pane *wp, int scroll_only)
        !          2003: {
        !          2004:        struct window_copy_mode_data    *data = wp->modedata;
        !          2005:        struct screen                   *s = &data->screen;
        !          2006:        u_int                            ox, oy, px, py;
        !          2007: 
        !          2008:        oy = screen_hsize(data->backing) + data->cy - data->oy;
        !          2009:        ox = window_copy_find_length(wp, oy);
        !          2010:        if (data->cx != ox) {
        !          2011:                data->lastcx = data->cx;
        !          2012:                data->lastsx = ox;
        !          2013:        }
        !          2014: 
        !          2015:        if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
        !          2016:                window_copy_other_end(wp);
        !          2017: 
        !          2018:        data->cx = data->lastcx;
        !          2019:        if (scroll_only || data->cy == 0) {
        !          2020:                window_copy_scroll_down(wp, 1);
        !          2021:                if (scroll_only) {
        !          2022:                        if (data->cy == screen_size_y(s) - 1)
        !          2023:                                window_copy_redraw_lines(wp, data->cy, 1);
        !          2024:                        else
        !          2025:                                window_copy_redraw_lines(wp, data->cy, 2);
        !          2026:                }
        !          2027:        } else {
        !          2028:                window_copy_update_cursor(wp, data->cx, data->cy - 1);
        !          2029:                if (window_copy_update_selection(wp, 1)) {
        !          2030:                        if (data->cy == screen_size_y(s) - 1)
        !          2031:                                window_copy_redraw_lines(wp, data->cy, 1);
        !          2032:                        else
        !          2033:                                window_copy_redraw_lines(wp, data->cy, 2);
        !          2034:                }
        !          2035:        }
        !          2036: 
        !          2037:        if (!data->screen.sel.flag || !data->rectflag) {
        !          2038:                py = screen_hsize(data->backing) + data->cy - data->oy;
        !          2039:                px = window_copy_find_length(wp, py);
        !          2040:                if ((data->cx >= data->lastsx && data->cx != px) ||
        !          2041:                    data->cx > px)
        !          2042:                        window_copy_cursor_end_of_line(wp);
        !          2043:        }
        !          2044: 
        !          2045:        if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
        !          2046:                window_copy_cursor_end_of_line(wp);
        !          2047:        else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
        !          2048:                window_copy_cursor_start_of_line(wp);
        !          2049: }
        !          2050: 
        !          2051: static void
        !          2052: window_copy_cursor_down(struct window_pane *wp, int scroll_only)
        !          2053: {
        !          2054:        struct window_copy_mode_data    *data = wp->modedata;
        !          2055:        struct screen                   *s = &data->screen;
        !          2056:        u_int                            ox, oy, px, py;
        !          2057: 
        !          2058:        oy = screen_hsize(data->backing) + data->cy - data->oy;
        !          2059:        ox = window_copy_find_length(wp, oy);
        !          2060:        if (data->cx != ox) {
        !          2061:                data->lastcx = data->cx;
        !          2062:                data->lastsx = ox;
        !          2063:        }
        !          2064: 
        !          2065:        if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
        !          2066:                window_copy_other_end(wp);
        !          2067: 
        !          2068:        data->cx = data->lastcx;
        !          2069:        if (scroll_only || data->cy == screen_size_y(s) - 1) {
        !          2070:                window_copy_scroll_up(wp, 1);
        !          2071:                if (scroll_only && data->cy > 0)
        !          2072:                        window_copy_redraw_lines(wp, data->cy - 1, 2);
        !          2073:        } else {
        !          2074:                window_copy_update_cursor(wp, data->cx, data->cy + 1);
        !          2075:                if (window_copy_update_selection(wp, 1))
        !          2076:                        window_copy_redraw_lines(wp, data->cy - 1, 2);
        !          2077:        }
        !          2078: 
        !          2079:        if (!data->screen.sel.flag || !data->rectflag) {
        !          2080:                py = screen_hsize(data->backing) + data->cy - data->oy;
        !          2081:                px = window_copy_find_length(wp, py);
        !          2082:                if ((data->cx >= data->lastsx && data->cx != px) ||
        !          2083:                    data->cx > px)
        !          2084:                        window_copy_cursor_end_of_line(wp);
        !          2085:        }
        !          2086: 
        !          2087:        if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
        !          2088:                window_copy_cursor_end_of_line(wp);
        !          2089:        else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
        !          2090:                window_copy_cursor_start_of_line(wp);
        !          2091: }
        !          2092: 
        !          2093: static void
        !          2094: window_copy_cursor_jump(struct window_pane *wp)
        !          2095: {
        !          2096:        struct window_copy_mode_data    *data = wp->modedata;
        !          2097:        struct screen                   *back_s = data->backing;
        !          2098:        struct grid_cell                 gc;
        !          2099:        u_int                            px, py, xx;
        !          2100: 
        !          2101:        px = data->cx + 1;
        !          2102:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          2103:        xx = window_copy_find_length(wp, py);
        !          2104: 
        !          2105:        while (px < xx) {
        !          2106:                grid_get_cell(back_s->grid, px, py, &gc);
        !          2107:                if (!(gc.flags & GRID_FLAG_PADDING) &&
        !          2108:                    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
        !          2109:                        window_copy_update_cursor(wp, px, data->cy);
        !          2110:                        if (window_copy_update_selection(wp, 1))
        !          2111:                                window_copy_redraw_lines(wp, data->cy, 1);
        !          2112:                        return;
        !          2113:                }
        !          2114:                px++;
        !          2115:        }
        !          2116: }
        !          2117: 
        !          2118: static void
        !          2119: window_copy_cursor_jump_back(struct window_pane *wp)
        !          2120: {
        !          2121:        struct window_copy_mode_data    *data = wp->modedata;
        !          2122:        struct screen                   *back_s = data->backing;
        !          2123:        struct grid_cell                 gc;
        !          2124:        u_int                            px, py;
        !          2125: 
        !          2126:        px = data->cx;
        !          2127:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          2128: 
        !          2129:        if (px > 0)
        !          2130:                px--;
        !          2131: 
        !          2132:        for (;;) {
        !          2133:                grid_get_cell(back_s->grid, px, py, &gc);
        !          2134:                if (!(gc.flags & GRID_FLAG_PADDING) &&
        !          2135:                    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
        !          2136:                        window_copy_update_cursor(wp, px, data->cy);
        !          2137:                        if (window_copy_update_selection(wp, 1))
        !          2138:                                window_copy_redraw_lines(wp, data->cy, 1);
        !          2139:                        return;
        !          2140:                }
        !          2141:                if (px == 0)
        !          2142:                        break;
        !          2143:                px--;
        !          2144:        }
        !          2145: }
        !          2146: 
        !          2147: static void
        !          2148: window_copy_cursor_jump_to(struct window_pane *wp, int jump_again)
        !          2149: {
        !          2150:        struct window_copy_mode_data    *data = wp->modedata;
        !          2151:        struct screen                   *back_s = data->backing;
        !          2152:        struct grid_cell                 gc;
        !          2153:        u_int                            px, py, xx;
        !          2154: 
        !          2155:        px = data->cx + 1 + jump_again;
        !          2156:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          2157:        xx = window_copy_find_length(wp, py);
        !          2158: 
        !          2159:        while (px < xx) {
        !          2160:                grid_get_cell(back_s->grid, px, py, &gc);
        !          2161:                if (!(gc.flags & GRID_FLAG_PADDING) &&
        !          2162:                    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
        !          2163:                        window_copy_update_cursor(wp, px - 1, data->cy);
        !          2164:                        if (window_copy_update_selection(wp, 1))
        !          2165:                                window_copy_redraw_lines(wp, data->cy, 1);
        !          2166:                        return;
        !          2167:                }
        !          2168:                px++;
        !          2169:        }
        !          2170: }
        !          2171: 
        !          2172: static void
        !          2173: window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again)
        !          2174: {
        !          2175:        struct window_copy_mode_data    *data = wp->modedata;
        !          2176:        struct screen                   *back_s = data->backing;
        !          2177:        struct grid_cell                 gc;
        !          2178:        u_int                            px, py;
        !          2179: 
        !          2180:        px = data->cx;
        !          2181:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          2182: 
        !          2183:        if (px > 0)
        !          2184:                px--;
        !          2185: 
        !          2186:        if (jump_again && px > 0)
        !          2187:                px--;
        !          2188: 
        !          2189:        for (;;) {
        !          2190:                grid_get_cell(back_s->grid, px, py, &gc);
        !          2191:                if (!(gc.flags & GRID_FLAG_PADDING) &&
        !          2192:                    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
        !          2193:                        window_copy_update_cursor(wp, px + 1, data->cy);
        !          2194:                        if (window_copy_update_selection(wp, 1))
        !          2195:                                window_copy_redraw_lines(wp, data->cy, 1);
        !          2196:                        return;
        !          2197:                }
        !          2198:                if (px == 0)
        !          2199:                        break;
        !          2200:                px--;
        !          2201:        }
        !          2202: }
        !          2203: 
        !          2204: static void
        !          2205: window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
        !          2206: {
        !          2207:        struct window_copy_mode_data    *data = wp->modedata;
        !          2208:        struct screen                   *back_s = data->backing;
        !          2209:        u_int                            px, py, xx, yy;
        !          2210:        int                              expected = 0;
        !          2211: 
        !          2212:        px = data->cx;
        !          2213:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          2214:        xx = window_copy_find_length(wp, py);
        !          2215:        yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
        !          2216: 
        !          2217:        /*
        !          2218:         * First skip past any nonword characters and then any word characters.
        !          2219:         *
        !          2220:         * expected is initially set to 0 for the former and then 1 for the
        !          2221:         * latter.
        !          2222:         */
        !          2223:        do {
        !          2224:                while (px > xx ||
        !          2225:                    window_copy_in_set(wp, px, py, separators) == expected) {
        !          2226:                        /* Move down if we're past the end of the line. */
        !          2227:                        if (px > xx) {
        !          2228:                                if (py == yy)
        !          2229:                                        return;
        !          2230:                                window_copy_cursor_down(wp, 0);
        !          2231:                                px = 0;
        !          2232: 
        !          2233:                                py = screen_hsize(back_s) + data->cy - data->oy;
        !          2234:                                xx = window_copy_find_length(wp, py);
        !          2235:                        } else
        !          2236:                                px++;
        !          2237:                }
        !          2238:                expected = !expected;
        !          2239:        } while (expected == 1);
        !          2240: 
        !          2241:        window_copy_update_cursor(wp, px, data->cy);
        !          2242:        if (window_copy_update_selection(wp, 1))
        !          2243:                window_copy_redraw_lines(wp, data->cy, 1);
        !          2244: }
        !          2245: 
        !          2246: static void
        !          2247: window_copy_cursor_next_word_end(struct window_pane *wp,
        !          2248:     const char *separators)
        !          2249: {
        !          2250:        struct window_copy_mode_data    *data = wp->modedata;
        !          2251:        struct options                  *oo = wp->window->options;
        !          2252:        struct screen                   *back_s = data->backing;
        !          2253:        u_int                            px, py, xx, yy;
        !          2254:        int                              keys, expected = 1;
        !          2255: 
        !          2256:        px = data->cx;
        !          2257:        py = screen_hsize(back_s) + data->cy - data->oy;
        !          2258:        xx = window_copy_find_length(wp, py);
        !          2259:        yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
        !          2260: 
        !          2261:        keys = options_get_number(oo, "mode-keys");
        !          2262:        if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators))
        !          2263:                px++;
        !          2264: 
        !          2265:        /*
        !          2266:         * First skip past any word characters, then any nonword characters.
        !          2267:         *
        !          2268:         * expected is initially set to 1 for the former and then 0 for the
        !          2269:         * latter.
        !          2270:         */
        !          2271:        do {
        !          2272:                while (px > xx ||
        !          2273:                    window_copy_in_set(wp, px, py, separators) == expected) {
        !          2274:                        /* Move down if we're past the end of the line. */
        !          2275:                        if (px > xx) {
        !          2276:                                if (py == yy)
        !          2277:                                        return;
        !          2278:                                window_copy_cursor_down(wp, 0);
        !          2279:                                px = 0;
        !          2280: 
        !          2281:                                py = screen_hsize(back_s) + data->cy - data->oy;
        !          2282:                                xx = window_copy_find_length(wp, py);
        !          2283:                        } else
        !          2284:                                px++;
        !          2285:                }
        !          2286:                expected = !expected;
        !          2287:        } while (expected == 0);
        !          2288: 
        !          2289:        if (keys == MODEKEY_VI && px != 0)
        !          2290:                px--;
        !          2291: 
        !          2292:        window_copy_update_cursor(wp, px, data->cy);
        !          2293:        if (window_copy_update_selection(wp, 1))
        !          2294:                window_copy_redraw_lines(wp, data->cy, 1);
        !          2295: }
        !          2296: 
        !          2297: /* Move to the previous place where a word begins. */
        !          2298: static void
        !          2299: window_copy_cursor_previous_word(struct window_pane *wp,
        !          2300:     const char *separators)
        !          2301: {
        !          2302:        struct window_copy_mode_data    *data = wp->modedata;
        !          2303:        u_int                            px, py;
        !          2304: 
        !          2305:        px = data->cx;
        !          2306:        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          2307: 
        !          2308:        /* Move back to the previous word character. */
        !          2309:        for (;;) {
        !          2310:                if (px > 0) {
        !          2311:                        px--;
        !          2312:                        if (!window_copy_in_set(wp, px, py, separators))
        !          2313:                                break;
        !          2314:                } else {
        !          2315:                        if (data->cy == 0 &&
        !          2316:                            (screen_hsize(data->backing) == 0 ||
        !          2317:                            data->oy >= screen_hsize(data->backing) - 1))
        !          2318:                                goto out;
        !          2319:                        window_copy_cursor_up(wp, 0);
        !          2320: 
        !          2321:                        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          2322:                        px = window_copy_find_length(wp, py);
        !          2323:                }
        !          2324:        }
        !          2325: 
        !          2326:        /* Move back to the beginning of this word. */
        !          2327:        while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
        !          2328:                px--;
        !          2329: 
        !          2330: out:
        !          2331:        window_copy_update_cursor(wp, px, data->cy);
        !          2332:        if (window_copy_update_selection(wp, 1))
        !          2333:                window_copy_redraw_lines(wp, data->cy, 1);
        !          2334: }
        !          2335: 
        !          2336: static void
        !          2337: window_copy_scroll_up(struct window_pane *wp, u_int ny)
        !          2338: {
        !          2339:        struct window_copy_mode_data    *data = wp->modedata;
        !          2340:        struct screen                   *s = &data->screen;
        !          2341:        struct screen_write_ctx          ctx;
        !          2342: 
        !          2343:        if (data->oy < ny)
        !          2344:                ny = data->oy;
        !          2345:        if (ny == 0)
        !          2346:                return;
        !          2347:        data->oy -= ny;
        !          2348: 
        !          2349:        window_copy_update_selection(wp, 0);
        !          2350: 
        !          2351:        screen_write_start(&ctx, wp, NULL);
        !          2352:        screen_write_cursormove(&ctx, 0, 0);
        !          2353:        screen_write_deleteline(&ctx, ny, 8);
        !          2354:        window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
        !          2355:        window_copy_write_line(wp, &ctx, 0);
        !          2356:        if (screen_size_y(s) > 1)
        !          2357:                window_copy_write_line(wp, &ctx, 1);
        !          2358:        if (screen_size_y(s) > 3)
        !          2359:                window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
        !          2360:        if (s->sel.flag && screen_size_y(s) > ny)
        !          2361:                window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
        !          2362:        screen_write_cursormove(&ctx, data->cx, data->cy);
        !          2363:        screen_write_stop(&ctx);
        !          2364: }
        !          2365: 
        !          2366: static void
        !          2367: window_copy_scroll_down(struct window_pane *wp, u_int ny)
        !          2368: {
        !          2369:        struct window_copy_mode_data    *data = wp->modedata;
        !          2370:        struct screen                   *s = &data->screen;
        !          2371:        struct screen_write_ctx          ctx;
        !          2372: 
        !          2373:        if (ny > screen_hsize(data->backing))
        !          2374:                return;
        !          2375: 
        !          2376:        if (data->oy > screen_hsize(data->backing) - ny)
        !          2377:                ny = screen_hsize(data->backing) - data->oy;
        !          2378:        if (ny == 0)
        !          2379:                return;
        !          2380:        data->oy += ny;
        !          2381: 
        !          2382:        window_copy_update_selection(wp, 0);
        !          2383: 
        !          2384:        screen_write_start(&ctx, wp, NULL);
        !          2385:        screen_write_cursormove(&ctx, 0, 0);
        !          2386:        screen_write_insertline(&ctx, ny, 8);
        !          2387:        window_copy_write_lines(wp, &ctx, 0, ny);
        !          2388:        if (s->sel.flag && screen_size_y(s) > ny)
        !          2389:                window_copy_write_line(wp, &ctx, ny);
        !          2390:        else if (ny == 1) /* nuke position */
        !          2391:                window_copy_write_line(wp, &ctx, 1);
        !          2392:        screen_write_cursormove(&ctx, data->cx, data->cy);
        !          2393:        screen_write_stop(&ctx);
        !          2394: }
        !          2395: 
        !          2396: int
        !          2397: window_copy_scroll_position(struct window_pane *wp)
        !          2398: {
        !          2399:        struct window_copy_mode_data    *data = wp->modedata;
        !          2400: 
        !          2401:        if (wp->mode != &window_copy_mode)
        !          2402:                return (-1);
        !          2403:        return (data->oy);
        !          2404: }
        !          2405: 
        !          2406: static void
        !          2407: window_copy_rectangle_toggle(struct window_pane *wp)
        !          2408: {
        !          2409:        struct window_copy_mode_data    *data = wp->modedata;
        !          2410:        u_int                            px, py;
        !          2411: 
        !          2412:        data->rectflag = !data->rectflag;
        !          2413: 
        !          2414:        py = screen_hsize(data->backing) + data->cy - data->oy;
        !          2415:        px = window_copy_find_length(wp, py);
        !          2416:        if (data->cx > px)
        !          2417:                window_copy_update_cursor(wp, px, data->cy);
        !          2418: 
        !          2419:        window_copy_update_selection(wp, 1);
        !          2420:        window_copy_redraw_screen(wp);
        !          2421: }
        !          2422: 
        !          2423: static void
        !          2424: window_copy_move_mouse(struct mouse_event *m)
        !          2425: {
        !          2426:        struct window_pane      *wp;
        !          2427:        u_int                    x, y;
        !          2428: 
        !          2429:        wp = cmd_mouse_pane(m, NULL, NULL);
        !          2430:        if (wp == NULL || wp->mode != &window_copy_mode)
        !          2431:                return;
        !          2432: 
        !          2433:        if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
        !          2434:                return;
        !          2435: 
        !          2436:        window_copy_update_cursor(wp, x, y);
        !          2437: }
        !          2438: 
        !          2439: void
        !          2440: window_copy_start_drag(struct client *c, struct mouse_event *m)
        !          2441: {
        !          2442:        struct window_pane      *wp;
        !          2443:        u_int                    x, y;
        !          2444: 
        !          2445:        if (c == NULL)
        !          2446:                return;
        !          2447: 
        !          2448:        wp = cmd_mouse_pane(m, NULL, NULL);
        !          2449:        if (wp == NULL || wp->mode != &window_copy_mode)
        !          2450:                return;
        !          2451: 
        !          2452:        if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
        !          2453:                return;
        !          2454: 
        !          2455:        c->tty.mouse_drag_update = window_copy_drag_update;
        !          2456:        c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */
        !          2457: 
        !          2458:        window_copy_update_cursor(wp, x, y);
        !          2459:        window_copy_start_selection(wp);
        !          2460:        window_copy_redraw_screen(wp);
        !          2461: }
        !          2462: 
        !          2463: static void
        !          2464: window_copy_drag_update(__unused struct client *c, struct mouse_event *m)
        !          2465: {
        !          2466:        struct window_pane              *wp;
        !          2467:        struct window_copy_mode_data    *data;
        !          2468:        u_int                            x, y, old_cy;
        !          2469: 
        !          2470:        wp = cmd_mouse_pane(m, NULL, NULL);
        !          2471:        if (wp == NULL || wp->mode != &window_copy_mode)
        !          2472:                return;
        !          2473:        data = wp->modedata;
        !          2474: 
        !          2475:        if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
        !          2476:                return;
        !          2477:        old_cy = data->cy;
        !          2478: 
        !          2479:        window_copy_update_cursor(wp, x, y);
        !          2480:        if (window_copy_update_selection(wp, 1))
        !          2481:                window_copy_redraw_selection(wp, old_cy);
        !          2482: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>