From 8da0b1fb990956bc82243dc9693041286d69636a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 23 Nov 2007 17:52:54 +0000 Subject: [PATCH] Mostly-complete copy and paste. --- CHANGES | 9 +++- Makefile | 4 +- TODO | 2 + cmd-paste-buffer.c | 57 ++++++++++++++++++++ cmd.c | 3 +- key-bindings.c | 3 +- tmux.c | 11 ++-- tmux.h | 10 ++-- window-copy.c | 132 +++++++++++++++++++++++++++++++++++++++++---- 9 files changed, 207 insertions(+), 24 deletions(-) create mode 100644 cmd-paste-buffer.c diff --git a/CHANGES b/CHANGES index 7f308674..240961a6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ 23 November 2007 +* Mostly complete copy & paste. Copy mode entered with C-b [ (copy-mode + command). In copy mode, arrow keys/page up/page down/hjkl/C-u/C-f navigate, + space or C-space starts selection, and enter or C-w copies and (important!) + exits copy mode. C-b ] (paste-buffer) pastes into current terminal. No + extra utility keys (bol/eol/clear selection/etc), only one single buffer, + and no buffer manipulation commands (clear/view/etc) yet. The code is also + fugly :-(. * history-limit option to set maximum history. Does not apply retroactively to existing windows! Lines take up a variable amount of space, but a reasonable guess for an 80-column terminal is 250 KB per 1000 lines (of history used, @@ -256,4 +263,4 @@ (including mutt, emacs). No status bar yet and no key remapping or other customisation. -$Id: CHANGES,v 1.81 2007-11-23 12:48:20 nicm Exp $ +$Id: CHANGES,v 1.82 2007-11-23 17:52:54 nicm Exp $ diff --git a/Makefile b/Makefile index ab0380ee..13d72164 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.47 2007-11-22 18:09:43 nicm Exp $ +# $Id: Makefile,v 1.48 2007-11-23 17:52:54 nicm Exp $ .SUFFIXES: .c .o .y .h .PHONY: clean update-index.html upload-index.html @@ -27,7 +27,7 @@ SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c status.c \ cmd-link-window.c cmd-unlink-window.c cmd-next-window.c \ cmd-swap-window.c cmd-rename-session.c cmd-kill-session.c \ cmd-switch-client.c cmd-has-session.c cmd-scroll-mode.c cmd-copy-mode.c \ - window-scroll.c window-more.c window-copy.c + cmd-paste-buffer.c window-scroll.c window-more.c window-copy.c CC?= cc INCDIRS+= -I. -I- -I/usr/local/include diff --git a/TODO b/TODO index 521c735d..dfccd2f5 100644 --- a/TODO +++ b/TODO @@ -59,6 +59,8 @@ command to purge window history extend list-clients to list clients attached to a session (-a for all?) bring back detach-session to detach all clients on a session? + paste buffer etc shouldn't be limited to keys + buffer manip: clear, view etc - function groups, bind-key ^W { select-window 0; send-key ^W } etc - more(1) style handling for in-client output - allow fnmatch for -c, so that you can, eg, detach all clients diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c new file mode 100644 index 00000000..b4bdf324 --- /dev/null +++ b/cmd-paste-buffer.c @@ -0,0 +1,57 @@ +/* $Id: cmd-paste-buffer.c,v 1.1 2007-11-23 17:52:54 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Paste paste buffer if present. + */ + +void cmd_paste_buffer_exec(void *, struct cmd_ctx *); + +const struct cmd_entry cmd_paste_buffer_entry = { + "paste-buffer", NULL, "paste", + CMD_NOCLIENT, + NULL, + cmd_paste_buffer_exec, + NULL, + NULL, + NULL +}; + +void +cmd_paste_buffer_exec(unused void *ptr, struct cmd_ctx *ctx) +{ + struct window *w = ctx->session->curw->window; + + if (ctx->flags & CMD_KEY) { + if (paste_buffer != NULL && *paste_buffer != '\0') { + buffer_write( + w->out, paste_buffer, strlen(paste_buffer)); + } + } + + if (ctx->cmdclient != NULL) + server_write_client(ctx->cmdclient, MSG_EXIT, NULL, 0); +} diff --git a/cmd.c b/cmd.c index 0023bbad..6526fa90 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $Id: cmd.c,v 1.31 2007-11-22 18:09:43 nicm Exp $ */ +/* $Id: cmd.c,v 1.32 2007-11-23 17:52:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -40,6 +40,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_new_session_entry, &cmd_new_window_entry, &cmd_next_window_entry, + &cmd_paste_buffer_entry, &cmd_previous_window_entry, &cmd_refresh_client_entry, &cmd_rename_session_entry, diff --git a/key-bindings.c b/key-bindings.c index 84358b4d..d6be315d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,4 +1,4 @@ -/* $Id: key-bindings.c,v 1.20 2007-11-22 18:09:43 nicm Exp $ */ +/* $Id: key-bindings.c,v 1.21 2007-11-23 17:52:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -110,6 +110,7 @@ key_bindings_init(void) { '&', &cmd_kill_window_entry, NULL }, { '=', &cmd_scroll_mode_entry, NULL }, { '[', &cmd_copy_mode_entry, NULL }, + { ']', &cmd_paste_buffer_entry, NULL }, { META, &cmd_send_prefix_entry, NULL }, }; u_int i; diff --git a/tmux.c b/tmux.c index 4d411e4b..7f8a6272 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id: tmux.c,v 1.43 2007-11-23 12:48:20 nicm Exp $ */ +/* $Id: tmux.c,v 1.44 2007-11-23 17:52:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -37,13 +37,14 @@ const char *malloc_options = "AFGJPX"; volatile sig_atomic_t sigwinch; volatile sig_atomic_t sigterm; +char *default_command; +char *paste_buffer; +int bell_action; int debug_level; int prefix_key = META; -u_int status_lines; u_char status_colour; -char *default_command; -int bell_action; u_int history_limit; +u_int status_lines; void sighandler(int); @@ -217,6 +218,8 @@ main(int argc, char **argv) history_limit = 2000; + paste_buffer = NULL; + if (path == NULL) { xasprintf(&path, "%s/%s-%lu", _PATH_TMP, __progname, (u_long) getuid()); diff --git a/tmux.h b/tmux.h index 7e6dcf45..f37d5414 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.97 2007-11-23 12:48:20 nicm Exp $ */ +/* $Id: tmux.h,v 1.98 2007-11-23 17:52:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -615,13 +615,14 @@ extern volatile sig_atomic_t sigterm; #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 +extern char *default_command; +extern char *paste_buffer; extern int bell_action; -extern int prefix_key; extern int debug_level; -extern u_int status_lines; +extern int prefix_key; extern u_char status_colour; extern u_int history_limit; -extern char *default_command; +extern u_int status_lines; void usage(char **, const char *, ...); void logfile(const char *); void siginit(void); @@ -651,6 +652,7 @@ extern const struct cmd_entry cmd_list_windows_entry; extern const struct cmd_entry cmd_new_session_entry; extern const struct cmd_entry cmd_new_window_entry; extern const struct cmd_entry cmd_next_window_entry; +extern const struct cmd_entry cmd_paste_buffer_entry; extern const struct cmd_entry cmd_previous_window_entry; extern const struct cmd_entry cmd_refresh_client_entry; extern const struct cmd_entry cmd_rename_session_entry; diff --git a/window-copy.c b/window-copy.c index ce998b18..0efa72e4 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,4 +1,4 @@ -/* $Id: window-copy.c,v 1.5 2007-11-23 16:43:04 nicm Exp $ */ +/* $Id: window-copy.c,v 1.6 2007-11-23 17:52:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -29,7 +29,10 @@ void window_copy_key(struct window *, int); void window_copy_draw_position(struct window *, struct screen_draw_ctx *); -u_int window_copy_line_length(struct window *); +void window_copy_copy_selection(struct window *); +void window_copy_copy_line( + struct window *, char **, size_t *, size_t *, u_int, u_int, u_int); +u_int window_copy_find_length(struct window *, u_int); void window_copy_move_cursor(struct window *); void window_copy_cursor_left(struct window *); void window_copy_cursor_right(struct window *); @@ -143,14 +146,7 @@ window_copy_key(struct window *w, int key) switch (key) { case 'Q': case 'q': - w->mode = NULL; - xfree(w->modedata); - - w->screen.mode &= ~MODE_BACKGROUND; - - recalculate_sizes(); - server_redraw_window_all(w); - return; + goto done; case 'h': case KEYC_LEFT: window_copy_cursor_left(w); @@ -184,17 +180,131 @@ window_copy_key(struct window *w, int key) data->oy -= sy; break; case '\000': /* C-space */ + case ' ': data->selflag = !data->selflag; data->selx = data->cx + data->ox; data->sely = data->size + data->cy - data->oy; oy = -1; /* XXX */ break; - /* XXX start/end of line, next word, prev word */ + case '\027': /* C-w */ + case '\r': /* enter */ + if (data->selflag) + window_copy_copy_selection(w); + goto done; + /* XXX start/end of line, next word, prev word, cancel sel */ } if (data->oy != oy) { server_redraw_window_all(w); window_copy_move_cursor(w); } + return; + +done: + w->mode = NULL; + xfree(w->modedata); + + w->screen.mode &= ~MODE_BACKGROUND; + + recalculate_sizes(); + server_redraw_window_all(w); +} + +void +window_copy_copy_selection(struct window *w) +{ + struct window_copy_mode_data *data = w->modedata; + char *buf; + size_t len, off; + u_int i, xx, yy, sx, sy, ex, ey; + + len = BUFSIZ; + buf = xmalloc(len); + off = 0; + + *buf = '\0'; + + /* Find start and end. */ + xx = data->cx + data->ox; + yy = data->size + data->cy - data->oy; + if (xx < data->selx || (yy == data->sely && xx < data->selx)) { + sx = xx; sy = yy; + ex = data->selx; ey = data->sely; + } else { + sx = data->selx; sy = data->sely; + ex = xx; ey = yy; + } + + /* Trim ex to end of line. */ + xx = window_copy_find_length(w, ey); + if (ex > xx) + ex = xx; + + log_debug("copying from %u,%u to %u,%u", sx, sy, ex, ey); + + /* Copy the lines. */ + if (sy == ey) + window_copy_copy_line(w, &buf, &off, &len, sy, sx, ex); + else { + xx = window_copy_find_length(w, sy); + window_copy_copy_line(w, &buf, &off, &len, sy, sx, xx); + if (ey - sy > 1) { + for (i = sy + 1; i < ey - 1; i++) { + xx = window_copy_find_length(w, i); + window_copy_copy_line( + w, &buf, &off, &len, i, 0, xx); + } + } + window_copy_copy_line(w, &buf, &off, &len, ey, 0, ex); + } + + /* Terminate buffer, overwriting final \n. */ + if (off != 0) + buf[off - 1] = '\0'; + + if (paste_buffer != NULL) + xfree(paste_buffer); + paste_buffer = buf; +} + +void +window_copy_copy_line(struct window *w, + char **buf, size_t *off, size_t *len, u_int sy, u_int sx, u_int ex) +{ + struct screen *s = &w->screen; + u_char i, xx; + + if (sx > ex) + return; + + xx = window_copy_find_length(w, sy); + if (ex > xx) + ex = xx; + if (sx > xx) + sx = xx; + + if (sx < ex) { + for (i = sx; i < ex; i++) { + *buf = ensure_size(*buf, len, 1, *off + 1); + (*buf)[*off] = s->grid_data[sy][i]; + (*off)++; + } + } + + *buf = ensure_size(*buf, len, 1, *off + 1); + (*buf)[*off] = '\n'; + (*off)++; +} + +u_int +window_copy_find_length(struct window *w, u_int py) +{ + struct screen *s = &w->screen; + u_int px; + + px = s->grid_size[py]; + while (px > 0 && s->grid_data[py][px] == SCREEN_DEFDATA) + px--; + return (px); } void