[BACK]Return to window-copy.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/window-copy.c, Revision 1.8

1.8     ! nicm        1: /* $OpenBSD: window-copy.c,v 1.7 2009/07/23 17:03:47 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
                     15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                     16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/types.h>
                     20:
                     21: #include <string.h>
                     22:
                     23: #include "tmux.h"
                     24:
                     25: struct screen *window_copy_init(struct window_pane *);
                     26: void   window_copy_free(struct window_pane *);
                     27: void   window_copy_resize(struct window_pane *, u_int, u_int);
                     28: void   window_copy_key(struct window_pane *, struct client *, int);
                     29: void   window_copy_mouse(
                     30:            struct window_pane *, struct client *, u_char, u_char, u_char);
                     31:
                     32: void   window_copy_redraw_lines(struct window_pane *, u_int, u_int);
                     33: void   window_copy_redraw_screen(struct window_pane *);
                     34: void   window_copy_write_line(
                     35:            struct window_pane *, struct screen_write_ctx *, u_int);
                     36: void   window_copy_write_lines(
                     37:            struct window_pane *, struct screen_write_ctx *, u_int, u_int);
                     38: void   window_copy_write_column(
                     39:            struct window_pane *, struct screen_write_ctx *, u_int);
                     40: void   window_copy_write_columns(
                     41:            struct window_pane *, struct screen_write_ctx *, u_int, u_int);
                     42:
                     43: void   window_copy_update_cursor(struct window_pane *);
                     44: void   window_copy_start_selection(struct window_pane *);
                     45: int    window_copy_update_selection(struct window_pane *);
                     46: void   window_copy_copy_selection(struct window_pane *, struct client *);
                     47: void   window_copy_copy_line(
                     48:            struct window_pane *, char **, size_t *, u_int, u_int, u_int);
                     49: int    window_copy_is_space(struct window_pane *, u_int, u_int);
                     50: u_int  window_copy_find_length(struct window_pane *, u_int);
1.5       nicm       51: void   window_copy_set_cursor_x(struct window_pane *, u_int);
1.1       nicm       52: void   window_copy_cursor_start_of_line(struct window_pane *);
1.6       nicm       53: void   window_copy_cursor_back_to_indentation(struct window_pane *);
1.1       nicm       54: void   window_copy_cursor_end_of_line(struct window_pane *);
                     55: void   window_copy_cursor_left(struct window_pane *);
                     56: void   window_copy_cursor_right(struct window_pane *);
                     57: void   window_copy_cursor_up(struct window_pane *);
                     58: void   window_copy_cursor_down(struct window_pane *);
                     59: void   window_copy_cursor_next_word(struct window_pane *);
                     60: void   window_copy_cursor_previous_word(struct window_pane *);
                     61: void   window_copy_scroll_left(struct window_pane *, u_int);
                     62: void   window_copy_scroll_right(struct window_pane *, u_int);
                     63: void   window_copy_scroll_up(struct window_pane *, u_int);
                     64: void   window_copy_scroll_down(struct window_pane *, u_int);
                     65:
                     66: const struct window_mode window_copy_mode = {
                     67:        window_copy_init,
                     68:        window_copy_free,
                     69:        window_copy_resize,
                     70:        window_copy_key,
                     71:        window_copy_mouse,
                     72:        NULL,
                     73: };
                     74:
                     75: struct window_copy_mode_data {
                     76:        struct screen   screen;
                     77:
                     78:        struct mode_key_data    mdata;
                     79:
                     80:        u_int   ox;
                     81:        u_int   oy;
                     82:
                     83:        u_int   selx;
                     84:        u_int   sely;
                     85:
                     86:        u_int   cx;
                     87:        u_int   cy;
                     88: };
                     89:
                     90: struct screen *
                     91: window_copy_init(struct window_pane *wp)
                     92: {
                     93:        struct window_copy_mode_data    *data;
                     94:        struct screen                   *s;
                     95:        struct screen_write_ctx          ctx;
                     96:        u_int                            i;
                     97:
                     98:        wp->modedata = data = xmalloc(sizeof *data);
                     99:        data->ox = 0;
                    100:        data->oy = 0;
                    101:        data->cx = wp->base.cx;
                    102:        data->cy = wp->base.cy;
                    103:
                    104:        s = &data->screen;
                    105:        screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
                    106:        s->mode |= MODE_MOUSE;
                    107:
                    108:        mode_key_init(&data->mdata,
                    109:            options_get_number(&wp->window->options, "mode-keys"), 0);
                    110:
                    111:        s->cx = data->cx;
                    112:        s->cy = data->cy;
                    113:
                    114:        screen_write_start(&ctx, NULL, s);
                    115:        for (i = 0; i < screen_size_y(s); i++)
                    116:                window_copy_write_line(wp, &ctx, i);
                    117:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    118:        screen_write_stop(&ctx);
                    119:
                    120:        return (s);
                    121: }
                    122:
                    123: void
                    124: window_copy_free(struct window_pane *wp)
                    125: {
                    126:        struct window_copy_mode_data    *data = wp->modedata;
                    127:
                    128:        mode_key_free(&data->mdata);
                    129:
                    130:        screen_free(&data->screen);
                    131:        xfree(data);
                    132: }
                    133:
                    134: void
                    135: window_copy_pageup(struct window_pane *wp)
                    136: {
                    137:        struct window_copy_mode_data    *data = wp->modedata;
                    138:        struct screen                   *s = &data->screen;
                    139:
                    140:        if (data->oy + screen_size_y(s) > screen_hsize(&wp->base))
                    141:                data->oy = screen_hsize(&wp->base);
                    142:        else
                    143:                data->oy += screen_size_y(s);
                    144:        window_copy_update_selection(wp);
                    145:        window_copy_redraw_screen(wp);
                    146: }
                    147:
                    148: void
                    149: window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
                    150: {
                    151:        struct window_copy_mode_data    *data = wp->modedata;
                    152:        struct screen                   *s = &data->screen;
                    153:        struct screen_write_ctx          ctx;
                    154:
                    155:        screen_resize(s, sx, sy);
                    156:        screen_write_start(&ctx, NULL, s);
                    157:        window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
                    158:        screen_write_stop(&ctx);
                    159:        window_copy_update_selection(wp);
                    160: }
                    161:
                    162: void
                    163: window_copy_key(struct window_pane *wp, struct client *c, int key)
                    164: {
                    165:        struct window_copy_mode_data    *data = wp->modedata;
                    166:        struct screen                   *s = &data->screen;
                    167:
                    168:        switch (mode_key_lookup(&data->mdata, key)) {
                    169:        case MODEKEYCMD_QUIT:
                    170:                window_pane_reset_mode(wp);
                    171:                break;
                    172:        case MODEKEYCMD_LEFT:
                    173:                window_copy_cursor_left(wp);
                    174:                return;
                    175:        case MODEKEYCMD_RIGHT:
                    176:                window_copy_cursor_right(wp);
                    177:                return;
                    178:        case MODEKEYCMD_UP:
                    179:                window_copy_cursor_up(wp);
                    180:                return;
                    181:        case MODEKEYCMD_DOWN:
                    182:                window_copy_cursor_down(wp);
                    183:                return;
                    184:        case MODEKEYCMD_PREVIOUSPAGE:
                    185:                window_copy_pageup(wp);
                    186:                break;
                    187:        case MODEKEYCMD_NEXTPAGE:
                    188:                if (data->oy < screen_size_y(s))
                    189:                        data->oy = 0;
                    190:                else
                    191:                        data->oy -= screen_size_y(s);
                    192:                window_copy_update_selection(wp);
                    193:                window_copy_redraw_screen(wp);
                    194:                break;
                    195:        case MODEKEYCMD_STARTSELECTION:
                    196:                window_copy_start_selection(wp);
1.7       nicm      197:                window_copy_redraw_screen(wp);
1.1       nicm      198:                break;
                    199:        case MODEKEYCMD_CLEARSELECTION:
                    200:                screen_clear_selection(&data->screen);
                    201:                window_copy_redraw_screen(wp);
                    202:                break;
                    203:        case MODEKEYCMD_COPYSELECTION:
                    204:                if (c != NULL && c->session != NULL) {
                    205:                        window_copy_copy_selection(wp, c);
                    206:                        window_pane_reset_mode(wp);
                    207:                }
                    208:                break;
                    209:        case MODEKEYCMD_STARTOFLINE:
                    210:                window_copy_cursor_start_of_line(wp);
                    211:                break;
1.6       nicm      212:        case MODEKEYCMD_BACKTOINDENTATION:
                    213:                window_copy_cursor_back_to_indentation(wp);
                    214:                break;
1.1       nicm      215:        case MODEKEYCMD_ENDOFLINE:
                    216:                window_copy_cursor_end_of_line(wp);
                    217:                break;
                    218:        case MODEKEYCMD_NEXTWORD:
                    219:                window_copy_cursor_next_word(wp);
                    220:                break;
                    221:        case MODEKEYCMD_PREVIOUSWORD:
                    222:                window_copy_cursor_previous_word(wp);
                    223:                break;
                    224:        default:
                    225:                break;
                    226:        }
                    227: }
                    228:
                    229: void
                    230: window_copy_mouse(struct window_pane *wp,
                    231:     unused struct client *c, u_char b, u_char x, u_char y)
                    232: {
                    233:        struct window_copy_mode_data    *data = wp->modedata;
                    234:        struct screen                   *s = &data->screen;
                    235:
                    236:        if ((b & 3) == 3)
                    237:                return;
                    238:        if (x >= screen_size_x(s))
                    239:                return;
                    240:        if (y >= screen_size_y(s))
                    241:                return;
                    242:
                    243:        data->cx = x;
                    244:        data->cy = y;
                    245:
                    246:        if (window_copy_update_selection(wp))
                    247:                window_copy_redraw_screen(wp);
                    248:        window_copy_update_cursor(wp);
                    249: }
                    250:
                    251: void
                    252: window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
                    253: {
                    254:        struct window_copy_mode_data    *data = wp->modedata;
                    255:        struct screen                   *s = &data->screen;
                    256:        struct grid_cell                 gc;
                    257:        char                             hdr[32];
                    258:        size_t                           size;
                    259:
                    260:        if (py == 0) {
                    261:                memcpy(&gc, &grid_default_cell, sizeof gc);
                    262:                size = xsnprintf(hdr, sizeof hdr,
                    263:                    "[%u,%u/%u]", data->ox, data->oy, screen_hsize(&wp->base));
                    264:                gc.bg = options_get_number(&wp->window->options, "mode-fg");
                    265:                gc.fg = options_get_number(&wp->window->options, "mode-bg");
                    266:                gc.attr |= options_get_number(&wp->window->options, "mode-attr");
                    267:                screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
                    268:                screen_write_puts(ctx, &gc, "%s", hdr);
                    269:        } else
                    270:                size = 0;
                    271:
                    272:        screen_write_cursormove(ctx, 0, py);
                    273:        screen_write_copy(ctx, &wp->base, data->ox, (screen_hsize(&wp->base) -
                    274:            data->oy) + py, screen_size_x(s) - size, 1);
                    275: }
                    276:
                    277: void
                    278: window_copy_write_lines(
                    279:     struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
                    280: {
                    281:        u_int   yy;
                    282:
                    283:        for (yy = py; yy < py + ny; yy++)
                    284:                window_copy_write_line(wp, ctx, py);
                    285: }
                    286:
                    287: void
                    288: window_copy_write_column(
                    289:     struct window_pane *wp, struct screen_write_ctx *ctx, u_int px)
                    290: {
                    291:        struct window_copy_mode_data    *data = wp->modedata;
                    292:        struct screen                   *s = &data->screen;
                    293:
                    294:        screen_write_cursormove(ctx, px, 0);
                    295:        screen_write_copy(ctx, &wp->base,
                    296:            data->ox + px, screen_hsize(&wp->base) - data->oy, 1, screen_size_y(s));
                    297: }
                    298:
                    299: void
                    300: window_copy_write_columns(
                    301:     struct window_pane *wp, struct screen_write_ctx *ctx, u_int px, u_int nx)
                    302: {
                    303:        u_int   xx;
                    304:
                    305:        for (xx = px; xx < px + nx; xx++)
                    306:                window_copy_write_column(wp, ctx, xx);
                    307: }
                    308:
                    309: void
                    310: window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
                    311: {
                    312:        struct window_copy_mode_data    *data = wp->modedata;
                    313:        struct screen_write_ctx          ctx;
                    314:        u_int                            i;
                    315:
                    316:        screen_write_start(&ctx, wp, NULL);
                    317:        for (i = py; i < py + ny; i++)
                    318:                window_copy_write_line(wp, &ctx, i);
                    319:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    320:        screen_write_stop(&ctx);
                    321: }
                    322:
                    323: void
                    324: window_copy_redraw_screen(struct window_pane *wp)
                    325: {
                    326:        struct window_copy_mode_data    *data = wp->modedata;
                    327:
                    328:        window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
                    329: }
                    330:
                    331: void
                    332: window_copy_update_cursor(struct window_pane *wp)
                    333: {
                    334:        struct window_copy_mode_data    *data = wp->modedata;
                    335:        struct screen_write_ctx          ctx;
                    336:
                    337:        screen_write_start(&ctx, wp, NULL);
                    338:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    339:        screen_write_stop(&ctx);
                    340: }
                    341:
                    342: void
                    343: window_copy_start_selection(struct window_pane *wp)
                    344: {
                    345:        struct window_copy_mode_data    *data = wp->modedata;
                    346:        struct screen                   *s = &data->screen;
                    347:
                    348:        data->selx = data->cx + data->ox;
                    349:        data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
                    350:
                    351:        s->sel.flag = 1;
                    352:        window_copy_update_selection(wp);
                    353: }
                    354:
                    355: int
                    356: window_copy_update_selection(struct window_pane *wp)
                    357: {
                    358:        struct window_copy_mode_data    *data = wp->modedata;
                    359:        struct screen                   *s = &data->screen;
                    360:        struct grid_cell                 gc;
                    361:        u_int                            sx, sy, tx, ty;
                    362:
                    363:        if (!s->sel.flag)
                    364:                return (0);
                    365:
                    366:        /* Set colours. */
                    367:        memcpy(&gc, &grid_default_cell, sizeof gc);
                    368:        gc.bg = options_get_number(&wp->window->options, "mode-fg");
                    369:        gc.fg = options_get_number(&wp->window->options, "mode-bg");
                    370:        gc.attr |= options_get_number(&wp->window->options, "mode-attr");
                    371:
                    372:        /* Find top-left of screen. */
                    373:        tx = data->ox;
                    374:        ty = screen_hsize(&wp->base) - data->oy;
                    375:
                    376:        /* Adjust the selection. */
                    377:        sx = data->selx;
                    378:        sy = data->sely;
                    379:        if (sy < ty) {
                    380:                /* Above it. */
                    381:                sx = 0;
                    382:                sy = 0;
                    383:        } else if (sy > ty + screen_size_y(s) - 1) {
                    384:                /* Below it. */
                    385:                sx = screen_size_x(s) - 1;
                    386:                sy = screen_size_y(s) - 1;
                    387:        } else if (sx < tx) {
                    388:                /* To the left. */
                    389:                sx = 0;
                    390:        } else if (sx > tx + screen_size_x(s) - 1) {
                    391:                /* To the right. */
                    392:                sx = 0;
                    393:                sy++;
                    394:                if (sy > screen_size_y(s) - 1)
                    395:                        sy = screen_size_y(s) - 1;
                    396:        } else {
                    397:                sx -= tx;
                    398:                sy -= ty;
                    399:        }
                    400:        sy = screen_hsize(s) + sy;
                    401:
                    402:        screen_set_selection(
                    403:            s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc);
                    404:        return (1);
                    405: }
                    406:
                    407: void
                    408: window_copy_copy_selection(struct window_pane *wp, struct client *c)
                    409: {
                    410:        struct window_copy_mode_data    *data = wp->modedata;
                    411:        struct screen                   *s = &data->screen;
                    412:        char                            *buf;
                    413:        size_t                           off;
                    414:        u_int                            i, xx, yy, sx, sy, ex, ey, limit;
                    415:
                    416:        if (!s->sel.flag)
                    417:                return;
                    418:
                    419:        buf = xmalloc(1);
                    420:        off = 0;
                    421:
                    422:        *buf = '\0';
                    423:
                    424:        /*
                    425:         * The selection extends from selx,sely to (adjusted) cx,cy on
                    426:         * the base screen.
                    427:         */
                    428:
                    429:        /* Find start and end. */
                    430:        xx = data->cx + data->ox;
                    431:        yy = screen_hsize(&wp->base) + data->cy - data->oy;
1.2       nicm      432:        if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1.1       nicm      433:                sx = xx; sy = yy;
                    434:                ex = data->selx; ey = data->sely;
                    435:        } else {
                    436:                sx = data->selx; sy = data->sely;
                    437:                ex = xx; ey = yy;
                    438:        }
                    439:
                    440:        /* Trim ex to end of line. */
                    441:        xx = window_copy_find_length(wp, ey);
                    442:        if (ex > xx)
                    443:                ex = xx;
                    444:
                    445:        /* Copy the lines. */
                    446:        if (sy == ey)
                    447:                window_copy_copy_line(wp, &buf, &off, sy, sx, ex);
                    448:        else {
                    449:                xx = window_copy_find_length(wp, sy);
                    450:                window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
                    451:                if (ey - sy > 1) {
1.3       nicm      452:                        for (i = sy + 1; i < ey; i++) {
1.1       nicm      453:                                xx = window_copy_find_length(wp, i);
                    454:                                window_copy_copy_line(wp, &buf, &off, i, 0, xx);
                    455:                        }
                    456:                }
                    457:                window_copy_copy_line(wp, &buf, &off, ey, 0, ex);
                    458:        }
                    459:
                    460:        /* Terminate buffer, overwriting final \n. */
                    461:        if (off != 0)
                    462:                buf[off - 1] = '\0';
                    463:
                    464:        /* Add the buffer to the stack. */
                    465:        limit = options_get_number(&c->session->options, "buffer-limit");
                    466:        paste_add(&c->session->buffers, buf, limit);
                    467: }
                    468:
                    469: void
                    470: window_copy_copy_line(struct window_pane *wp,
                    471:     char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
                    472: {
                    473:        const struct grid_cell  *gc;
                    474:        const struct grid_utf8  *gu;
                    475:        u_int                    i, j, xx;
                    476:
                    477:        if (sx > ex)
                    478:                return;
                    479:
                    480:        xx = window_copy_find_length(wp, sy);
                    481:        if (ex > xx)
                    482:                ex = xx;
                    483:        if (sx > xx)
                    484:                sx = xx;
                    485:
                    486:        if (sx < ex) {
                    487:                for (i = sx; i < ex; i++) {
                    488:                        gc = grid_peek_cell(wp->base.grid, i, sy);
                    489:                        if (gc->flags & GRID_FLAG_PADDING)
                    490:                                continue;
                    491:                        if (!(gc->flags & GRID_FLAG_UTF8)) {
                    492:                                *buf = xrealloc(*buf, 1, (*off) + 1);
                    493:                                (*buf)[(*off)++] = gc->data;
                    494:                        } else {
                    495:                                gu = grid_peek_utf8(wp->base.grid, i, sy);
                    496:                                *buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE);
                    497:                                for (j = 0; j < UTF8_SIZE; j++) {
                    498:                                        if (gu->data[j] == 0xff)
                    499:                                                break;
                    500:                                        (*buf)[(*off)++] = gu->data[j];
                    501:                                }
                    502:                        }
                    503:                }
                    504:        }
                    505:
                    506:        *buf = xrealloc(*buf, 1, (*off) + 1);
                    507:        (*buf)[*off] = '\n';
                    508:        (*off)++;
                    509: }
                    510:
                    511: int
                    512: window_copy_is_space(struct window_pane *wp, u_int px, u_int py)
                    513: {
                    514:        const struct grid_cell  *gc;
                    515:        const char              *spaces = " -_@";
                    516:
                    517:        gc = grid_peek_cell(wp->base.grid, px, py);
                    518:        if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
                    519:                return (0);
                    520:        if (gc->data == 0x00 || gc->data == 0x7f)
                    521:                return (0);
                    522:        return (strchr(spaces, gc->data) != NULL);
                    523: }
                    524:
                    525: u_int
                    526: window_copy_find_length(struct window_pane *wp, u_int py)
                    527: {
                    528:        const struct grid_cell  *gc;
                    529:        u_int                    px;
                    530:
1.4       nicm      531:        /*
                    532:         * If the pane has been resized, its grid can contain old overlong
                    533:         * lines. grid_peek_cell does not allow accessing cells beyond the
                    534:         * width of the grid, and screen_write_copy treats them as spaces, so
                    535:         * ignore them here too.
                    536:         */
1.1       nicm      537:        px = wp->base.grid->size[py];
1.4       nicm      538:        if (px > screen_size_x(&wp->base))
                    539:                px = screen_size_x(&wp->base);
1.1       nicm      540:        while (px > 0) {
                    541:                gc = grid_peek_cell(wp->base.grid, px - 1, py);
                    542:                if (gc->flags & GRID_FLAG_UTF8)
                    543:                        break;
                    544:                if (gc->data != ' ')
                    545:                        break;
                    546:                px--;
                    547:        }
                    548:        return (px);
                    549: }
                    550:
1.5       nicm      551: /*
                    552:  * Set the cursor X coordinate and scroll horizontally to make it visible.
                    553:  * Also redraw the selection or the cursor, as needed.
                    554:  */
1.1       nicm      555: void
1.5       nicm      556: window_copy_set_cursor_x(struct window_pane *wp, u_int px)
1.1       nicm      557: {
                    558:        struct window_copy_mode_data    *data = wp->modedata;
                    559:        struct screen                   *s = &data->screen;
                    560:
                    561:        /* On screen. */
                    562:        if (px > data->ox && px <= data->ox + screen_size_x(s) - 1)
                    563:                data->cx = px - data->ox;
                    564:
                    565:        /* Off right of screen. */
                    566:        if (px > data->ox + screen_size_x(s) - 1) {
                    567:                /* Move cursor to last and scroll screen. */
                    568:                window_copy_scroll_left(
                    569:                    wp, px - data->ox - (screen_size_x(s) - 1));
                    570:                data->cx = screen_size_x(s) - 1;
                    571:        }
                    572:
                    573:        /* Off left of screen. */
                    574:        if (px <= data->ox) {
                    575:                if (px < screen_size_x(s) - 1) {
                    576:                        /* Short enough to fit on screen. */
                    577:                        window_copy_scroll_right(wp, data->ox);
                    578:                        data->cx = px;
                    579:                } else {
                    580:                        /* Too long to fit on screen. */
                    581:                        window_copy_scroll_right(
                    582:                            wp, data->ox - (px - (screen_size_x(s) - 1)));
                    583:                        data->cx = screen_size_x(s) - 1;
                    584:                }
                    585:        }
                    586:
                    587:        if (window_copy_update_selection(wp))
                    588:                window_copy_redraw_lines(wp, data->cy, 1);
                    589:        else
                    590:                window_copy_update_cursor(wp);
                    591: }
                    592:
                    593: void
1.5       nicm      594: window_copy_cursor_start_of_line(struct window_pane *wp)
                    595: {
                    596:        struct window_copy_mode_data    *data = wp->modedata;
                    597:
                    598:        if (data->ox != 0)
                    599:                window_copy_scroll_right(wp, data->ox);
                    600:        data->cx = 0;
                    601:
                    602:        if (window_copy_update_selection(wp))
                    603:                window_copy_redraw_lines(wp, data->cy, 1);
                    604:        else
                    605:                window_copy_update_cursor(wp);
1.6       nicm      606: }
                    607:
                    608: void
                    609: window_copy_cursor_back_to_indentation(struct window_pane *wp)
                    610: {
                    611:        struct window_copy_mode_data    *data = wp->modedata;
                    612:        u_int                            px, py, xx;
                    613:        const struct grid_cell          *gc;
                    614:
                    615:        px = 0;
                    616:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    617:        xx = window_copy_find_length(wp, py);
                    618:
                    619:        /*
                    620:         * Don't use window_copy_is_space because that treats some word
                    621:         * delimiters as spaces.
                    622:         */
                    623:        while (px < xx) {
                    624:                gc = grid_peek_cell(wp->base.grid, px, py);
                    625:                if (gc->flags & GRID_FLAG_UTF8)
                    626:                        break;
                    627:                if (gc->data != ' ')
                    628:                        break;
                    629:                px++;
                    630:        }
                    631:
                    632:        window_copy_set_cursor_x(wp, px);
1.5       nicm      633: }
                    634:
                    635: void
                    636: window_copy_cursor_end_of_line(struct window_pane *wp)
                    637: {
                    638:        struct window_copy_mode_data    *data = wp->modedata;
                    639:        u_int                            px, py;
                    640:
                    641:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    642:        px = window_copy_find_length(wp, py);
                    643:
                    644:        window_copy_set_cursor_x(wp, px);
                    645: }
                    646:
                    647: void
1.1       nicm      648: window_copy_cursor_left(struct window_pane *wp)
                    649: {
                    650:        struct window_copy_mode_data    *data = wp->modedata;
                    651:
                    652:        if (data->cx == 0) {
                    653:                if (data->ox > 0)
                    654:                        window_copy_scroll_right(wp, 1);
                    655:                else {
                    656:                        window_copy_cursor_up(wp);
                    657:                        window_copy_cursor_end_of_line(wp);
                    658:                }
                    659:        } else {
                    660:                data->cx--;
                    661:                if (window_copy_update_selection(wp))
                    662:                        window_copy_redraw_lines(wp, data->cy, 1);
                    663:                else
                    664:                        window_copy_update_cursor(wp);
                    665:        }
                    666: }
                    667:
                    668: void
                    669: window_copy_cursor_right(struct window_pane *wp)
                    670: {
                    671:        struct window_copy_mode_data    *data = wp->modedata;
                    672:        u_int                            px, py;
                    673:
                    674:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    675:        px = window_copy_find_length(wp, py);
                    676:
                    677:        if (data->cx >= px) {
                    678:                window_copy_cursor_start_of_line(wp);
                    679:                window_copy_cursor_down(wp);
                    680:        } else {
                    681:                data->cx++;
                    682:                if (window_copy_update_selection(wp))
                    683:                        window_copy_redraw_lines(wp, data->cy, 1);
                    684:                else
                    685:                        window_copy_update_cursor(wp);
                    686:        }
                    687: }
                    688:
                    689: void
                    690: window_copy_cursor_up(struct window_pane *wp)
                    691: {
                    692:        struct window_copy_mode_data    *data = wp->modedata;
                    693:        u_int                            ox, oy, px, py;
                    694:
                    695:        oy = screen_hsize(&wp->base) + data->cy - data->oy;
                    696:        ox = window_copy_find_length(wp, oy);
                    697:
                    698:        if (data->cy == 0)
                    699:                window_copy_scroll_down(wp, 1);
                    700:        else {
                    701:                data->cy--;
                    702:                if (window_copy_update_selection(wp))
                    703:                        window_copy_redraw_lines(wp, data->cy, 2);
                    704:                else
                    705:                        window_copy_update_cursor(wp);
                    706:        }
                    707:
                    708:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    709:        px = window_copy_find_length(wp, py);
                    710:
                    711:        if (data->cx + data->ox >= px || data->cx + data->ox >= ox)
                    712:                window_copy_cursor_end_of_line(wp);
                    713: }
                    714:
                    715: void
                    716: window_copy_cursor_down(struct window_pane *wp)
                    717: {
                    718:        struct window_copy_mode_data    *data = wp->modedata;
                    719:        struct screen                   *s = &data->screen;
                    720:        u_int                            ox, oy, px, py;
                    721:
                    722:        oy = screen_hsize(&wp->base) + data->cy - data->oy;
                    723:        ox = window_copy_find_length(wp, oy);
                    724:
                    725:        if (data->cy == screen_size_y(s) - 1)
                    726:                window_copy_scroll_up(wp, 1);
                    727:        else {
                    728:                data->cy++;
                    729:                if (window_copy_update_selection(wp))
                    730:                        window_copy_redraw_lines(wp, data->cy - 1, 2);
                    731:                else
                    732:                        window_copy_update_cursor(wp);
                    733:        }
                    734:
                    735:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    736:        px = window_copy_find_length(wp, py);
                    737:
                    738:        if (data->cx + data->ox >= px || data->cx + data->ox >= ox)
                    739:                window_copy_cursor_end_of_line(wp);
                    740: }
                    741:
                    742: void
                    743: window_copy_cursor_next_word(struct window_pane *wp)
                    744: {
                    745:        struct window_copy_mode_data    *data = wp->modedata;
                    746:        struct screen                   *s = &data->screen;
                    747:        u_int                            px, py, xx, skip;
                    748:
                    749:        px = data->ox + data->cx;
                    750:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    751:        xx = window_copy_find_length(wp, py);
                    752:
                    753:        skip = 1;
                    754:        if (px < xx) {
                    755:                /* If currently on a space, skip space. */
                    756:                if (window_copy_is_space(wp, px, py))
                    757:                        skip = 0;
                    758:        } else
                    759:                skip = 0;
                    760:        for (;;) {
                    761:                if (px >= xx) {
                    762:                        if (skip) {
                    763:                                px = xx;
                    764:                                break;
                    765:                        }
                    766:
                    767:                        while (px >= xx) {
                    768:                                if (data->cy == screen_size_y(s) - 1) {
                    769:                                        if (data->oy == 0)
                    770:                                                goto out;
                    771:                                }
                    772:
                    773:                                px = 0;
                    774:                                window_copy_cursor_down(wp);
                    775:
                    776:                                py =screen_hsize(
                    777:                                    &wp->base) + data->cy - data->oy;
                    778:                                xx = window_copy_find_length(wp, py);
                    779:                        }
                    780:                }
                    781:
                    782:                if (skip) {
                    783:                        /* Currently skipping non-space (until space). */
                    784:                        if (window_copy_is_space(wp, px, py))
                    785:                                break;
                    786:                } else {
                    787:                        /* Currently skipping space (until non-space). */
                    788:                        if (!window_copy_is_space(wp, px, py))
                    789:                                skip = 1;
                    790:                }
                    791:
                    792:                px++;
                    793:        }
                    794: out:
                    795:
1.5       nicm      796:        window_copy_set_cursor_x(wp, px);
1.1       nicm      797: }
                    798:
1.8     ! nicm      799: /* Move to the previous place where a word begins. */
1.1       nicm      800: void
                    801: window_copy_cursor_previous_word(struct window_pane *wp)
                    802: {
                    803:        struct window_copy_mode_data    *data = wp->modedata;
1.8     ! nicm      804:        u_int                            px, py;
1.1       nicm      805:
1.8     ! nicm      806:        px = data->ox + data->cx;
1.1       nicm      807:        py = screen_hsize(&wp->base) + data->cy - data->oy;
                    808:
1.8     ! nicm      809:        /* Move back to the previous word character. */
1.1       nicm      810:        for (;;) {
1.8     ! nicm      811:                if (px > 0) {
        !           812:                        px--;
        !           813:                        if (!window_copy_is_space(wp, px, py))
1.1       nicm      814:                                break;
1.8     ! nicm      815:                } else {
        !           816:                        if (data->cy == 0 &&
        !           817:                            (screen_hsize(&wp->base) == 0 ||
        !           818:                            data->oy >= screen_hsize(&wp->base) - 1))
        !           819:                                goto out;
        !           820:                        window_copy_cursor_up(wp);
1.1       nicm      821:
1.8     ! nicm      822:                        py = screen_hsize(
        !           823:                            &wp->base) + data->cy - data->oy;
        !           824:                        px = window_copy_find_length(wp, py);
1.1       nicm      825:                }
1.8     ! nicm      826:        }
1.1       nicm      827:
1.8     ! nicm      828:        /* Move back to the beginning of this word. */
        !           829:        while (px > 0 && !window_copy_is_space(wp, px - 1, py))
1.1       nicm      830:                px--;
1.8     ! nicm      831:
1.1       nicm      832: out:
1.5       nicm      833:        window_copy_set_cursor_x(wp, px);
1.1       nicm      834: }
                    835:
                    836: void
                    837: window_copy_scroll_left(struct window_pane *wp, u_int nx)
                    838: {
                    839:        struct window_copy_mode_data    *data = wp->modedata;
                    840:        struct screen                   *s = &data->screen;
                    841:        struct screen_write_ctx          ctx;
                    842:        u_int                            i;
                    843:
                    844:        if (data->ox > SHRT_MAX - nx)
                    845:                nx = SHRT_MAX - data->ox;
                    846:        if (nx == 0)
                    847:                return;
                    848:        data->ox += nx;
                    849:        window_copy_update_selection(wp);
                    850:
                    851:        screen_write_start(&ctx, wp, NULL);
                    852:        for (i = 1; i < screen_size_y(s); i++) {
                    853:                screen_write_cursormove(&ctx, 0, i);
                    854:                screen_write_deletecharacter(&ctx, nx);
                    855:        }
                    856:        window_copy_write_columns(wp, &ctx, screen_size_x(s) - nx, nx);
                    857:        window_copy_write_line(wp, &ctx, 0);
                    858:        if (s->sel.flag) {
                    859:                window_copy_update_selection(wp);
                    860:                window_copy_write_lines(wp, &ctx, data->cy, 1);
                    861:        }
                    862:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    863:        screen_write_stop(&ctx);
                    864: }
                    865:
                    866: void
                    867: window_copy_scroll_right(struct window_pane *wp, u_int nx)
                    868: {
                    869:        struct window_copy_mode_data    *data = wp->modedata;
                    870:        struct screen                   *s = &data->screen;
                    871:        struct screen_write_ctx          ctx;
                    872:        u_int                            i;
                    873:
                    874:        if (data->ox < nx)
                    875:                nx = data->ox;
                    876:        if (nx == 0)
                    877:                return;
                    878:        data->ox -= nx;
                    879:        window_copy_update_selection(wp);
                    880:
                    881:        screen_write_start(&ctx, wp, NULL);
                    882:        for (i = 1; i < screen_size_y(s); i++) {
                    883:                screen_write_cursormove(&ctx, 0, i);
                    884:                screen_write_insertcharacter(&ctx, nx);
                    885:        }
                    886:        window_copy_write_columns(wp, &ctx, 0, nx);
                    887:        window_copy_write_line(wp, &ctx, 0);
                    888:        if (s->sel.flag)
                    889:                window_copy_write_line(wp, &ctx, data->cy);
                    890:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    891:        screen_write_stop(&ctx);
                    892: }
                    893:
                    894: void
                    895: window_copy_scroll_up(struct window_pane *wp, u_int ny)
                    896: {
                    897:        struct window_copy_mode_data    *data = wp->modedata;
                    898:        struct screen                   *s = &data->screen;
                    899:        struct screen_write_ctx          ctx;
                    900:
                    901:        if (data->oy < ny)
                    902:                ny = data->oy;
                    903:        if (ny == 0)
                    904:                return;
                    905:        data->oy -= ny;
                    906:        window_copy_update_selection(wp);
                    907:
                    908:        screen_write_start(&ctx, wp, NULL);
                    909:        screen_write_cursormove(&ctx, 0, 0);
                    910:        screen_write_deleteline(&ctx, ny);
                    911:        window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
                    912:        window_copy_write_line(wp, &ctx, 0);
                    913:        window_copy_write_line(wp, &ctx, 1);
                    914:        if (s->sel.flag && screen_size_y(s) > ny)
                    915:                window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
                    916:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    917:        screen_write_stop(&ctx);
                    918: }
                    919:
                    920: void
                    921: window_copy_scroll_down(struct window_pane *wp, u_int ny)
                    922: {
                    923:        struct window_copy_mode_data    *data = wp->modedata;
                    924:        struct screen                   *s = &data->screen;
                    925:        struct screen_write_ctx          ctx;
                    926:
                    927:        if (ny > screen_hsize(&wp->base))
                    928:                return;
                    929:
                    930:        if (data->oy > screen_hsize(&wp->base) - ny)
                    931:                ny = screen_hsize(&wp->base) - data->oy;
                    932:        if (ny == 0)
                    933:                return;
                    934:        data->oy += ny;
                    935:        window_copy_update_selection(wp);
                    936:
                    937:        screen_write_start(&ctx, wp, NULL);
                    938:        screen_write_cursormove(&ctx, 0, 0);
                    939:        screen_write_insertline(&ctx, ny);
                    940:        window_copy_write_lines(wp, &ctx, 0, ny);
                    941:        if (s->sel.flag && screen_size_y(s) > ny)
                    942:                window_copy_write_line(wp, &ctx, ny);
                    943:        else if (ny == 1) /* nuke position */
                    944:                window_copy_write_line(wp, &ctx, 1);
                    945:        screen_write_cursormove(&ctx, data->cx, data->cy);
                    946:        screen_write_stop(&ctx);
                    947: }