diff options
-rw-r--r-- | CHANGES | 81 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | TODO | 17 | ||||
-rw-r--r-- | attributes.c | 56 | ||||
-rw-r--r-- | cmd-attach-session.c | 2 | ||||
-rw-r--r-- | cmd-break-pane.c | 2 | ||||
-rw-r--r-- | cmd-find-window.c | 6 | ||||
-rw-r--r-- | cmd-find.c | 8 | ||||
-rw-r--r-- | cmd-if-shell.c | 15 | ||||
-rw-r--r-- | cmd-list-sessions.c | 3 | ||||
-rw-r--r-- | cmd-load-buffer.c | 1 | ||||
-rw-r--r-- | cmd-new-session.c | 60 | ||||
-rw-r--r-- | cmd-refresh-client.c | 83 | ||||
-rw-r--r-- | cmd-resize-pane.c | 18 | ||||
-rw-r--r-- | cmd-resize-window.c | 109 | ||||
-rw-r--r-- | cmd-run-shell.c | 33 | ||||
-rw-r--r-- | cmd-select-pane.c | 35 | ||||
-rw-r--r-- | cmd-set-option.c | 15 | ||||
-rw-r--r-- | cmd-show-messages.c | 22 | ||||
-rw-r--r-- | cmd-split-window.c | 2 | ||||
-rw-r--r-- | cmd-string.c | 39 | ||||
-rw-r--r-- | cmd-swap-pane.c | 2 | ||||
-rw-r--r-- | cmd-switch-client.c | 1 | ||||
-rw-r--r-- | cmd.c | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | format.c | 99 | ||||
-rw-r--r-- | grid.c | 16 | ||||
-rw-r--r-- | input-keys.c | 4 | ||||
-rw-r--r-- | input.c | 164 | ||||
-rw-r--r-- | job.c | 102 | ||||
-rw-r--r-- | key-bindings.c | 7 | ||||
-rw-r--r-- | key-string.c | 6 | ||||
-rw-r--r-- | layout-custom.c | 2 | ||||
-rw-r--r-- | layout-set.c | 8 | ||||
-rw-r--r-- | layout.c | 74 | ||||
-rw-r--r-- | options-table.c | 37 | ||||
-rw-r--r-- | resize.c | 315 | ||||
-rw-r--r-- | screen-redraw.c | 382 | ||||
-rw-r--r-- | screen-write.c | 150 | ||||
-rw-r--r-- | server-client.c | 246 | ||||
-rw-r--r-- | server-fn.c | 9 | ||||
-rw-r--r-- | server.c | 27 | ||||
-rw-r--r-- | session.c | 19 | ||||
-rw-r--r-- | status.c | 99 | ||||
-rw-r--r-- | tmux.1 | 174 | ||||
-rw-r--r-- | tmux.h | 177 | ||||
-rw-r--r-- | tty-keys.c | 103 | ||||
-rw-r--r-- | tty-term.c | 52 | ||||
-rw-r--r-- | tty.c | 533 | ||||
-rw-r--r-- | window-copy.c | 38 | ||||
-rw-r--r-- | window-tree.c | 1 | ||||
-rw-r--r-- | window.c | 61 |
52 files changed, 2512 insertions, 1010 deletions
@@ -1,3 +1,74 @@ +CHANGES FROM 2.8 to master + +* Do not move the cursor in copy mode when the mouse wheel is used. + +* Use the same working directory rules for jobs as new windows rather + than always starting in the user's home. + +* Allow panes to be one line or column in size. + +* Go to last line when goto-line number is out of range in copy mode. + +* Yank previously cut text if any with C-y in the command prompt, only + use the buffer if no text has been cut. + +* Add q: format modifier to quote shell special characters. + +* Add StatusLeft and StatusRight mouse locations (keys such as + MouseDown1StatusLeft) for the status-left and status-right areas of + the status line. + +* Add -Z to find-window. + +* Support for windows larger than the client. This adds two new + options, window-size and default-size, and a new command, + resize-window. The force-width and force-height options and the + session_width and session_height formats have been removed. + + The new window-size option tells tmux how to work out the size of + windows: largest means it picks the size of the largest session, + smallest the smallest session (similar to the old behaviour) and + manual means that it does not automatically resize + windows. aggressive-resize modifies the choice of session for + largest and smallest as it did before. + + If a window is in a session attached to a client that is too small, + only part of the window is shown. tmux attempts to keep the cursor + visible, so the part of the window displayed is changed as the + cursor moves (with a small delay, to try and avoid excess redrawing + when applications redraw status lines or similar that are not + currently visible). + + Drawing windows which are larger than the client is not as efficient + as those which fit, particularly when the cursor moves, so it is + recommended to avoid using this on slow machines or networks (set + window-size to smallest or manual). + + The resize-window command can be used to resize a window + manually. If it is used, the window-size option is automatically set + to manual for the window (undo this with "setw -u + window-size"). resize-window works in a similar way to resize-pane + (-U -D -L -R -x -y flags) but also has -a and -A flags. -a sets the + window to the size of the smallest client (what it would be if + window-size was smallest) and -A the largest. + + For the same behaviour as force-width or force-height, use + resize-window -x or -y. + + If the global window-size option is set to manual, the default-size + option is used for new windows. If -x or -y is used with + new-session, that sets the default-size option for the new session. + + The maximum size of a window is 10000x10000. But expect applications + to complain and higher memory use if making a window that big. The + minimum size is the size required for the current layout including + borders. + + The refresh-client command can be used to pan around a window, -U -D + -L -R moves up, down, left or right and -c returns to automatic + cursor tracking. The position is reset when the current window is + changed. + CHANGES FROM 2.7 to 2.8 * Make display-panes block the client until a pane is chosen or it @@ -291,7 +362,7 @@ CHANGES FROM 2.4 TO 2.5, 09 May 2017 * Do not redraw a client unless we realistically think it can accept the data - defer redraws until the client has nothing else waiting to write. - + CHANGES FROM 2.3 TO 2.4, 20 April 2017 Incompatible Changes @@ -577,7 +648,7 @@ Normal Changes ============== * Fix crash due to uninitialized lastwp member of layout_cell -* Fix -fg/-bg/-style with 256 colour terminals. +* Fix -fg/-bg/-style with 256 colour terminals. CHANGES FROM 1.8 TO 1.9, 20 February 2014 @@ -935,7 +1006,7 @@ CHANGES FROM 1.2 TO 1.3, 18 July 2010 * Run job commands explicitly in the global environment (which can be modified with setenv -g), rather than with the environment tmux started with. * Use the machine's hostname as the default title, instead of an empty string. -* Prevent double free if the window option remain-on-exit is set. +* Prevent double free if the window option remain-on-exit is set. * Key string conversions rewritten. * Mark zombie windows as dead in the choose-window list. * Tiled layout added. @@ -1037,7 +1108,7 @@ CHANGES FROM 1.0 TO 1.1, 05 November 2009 * New lock-client (alias lockc), and lock-session (alias locks) commands to lock a particular client, or all clients attached to a session. * Support C-n/C-p/C-v/M-v with emacs keys in choice mode. -* Use : for goto line rather than g in vi mode. +* Use : for goto line rather than g in vi mode. * Try to guess which client to use when no target client was specified. Finds the current session, and if only one client is present, use it. Otherwise, return the most recently used client. @@ -1168,7 +1239,7 @@ The list of older changes is below. * main-horizontal layout and main-pane-height option to match vertical. * New window option main-pane-width to set the width of the large left pane with - main-vertical (was left-vertical) layout. + main-vertical (was left-vertical) layout. * Lots of layout cleanup. manual layout is now manual-vertical. 16 May 2009 diff --git a/Makefile.am b/Makefile.am index a57baa42..8519c47c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -100,6 +100,7 @@ dist_tmux_SOURCES = \ cmd-rename-session.c \ cmd-rename-window.c \ cmd-resize-pane.c \ + cmd-resize-window.c \ cmd-respawn-pane.c \ cmd-respawn-window.c \ cmd-rotate-window.c \ @@ -195,7 +196,7 @@ install-exec-hook: >$(srcdir)/tmux.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ - $(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ + $(AWK) -f $(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ @@ -16,13 +16,11 @@ * way to set socket path from config file - format improvements: - * option to quote format (#{q:session_name}) * some way to pad # stuff with spaces * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple attached sessions? * comparison operators like < and > (for #{version}?) - * %else statement in config file - improve monitor-*: * straighten out rules for multiple clients @@ -50,6 +48,10 @@ dragging it should select by word. compare how xterm works. GitHub issue 682) * key to search for word under cursor (GitHub issue 1240) + * when entering copy mode, should copy grid so that input does not + need to be suspended + * allow the prefix for automatic buffers to be specified as part of the + key binding to allow session buffers or similar (GitHub issue 1501) - layout stuff * way to tag a layout as a number/name @@ -61,7 +63,6 @@ not attached to a cell at all. this could be the time to introduce panelink to replace layout_cell * way to set hints/limits about pane size for resizing - * panning over window (window larger than visible) * a mode where one application can cross two panes (ie x|y, width = COLUMNS/2 but height = ROWS * 2) * separate active panes for different clients @@ -109,11 +110,13 @@ * marks in history, automatically add (move?) one when pane is changed * this doesn't work, need pane reference count probably: bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window - * respawn -c flag same as neww etc * marker lines in history (GitHub issue 1042) * tree mode stuff: make command prompt (:) common code so all modes get it, predefined filters, tag-all key, ... * drag panes and windows around to move/swap them in choose mode + * flag to specify environment to new-window, split-window, + new-session (issue 1498) + * multiple column panes (issue 1503) - hooks * more hooks for various things @@ -131,3 +134,9 @@ * finish hooks for notifys * for session_closed, if no sessions at all, perhaps fake up a temporary one + +- pan + * tty_window_offset should try to keep as much off active pane + visible as possible + * rather than centering cursor it might be better if only + moved offset when it gets close to an edge? diff --git a/attributes.c b/attributes.c index 1e45e584..1b831733 100644 --- a/attributes.c +++ b/attributes.c @@ -25,13 +25,13 @@ const char * attributes_tostring(int attr) { - static char buf[128]; + static char buf[512]; size_t len; if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", @@ -39,7 +39,11 @@ attributes_tostring(int attr) (attr & GRID_ATTR_REVERSE) ? "reverse," : "", (attr & GRID_ATTR_HIDDEN) ? "hidden," : "", (attr & GRID_ATTR_ITALICS) ? "italics," : "", - (attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : ""); + (attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : "", + (attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "", + (attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "", + (attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "", + (attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : ""); if (len > 0) buf[len - 1] = '\0'; @@ -52,6 +56,25 @@ attributes_fromstring(const char *str) const char delimiters[] = " ,|"; int attr; size_t end; + u_int i; + struct { + const char* name; + int attr; + } table[] = { + { "bright", GRID_ATTR_BRIGHT }, + { "bold", GRID_ATTR_BRIGHT }, + { "dim", GRID_ATTR_DIM }, + { "underscore", GRID_ATTR_UNDERSCORE }, + { "blink", GRID_ATTR_BLINK }, + { "reverse", GRID_ATTR_REVERSE }, + { "hidden", GRID_ATTR_HIDDEN }, + { "italics", GRID_ATTR_ITALICS }, + { "strikethrough", GRID_ATTR_STRIKETHROUGH }, + { "double-underscore", GRID_ATTR_UNDERSCORE_2 }, + { "curly-underscore", GRID_ATTR_UNDERSCORE_3 }, + { "dotted-underscore", GRID_ATTR_UNDERSCORE_4 }, + { "dashed-underscore", GRID_ATTR_UNDERSCORE_5 } + }; if (*str == '\0' || strcspn(str, delimiters) == 0) return (-1); @@ -64,24 +87,15 @@ attributes_fromstring(const char *str) attr = 0; do { end = strcspn(str, delimiters); - if ((end == 6 && strncasecmp(str, "bright", end) == 0) || - (end == 4 && strncasecmp(str, "bold", end) == 0)) - attr |= GRID_ATTR_BRIGHT; - else if (end == 3 && strncasecmp(str, "dim", end) == 0) - attr |= GRID_ATTR_DIM; - else if (end == 10 && strncasecmp(str, "underscore", end) == 0) - attr |= GRID_ATTR_UNDERSCORE; - else if (end == 5 && strncasecmp(str, "blink", end) == 0) - attr |= GRID_ATTR_BLINK; - else if (end == 7 && strncasecmp(str, "reverse", end) == 0) - attr |= GRID_ATTR_REVERSE; - else if (end == 6 && strncasecmp(str, "hidden", end) == 0) - attr |= GRID_ATTR_HIDDEN; - else if (end == 7 && strncasecmp(str, "italics", end) == 0) - attr |= GRID_ATTR_ITALICS; - else if (end == 13 && strncasecmp(str, "strikethrough", end) == 0) - attr |= GRID_ATTR_STRIKETHROUGH; - else + for (i = 0; i < nitems(table); i++) { + if (end != strlen(table[i].name)) + continue; + if (strncasecmp(str, table[i].name, end) == 0) { + attr |= table[i].attr; + break; + } + } + if (i == nitems(table)) return (-1); str += end + strspn(str + end, delimiters); } while (*str != '\0'); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 0db0d855..73ff530d 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -115,6 +115,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, c->session = s; if (~item->shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); + tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); @@ -142,6 +143,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, c->session = s; server_client_set_key_table(c, NULL); + tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 74ecce6f..3b929dee 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -76,7 +76,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) window_lost_pane(w, wp); layout_close_pane(wp); - w = wp->window = window_create(dst_s->sx, dst_s->sy); + w = wp->window = window_create(w->sx, w->sy); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; diff --git a/cmd-find-window.c b/cmd-find-window.c index 22a54195..c2d230a5 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = { .name = "find-window", .alias = "findw", - .args = { "CNt:T", 1, 1 }, - .usage = "[-CNT] " CMD_TARGET_PANE_USAGE " match-string", + .args = { "CNt:TZ", 1, 1 }, + .usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string", .target = { 't', CMD_FIND_PANE, 0 }, @@ -83,6 +83,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s); new_args = args_parse("", 1, &argv); + if (args_has(args, 'Z')) + args_set(new_args, 'Z', NULL); args_set(new_args, 'f', filter); window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args); @@ -135,7 +135,7 @@ cmd_find_best_client(struct session *s) { struct client *c_loop, *c; - if (s->flags & SESSION_UNATTACHED) + if (s->attached == 0) s = NULL; c = NULL; @@ -159,10 +159,10 @@ cmd_find_session_better(struct session *s, struct session *than, int flags) if (than == NULL) return (1); if (flags & CMD_FIND_PREFER_UNATTACHED) { - attached = (~than->flags & SESSION_UNATTACHED); - if (attached && (s->flags & SESSION_UNATTACHED)) + attached = (than->attached != 0); + if (attached && s->attached == 0) return (1); - else if (!attached && (~s->flags & SESSION_UNATTACHED)) + else if (!attached && s->attached != 0) return (0); } return (timercmp(&s->activity_time, &than->activity_time, >)); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index d7ce3039..480912df 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -120,8 +120,13 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->item = NULL; memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse); - job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, - cmd_if_shell_callback, cmd_if_shell_free, cdata, 0); + if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, + cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { + cmdq_error(item, "failed to run command: %s", shellcmd); + free(shellcmd); + free(cdata); + return (CMD_RETURN_ERROR); + } free(shellcmd); if (args_has(args, 'b')) @@ -132,14 +137,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) static void cmd_if_shell_callback(struct job *job) { - struct cmd_if_shell_data *cdata = job->data; + struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; struct cmd_list *cmdlist; struct cmdq_item *new_item; char *cause, *cmd, *file = cdata->file; u_int line = cdata->line; + int status; - if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) + status = job_get_status(job); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) cmd = cdata->cmd_else; else cmd = cdata->cmd_if; diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index df8a25bc..72ff47e8 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -30,8 +30,7 @@ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ - "(created #{t:session_created}) " \ - "[#{session_width}x#{session_height}]" \ + "(created #{t:session_created})" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ "#{?session_attached, (attached),}" diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index b7c299b7..47cb0ca2 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -87,6 +87,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) if (error != 0) { cmdq_error(item, "-: %s", cause); free(cause); + free(cdata); return (CMD_RETURN_ERROR); } return (CMD_RETURN_WAIT); diff --git a/cmd-new-session.c b/cmd-new-session.c index e809de24..162a50bd 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -71,14 +71,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct session *s, *as, *groupwith; struct window *w; struct environ *env; + struct options *oo; struct termios tio, *tiop; struct session_group *sg; const char *errstr, *template, *group, *prefix; - const char *path, *cmd, *tmp; + const char *path, *cmd, *tmp, *value; char **argv, *cause, *cp, *newname, *cwd = NULL; int detached, already_attached, idx, argc; int is_control = 0; - u_int sx, sy; + u_int sx, sy, dsx = 80, dsy = 24; struct environ_entry *envent; struct cmd_find_state fs; enum cmd_retval retval; @@ -189,44 +190,53 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } } - /* Find new session size. */ - if (!detached) { - sx = c->tty.sx; - sy = c->tty.sy; - if (!is_control && - sy > 0 && - options_get_number(global_s_options, "status")) - sy--; - } else { - sx = 80; - sy = 24; - } - if ((is_control || detached) && args_has(args, 'x')) { + /* Get default session size. */ + if (args_has(args, 'x')) { tmp = args_get(args, 'x'); if (strcmp(tmp, "-") == 0) { if (c != NULL) - sx = c->tty.sx; + dsx = c->tty.sx; } else { - sx = strtonum(tmp, 1, USHRT_MAX, &errstr); + dsx = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "width %s", errstr); goto error; } } } - if ((is_control || detached) && args_has(args, 'y')) { + if (args_has(args, 'y')) { tmp = args_get(args, 'y'); if (strcmp(tmp, "-") == 0) { if (c != NULL) - sy = c->tty.sy; + dsy = c->tty.sy; } else { - sy = strtonum(tmp, 1, USHRT_MAX, &errstr); + dsy = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "height %s", errstr); goto error; } } } + + /* Find new session size. */ + if (!detached && !is_control) { + sx = c->tty.sx; + sy = c->tty.sy; + if (!is_control && + sy > 0 && + options_get_number(global_s_options, "status")) + sy--; + } else { + value = options_get_string(global_s_options, "default-size"); + if (sscanf(value, "%ux%u", &sx, &sy) != 2) { + sx = 80; + sy = 24; + } + if (args_has(args, 'x')) + sx = dsx; + if (args_has(args, 'y')) + sy = dsy; + } if (sx == 0) sx = 1; if (sy == 0) @@ -262,10 +272,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); + /* Set up the options. */ + oo = options_create(global_s_options); + if (args_has(args, 'x') || args_has(args, 'y')) + options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy); + /* Create the new session. */ idx = -1 - options_get_number(global_s_options, "base-index"); - s = session_create(prefix, newname, argc, argv, path, cwd, env, tiop, - idx, sx, sy, &cause); + s = session_create(prefix, newname, argc, argv, path, cwd, env, oo, + tiop, idx, &cause); environ_free(env); if (s == NULL) { cmdq_error(item, "create session failed: %s", cause); @@ -313,6 +328,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) c->session = s; if (~item->shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); + tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index df1d2135..e5ae099f 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -18,6 +18,8 @@ #include <sys/types.h> +#include <stdlib.h> + #include "tmux.h" /* @@ -31,8 +33,8 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "C:St:", 0, 0 }, - .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, + .args = { "cC:DlLRSt:U", 0, 1 }, + .usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]", .flags = CMD_AFTERHOOK, .exec = cmd_refresh_client_exec @@ -43,23 +45,80 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; - const char *size; - u_int w, h; + struct tty *tty; + struct window *w; + const char *size, *errstr; + u_int x, y, adjust; if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); + tty = &c->tty; + + if (args_has(args, 'c') || + args_has(args, 'L') || + args_has(args, 'R') || + args_has(args, 'U') || + args_has(args, 'D')) + { + if (args->argc == 0) + adjust = 1; + else { + adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); + if (errstr != NULL) { + cmdq_error(item, "adjustment %s", errstr); + return (CMD_RETURN_ERROR); + } + } + + if (args_has(args, 'c')) + c->pan_window = NULL; + else { + w = c->session->curw->window; + if (c->pan_window != w) { + c->pan_window = w; + c->pan_ox = tty->oox; + c->pan_oy = tty->ooy; + } + if (args_has(args, 'L')) { + if (c->pan_ox > adjust) + c->pan_ox -= adjust; + else + c->pan_ox = 0; + } else if (args_has(args, 'R')) { + c->pan_ox += adjust; + if (c->pan_ox > w->sx - tty->osx) + c->pan_ox = w->sx - tty->osx; + } else if (args_has(args, 'U')) { + if (c->pan_oy > adjust) + c->pan_oy -= adjust; + else + c->pan_oy = 0; + } else if (args_has(args, 'D')) { + c->pan_oy += adjust; + if (c->pan_oy > w->sy - tty->osy) + c->pan_oy = w->sy - tty->osy; + } + } + tty_update_client_offset(c); + server_redraw_client(c); + return (CMD_RETURN_NORMAL); + } - if (args_has(args, 'C')) { + if (args_has(args, 'l')) { + if (c->session != NULL) + tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); + } else if (args_has(args, 'C')) { if ((size = args_get(args, 'C')) == NULL) { cmdq_error(item, "missing size"); return (CMD_RETURN_ERROR); } - if (sscanf(size, "%u,%u", &w, &h) != 2) { + if (sscanf(size, "%u,%u", &x, &y) != 2 && + sscanf(size, "%ux%u", &x, &y)) { cmdq_error(item, "bad size argument"); return (CMD_RETURN_ERROR); } - if (w < PANE_MINIMUM || w > 5000 || - h < PANE_MINIMUM || h > 5000) { + if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || + y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { cmdq_error(item, "size too small or too big"); return (CMD_RETURN_ERROR); } @@ -67,16 +126,18 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } - tty_set_size(&c->tty, w, h); + tty_set_size(&c->tty, x, y); c->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); - } else if (args_has(args, 'S')) { + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 'S')) { c->flags |= CLIENT_STATUSFORCE; server_status_client(c); } else { c->flags |= CLIENT_STATUSFORCE; server_redraw_client(c); } - return (CMD_RETURN_NORMAL); } diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 6b991aa6..31474f3f 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -91,9 +91,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } } - if (args_has(self->args, 'x')) { - x = args_strtonum(self->args, 'x', PANE_MINIMUM, INT_MAX, - &cause); + if (args_has(args, 'x')) { + x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "width %s", cause); free(cause); @@ -101,9 +100,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) } layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); } - if (args_has(self->args, 'y')) { - y = args_strtonum(self->args, 'y', PANE_MINIMUM, INT_MAX, - &cause); + if (args_has(args, 'y')) { + y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "height %s", cause); free(cause); @@ -112,13 +110,13 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } - if (args_has(self->args, 'L')) + if (args_has(args, 'L')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust, 1); - else if (args_has(self->args, 'R')) + else if (args_has(args, 'R')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust, 1); - else if (args_has(self->args, 'U')) + else if (args_has(args, 'U')) layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust, 1); - else if (args_has(self->args, 'D')) + else if (args_has(args, 'D')) layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust, 1); server_redraw_window(wl->window); diff --git a/cmd-resize-window.c b/cmd-resize-window.c new file mode 100644 index 00000000..d780b6ee --- /dev/null +++ b/cmd-resize-window.c @@ -0,0 +1,109 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com> + * + * 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 <sys/types.h> + +#include <stdlib.h> + +#include "tmux.h" + +/* + * Increase or decrease window size. + */ + +static enum cmd_retval cmd_resize_window_exec(struct cmd *, + struct cmdq_item *); + +const struct cmd_entry cmd_resize_window_entry = { + .name = "resize-window", + .alias = "resizew", + + .args = { "aADLRt:Ux:y:", 0, 1 }, + .usage = "[-aADLRU] [-x width] [-y height] " CMD_TARGET_WINDOW_USAGE " " + "[adjustment]", + + .target = { 't', CMD_FIND_WINDOW, 0 }, + + .flags = CMD_AFTERHOOK, + .exec = cmd_resize_window_exec +}; + +static enum cmd_retval +cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = self->args; + struct winlink *wl = item->target.wl; + struct window *w = wl->window; + struct session *s = item->target.s; + const char *errstr; + char *cause; + u_int adjust, sx, sy; + + if (args->argc == 0) + adjust = 1; + else { + adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); + if (errstr != NULL) { + cmdq_error(item, "adjustment %s", errstr); + return (CMD_RETURN_ERROR); + } + } + + sx = w->sx; + sy = w->sy; + + if (args_has(args, 'x')) { + sx = args_strtonum(args, 'x', WINDOW_MINIMUM, WINDOW_MAXIMUM, + &cause); + if (cause != NULL) { + cmdq_error(item, "width %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + if (args_has(args, 'y')) { + sy = args_strtonum(args, 'y', WINDOW_MINIMUM, WINDOW_MAXIMUM, + &cause); + if (cause != NULL) { + cmdq_error(item, "height %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + + if (args_has(args, 'L')) { + if (sx >= adjust) + sx -= adjust; + } else if (args_has(args, 'R')) + sx += adjust; + else if (args_has(args, 'U')) { + if (sy >= adjust) + sy -= adjust; + } else if (args_has(args, 'D')) + sy += adjust; + + if (args_has(args, 'A')) + default_window_size(s, w, &sx, &sy, WINDOW_SIZE_LARGEST); + else if (args_has(args, 'a')) + default_window_size(s, w, &sx, &sy, WINDOW_SIZE_SMALLEST); + + options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL); + resize_window(w, sx, sy); + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd-run-shell.c b/cmd-run-shell.c index a9961988..47fceafe 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -57,7 +57,7 @@ struct cmd_run_shell_data { static void cmd_run_shell_print(struct job *job, const char *msg) { - struct cmd_run_shell_data *cdata = job->data; + struct cmd_run_shell_data *cdata = job_get_data(job); struct window_pane *wp = NULL; struct cmd_find_state fs; @@ -102,8 +102,12 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL, - cmd_run_shell_callback, cmd_run_shell_free, cdata, 0); + if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL, + cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) { + cmdq_error(item, "failed to run command: %s", cdata->cmd); + free(cdata); + return (CMD_RETURN_ERROR); + } if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); @@ -113,22 +117,23 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) static void cmd_run_shell_callback(struct job *job) { - struct cmd_run_shell_data *cdata = job->data; - char *cmd = cdata->cmd, *msg, *line; + struct cmd_run_shell_data *cdata = job_get_data(job); + struct bufferevent *event = job_get_event(job); + char *cmd = cdata->cmd, *msg = NULL, *line; size_t size; - int retcode; + int retcode, status; do { - if ((line = evbuffer_readline(job->event->input)) != NULL) { + if ((line = evbuffer_readline(event->input)) != NULL) { cmd_run_shell_print(job, line); free(line); } } while (line != NULL); - size = EVBUFFER_LENGTH(job->event->input); + size = EVBUFFER_LENGTH(event->input); if (size != 0) { line = xmalloc(size + 1); - memcpy(line, EVBUFFER_DATA(job->event->input), size); + memcpy(line, EVBUFFER_DATA(event->input), size); line[size] = '\0'; cmd_run_shell_print(job, line); @@ -136,12 +141,12 @@ cmd_run_shell_callback(struct job *job) free(line); } - msg = NULL; - if (WIFEXITED(job->status)) { - if ((retcode = WEXITSTATUS(job->status)) != 0) + status = job_get_status(job); + if (WIFEXITED(status)) { + if ((retcode = WEXITSTATUS(status)) != 0) xasprintf(&msg, "'%s' returned %d", cmd, retcode); - } else if (WIFSIGNALED(job->status)) { - retcode = WTERMSIG(job->status); + } else if (WIFSIGNALED(status)) { + retcode = WTERMSIG(status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } if (msg != NULL) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index a2345fe1..90ca46ba 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -54,6 +54,31 @@ const struct cmd_entry cmd_last_pane_entry = { .exec = cmd_select_pane_exec }; +static void +cmd_select_pane_redraw(struct window *w) +{ + struct client *c; + + /* + * Redraw entire window if it is bigger than the client (the + * offset may change), otherwise just draw borders. + */ + + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL) + continue; + if (c->session->curw->window == w && tty_window_bigger(&c->tty)) + server_redraw_client(c); + else { + if (c->session->curw->window == w) + c->flags |= CLIENT_REDRAWBORDERS; + if (session_has(c->session, w)) + c->flags |= CLIENT_REDRAWSTATUS; + } + + } +} + static enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { @@ -87,8 +112,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp)) { cmd_find_from_winlink(current, wl, 0); - server_status_window(w); - server_redraw_window_borders(w); + cmd_select_pane_redraw(w); } } return (CMD_RETURN_NORMAL); @@ -168,16 +192,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (wp == w->active) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); - if (!window_pane_visible(wp)) { - cmdq_error(item, "pane not visible"); - return (CMD_RETURN_ERROR); - } window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp)) { cmd_find_from_winlink_pane(current, wl, wp, 0); hooks_insert(s->hooks, item, current, "after-select-pane"); - server_status_window(w); - server_redraw_window_borders(w); + cmd_select_pane_redraw(w); } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-option.c b/cmd-set-option.c index bdc42cae..c4b82004 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -18,6 +18,7 @@ #include <sys/types.h> +#include <fnmatch.h> #include <stdlib.h> #include <string.h> @@ -260,7 +261,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) } if (strcmp(name, "pane-border-status") == 0) { RB_FOREACH(w, windows, &windows) - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); } RB_FOREACH(s, sessions, &sessions) status_update_saved(s); @@ -297,7 +298,8 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, int append = args_has(args, 'a'); struct options_entry *o; long long number; - const char *errstr; + const char *errstr, *new; + char *old; key_code key; oe = options_table_entry(parent); @@ -310,7 +312,16 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, switch (oe->type) { case OPTIONS_TABLE_STRING: + old = xstrdup(options_get_string(oo, oe->name)); options_set_string(oo, oe->name, append, "%s", value); + new = options_get_string(oo, oe->name); + if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { + options_set_string(oo, oe->name, 0, "%s", old); + free(old); + cmdq_error(item, "value is invalid: %s", value); + return (-1); + } + free(old); return (0); case OPTIONS_TABLE_NUMBER: number = strtonum(value, oe->minimum, oe->maximum, &errstr); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 21511b48..8da12374 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -43,7 +43,6 @@ const struct cmd_entry cmd_show_messages_entry = { }; static int cmd_show_messages_terminals(struct cmdq_item *, int); -static int cmd_show_messages_jobs(struct cmdq_item *, int); static int cmd_show_messages_terminals(struct cmdq_item *item, int blank) @@ -66,25 +65,6 @@ cmd_show_messages_terminals(struct cmdq_item *item, int blank) return (n != 0); } -static int -cmd_show_messages_jobs(struct cmdq_item *item, int blank) -{ - struct job *job; - u_int n; - - n = 0; - LIST_FOREACH(job, &all_jobs, entry) { - if (blank) { - cmdq_print(item, "%s", ""); - blank = 0; - } - cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]", - n, job->cmd, job->fd, (long)job->pid, job->status); - n++; - } - return (n != 0); -} - static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { @@ -103,7 +83,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) done = 1; } if (args_has(args, 'J')) { - cmd_show_messages_jobs(item, blank); + job_print_summary(item, blank); done = 1; } if (done) diff --git a/cmd-split-window.c b/cmd-split-window.c index 378576ff..f5dde751 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -147,7 +147,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } environ_free(env); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); server_redraw_window(w); if (!args_has(args, 'd')) { diff --git a/cmd-string.c b/cmd-string.c index 9c8f10c1..440a0231 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -54,6 +54,22 @@ cmd_string_ungetc(size_t *p) (*p)--; } +static int +cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch) +{ + int size = (ch == 'u') ? 4 : 8; + u_int tmp; + + if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1) + return (-1); + if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1) + return (-1); + *p += size; + + *wc = (wchar_t)tmp; + return (0); +} + int cmd_string_split(const char *s, int *rargc, char ***rargv) { @@ -191,12 +207,11 @@ cmd_string_copy(char **dst, char *src, size_t *len) static char * cmd_string_string(const char *s, size_t *p, char endch, int esc) { - int ch; - char *buf, *t; - size_t len; - - buf = NULL; - len = 0; + int ch; + wchar_t wc; + struct utf8_data ud; + char *buf = NULL, *t; + size_t len = 0; while ((ch = cmd_string_getc(s, p)) != endch) { switch (ch) { @@ -220,6 +235,18 @@ cmd_string_string(const char *s, size_t *p, char endch, int esc) case 't': ch = '\t'; break; + case 'u': + case 'U': + if (cmd_string_unicode(&wc, s, p, ch) != 0) + goto error; + if (utf8_split(wc, &ud) != UTF8_DONE) + goto error; + if (len >= SIZE_MAX - ud.size - 1) + goto error; + buf = xrealloc(buf, len + ud.size); + memcpy(buf + len, ud.data, ud.size); + len += ud.size; + continue; } break; case '$': diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 7283bf53..1de272c4 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -105,8 +105,6 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) window_set_active_pane(dst_w, src_wp); } else { tmp_wp = dst_wp; - if (!window_pane_visible(tmp_wp)) - tmp_wp = src_wp; window_set_active_pane(src_w, tmp_wp); } } else { diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 180635df..6181073d 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -128,6 +128,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) c->session = s; if (~item->shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); + tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); @@ -80,6 +80,7 @@ extern const struct cmd_entry cmd_refresh_client_entry; extern const struct cmd_entry cmd_rename_session_entry; extern const struct cmd_entry cmd_rename_window_entry; extern const struct cmd_entry cmd_resize_pane_entry; +extern const struct cmd_entry cmd_resize_window_entry; extern const struct cmd_entry cmd_respawn_pane_entry; extern const struct cmd_entry cmd_respawn_window_entry; extern const struct cmd_entry cmd_rotate_window_entry; @@ -166,6 +167,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_rename_session_entry, &cmd_rename_window_entry, &cmd_resize_pane_entry, + &cmd_resize_window_entry, &cmd_respawn_pane_entry, &cmd_respawn_window_entry, &cmd_rotate_window_entry, diff --git a/configure.ac b/configure.ac index 2d9fad77..5b4bc4a2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT(tmux, 2.8) +AC_INIT(tmux, master) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) @@ -52,8 +52,7 @@ static int format_replace(struct format_tree *, const char *, size_t, static void format_defaults_session(struct format_tree *, struct session *); static void format_defaults_client(struct format_tree *, struct client *); -static void format_defaults_winlink(struct format_tree *, - struct winlink *); +static void format_defaults_winlink(struct format_tree *, struct winlink *); /* Entry in format job tree. */ struct format_job { @@ -94,6 +93,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_BASENAME 0x2 #define FORMAT_DIRNAME 0x4 #define FORMAT_SUBSTITUTE 0x8 +#define FORMAT_QUOTE 0x10 /* Entry in format tree. */ struct format_entry { @@ -106,9 +106,10 @@ struct format_entry { /* Format entry tree. */ struct format_tree { - struct window *w; - struct winlink *wl; + struct client *c; struct session *s; + struct winlink *wl; + struct window *w; struct window_pane *wp; struct client *client; @@ -191,8 +192,8 @@ static const char *format_lower[] = { static void format_job_update(struct job *job) { - struct format_job *fj = job->data; - struct evbuffer *evb = job->event->input; + struct format_job *fj = job_get_data(job); + struct evbuffer *evb = job_get_event(job)->input; char *line = NULL, *next; time_t t; @@ -221,18 +222,19 @@ format_job_update(struct job *job) static void format_job_complete(struct job *job) { - struct format_job *fj = job->data; + struct format_job *fj = job_get_data(job); + struct evbuffer *evb = job_get_event(job)->input; char *line, *buf; size_t len; fj->job = NULL; buf = NULL; - if ((line = evbuffer_readline(job->event->input)) == NULL) { - len = EVBUFFER_LENGTH(job->event->input); + if ((line = evbuffer_readline(evb)) == NULL) { + len = EVBUFFER_LENGTH(evb); buf = xmalloc(len + 1); if (len != 0) - memcpy(buf, EVBUFFER_DATA(job->event->input), len); + memcpy(buf, EVBUFFER_DATA(evb), len); buf[len] = '\0'; } else buf = line; @@ -295,7 +297,8 @@ format_job_get(struct format_tree *ft, const char *cmd) t = time(NULL); if (fj->job == NULL && (force || fj->last != t)) { - fj->job = job_run(expanded, NULL, NULL, format_job_update, + fj->job = job_run(expanded, NULL, + server_client_get_cwd(ft->client, NULL), format_job_update, format_job_complete, NULL, fj, JOB_NOWAIT); if (fj->job == NULL) { free(fj->out); @@ -769,6 +772,23 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) fe->value = NULL; } +/* Quote special characters in string. */ +static char * +format_quote(const char *s) +{ + const char *cp; + char *out, *at; + + at = out = xmalloc(strlen(s) * 2 + 1); + for (cp = s; *cp != '\0'; cp++) { + if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL) + *at++ = '\\'; + *at++ = *cp; + } + *at = '\0'; + return (out); +} + /* Find a format entry. */ static char * format_find(struct format_tree *ft, const char *key, int modifiers) @@ -851,6 +871,11 @@ found: copy = xstrdup(dirname(saved)); free(saved); } + if (modifiers & FORMAT_QUOTE) { + saved = copy; + copy = xstrdup(format_quote(saved)); + free(saved); + } return (copy); } @@ -991,6 +1016,12 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, modifiers |= FORMAT_TIMESTRING; copy += 2; break; + case 'q': + if (copy[1] != ':') + break; + modifiers |= FORMAT_QUOTE; + copy += 2; + break; case 's': sep = copy[1]; if (sep == ':' || !ispunct((u_char)sep)) @@ -1072,8 +1103,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, found = xstrdup(""); } } - if (format_choose(ptr + 1, &left, &right) != 0) + if (format_choose(ptr + 1, &left, &right) != 0) { + free(found); goto fail; + } if (format_true(found)) value = format_expand(ft, left); @@ -1330,8 +1363,6 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_name", "%s", s->name); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); - format_add(ft, "session_width", "%u", s->sx); - format_add(ft, "session_height", "%u", s->sy); format_add(ft, "session_id", "$%u", s->id); sg = session_group_contains(s); @@ -1366,6 +1397,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) if (ft->s == NULL) ft->s = c->session; + ft->c = c; format_add(ft, "client_name", "%s", c->name); format_add(ft, "client_pid", "%ld", (long) c->pid); @@ -1434,8 +1466,11 @@ format_defaults_window(struct format_tree *ft, struct window *w) static void format_defaults_winlink(struct format_tree *ft, struct winlink *wl) { + struct client *c = ft->c; struct session *s = wl->session; struct window *w = wl->window; + int flag; + u_int ox, oy, sx, sy; if (ft->w == NULL) ft->w = wl->window; @@ -1443,6 +1478,15 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_defaults_window(ft, w); + if (c != NULL) { + flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + format_add(ft, "window_bigger", "%d", flag); + if (flag) { + format_add(ft, "window_offset_x", "%u", ox); + format_add(ft, "window_offset_y", "%u", oy); + } + } + format_add(ft, "window_index", "%d", wl->idx); format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); format_add(ft, "window_flags", "%s", window_printable_flags(wl)); @@ -1463,12 +1507,13 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) void format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { + struct window *w = wp->window; struct grid *gd = wp->base.grid; int status = wp->status; u_int idx; if (ft->w == NULL) - ft->w = wp->window; + ft->w = w; ft->wp = wp; format_add(ft, "history_size", "%u", gd->hsize); @@ -1483,7 +1528,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_title", "%s", wp->base.title); format_add(ft, "pane_id", "%%%u", wp->id); - format_add(ft, "pane_active", "%d", wp == wp->window->active); + format_add(ft, "pane_active", "%d", wp == w->active); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1); @@ -1491,25 +1536,21 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); format_add(ft, "pane_dead", "%d", wp->fd == -1); - if (window_pane_visible(wp)) { - format_add(ft, "pane_left", "%u", wp->xoff); - format_add(ft, "pane_top", "%u", wp->yoff); - format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); - format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); - format_add(ft, "pane_at_left", "%d", wp->xoff == 0); - format_add(ft, "pane_at_top", "%d", wp->yoff == 0); - format_add(ft, "pane_at_right", "%d", - wp->xoff + wp->sx == wp->window->sx); - format_add(ft, "pane_at_bottom", "%d", - wp->yoff + wp->sy == wp->window->sy); - } + format_add(ft, "pane_left", "%u", wp->xoff); + format_add(ft, "pane_top", "%u", wp->yoff); + format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); + format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); + format_add(ft, "pane_at_left", "%d", wp->xoff == 0); + format_add(ft, "pane_at_top", "%d", wp->yoff == 0); + format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); + format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == w->sy); format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); if (wp->mode != NULL) format_add(ft, "pane_mode", "%s", wp->mode->name); format_add(ft, "pane_synchronized", "%d", - !!options_get_number(wp->window->options, "synchronize-panes")); + !!options_get_number(w->options, "synchronize-panes")); if (wp->searchstr != NULL) format_add(ft, "pane_search_string", "%s", wp->searchstr); @@ -764,7 +764,11 @@ grid_string_cells_code(const struct grid_cell *lastgc, { GRID_ATTR_BLINK, 5 }, { GRID_ATTR_REVERSE, 7 }, { GRID_ATTR_HIDDEN, 8 }, - { GRID_ATTR_STRIKETHROUGH, 9 } + { GRID_ATTR_STRIKETHROUGH, 9 }, + { GRID_ATTR_UNDERSCORE_2, 42 }, + { GRID_ATTR_UNDERSCORE_3, 43 }, + { GRID_ATTR_UNDERSCORE_4, 44 }, + { GRID_ATTR_UNDERSCORE_5, 45 }, }; n = 0; @@ -790,11 +794,15 @@ grid_string_cells_code(const struct grid_cell *lastgc, else strlcat(buf, "\033[", len); for (i = 0; i < n; i++) { - if (i + 1 < n) - xsnprintf(tmp, sizeof tmp, "%d;", s[i]); - else + if (s[i] < 10) xsnprintf(tmp, sizeof tmp, "%d", s[i]); + else { + xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10, + s[i] % 10); + } strlcat(buf, tmp, len); + if (i + 1 < n) + strlcat(buf, ";", len); } strlcat(buf, "m", len); } diff --git a/input-keys.c b/input-keys.c index 85dc2591..f0a38c09 100644 --- a/input-keys.c +++ b/input-keys.c @@ -247,10 +247,10 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) if ((mode & ALL_MOUSE_MODES) == 0) return; - if (!window_pane_visible(wp)) - return; if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; + if (!window_pane_visible(wp)) + return; /* If this pane is not in button or all mode, discard motion events. */ if (MOUSE_DRAG(m->b) && @@ -93,6 +93,10 @@ struct input_ctx { u_char *input_buf; size_t input_len; size_t input_space; + enum { + INPUT_END_ST, + INPUT_END_BEL + } input_end; struct input_param param_list[24]; u_int param_list_len; @@ -126,11 +130,11 @@ static void input_set_state(struct window_pane *, const struct input_transition *); static void input_reset_cell(struct input_ctx *); -static void input_osc_4(struct window_pane *, const char *); -static void input_osc_10(struct window_pane *, const char *); -static void input_osc_11(struct window_pane *, const char *); -static void input_osc_52(struct window_pane *, const char *); -static void input_osc_104(struct window_pane *, const char *); +static void input_osc_4(struct input_ctx *, const char *); +static void input_osc_10(struct input_ctx *, const char *); +static void input_osc_11(struct input_ctx *, const char *); +static void input_osc_52(struct input_ctx *, const char *); +static void input_osc_104(struct input_ctx *, const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); @@ -161,6 +165,7 @@ static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); static void input_csi_dispatch_sgr(struct input_ctx *); static int input_dcs_dispatch(struct input_ctx *); static int input_top_bit_set(struct input_ctx *); +static int input_end_bel(struct input_ctx *); /* Command table comparison function. */ static int input_table_compare(const void *, const void *); @@ -487,7 +492,7 @@ static const struct input_transition input_state_esc_enter_table[] = { { -1, -1, NULL, NULL } }; -/* esc_interm state table. */ +/* esc_intermediate state table. */ static const struct input_transition input_state_esc_intermediate_table[] = { INPUT_STATE_ANYWHERE, @@ -602,7 +607,7 @@ static const struct input_transition input_state_dcs_parameter_table[] = { { -1, -1, NULL, NULL } }; -/* dcs_interm state table. */ +/* dcs_intermediate state table. */ static const struct input_transition input_state_dcs_intermediate_table[] = { INPUT_STATE_ANYWHERE, @@ -655,12 +660,12 @@ static const struct input_transition input_state_dcs_ignore_table[] = { static const struct input_transition input_state_osc_string_table[] = { INPUT_STATE_ANYWHERE, - { 0x00, 0x06, NULL, NULL }, - { 0x07, 0x07, NULL, &input_state_ground }, - { 0x08, 0x17, NULL, NULL }, - { 0x19, 0x19, NULL, NULL }, - { 0x1c, 0x1f, NULL, NULL }, - { 0x20, 0xff, input_input, NULL }, + { 0x00, 0x06, NULL, NULL }, + { 0x07, 0x07, input_end_bel, &input_state_ground }, + { 0x08, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, { -1, -1, NULL, NULL } }; @@ -993,8 +998,8 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) static void input_reply(struct input_ctx *ictx, const char *fmt, ...) { - va_list ap; - char *reply; + va_list ap; + char *reply; va_start(ap, fmt); xvasprintf(&reply, fmt, ap); @@ -1019,6 +1024,8 @@ input_clear(struct input_ctx *ictx) *ictx->input_buf = '\0'; ictx->input_len = 0; + ictx->input_end = INPUT_END_ST; + ictx->flags &= ~INPUT_DISCARD; } @@ -1835,10 +1842,11 @@ input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) static void input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) { - char *s = ictx->param_list[i].str, *copy, *ptr, *out; - int p[8]; - u_int n; - const char *errstr; + struct grid_cell *gc = &ictx->cell.cell; + char *s = ictx->param_list[i].str, *copy, *ptr, *out; + int p[8]; + u_int n; + const char *errstr; for (n = 0; n < nitems(p); n++) p[n] = -1; @@ -1857,7 +1865,39 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) } free(copy); - if (n == 0 || (p[0] != 38 && p[0] != 48)) + if (n == 0) + return; + if (p[0] == 4) { + if (n != 2) + return; + switch (p[1]) { + case 0: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + break; + case 1: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE; + break; + case 2: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_2; + break; + case 3: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_3; + break; + case 4: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_4; + break; + case 5: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; + gc->attr |= GRID_ATTR_UNDERSCORE_5; + break; + } + return; + } + if (p[0] != 38 && p[0] != 48) return; if (p[1] == -1) i = 2; @@ -1927,6 +1967,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) gc->attr |= GRID_ATTR_ITALICS; break; case 4: + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE; break; case 5: @@ -1948,7 +1989,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) gc->attr &= ~GRID_ATTR_ITALICS; break; case 24: - gc->attr &= ~GRID_ATTR_UNDERSCORE; + gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; break; case 25: gc->attr &= ~GRID_ATTR_BLINK; @@ -2012,6 +2053,17 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) } } +/* End of input with BEL. */ +static int +input_end_bel(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + ictx->input_end = INPUT_END_BEL; + + return (0); +} + /* DCS string started. */ static void input_enter_dcs(struct input_ctx *ictx) @@ -2068,7 +2120,8 @@ input_exit_osc(struct input_ctx *ictx) if (ictx->input_len < 1 || *p < '0' || *p > '9') return; - log_debug("%s: \"%s\"", __func__, p); + log_debug("%s: \"%s\" (end %s)", __func__, p, + ictx->input_end == INPUT_END_ST ? "ST" : "BEL"); option = 0; while (*p >= '0' && *p <= '9') @@ -2085,23 +2138,23 @@ input_exit_osc(struct input_ctx *ictx) } break; case 4: - input_osc_4(ictx->wp, p); + input_osc_4(ictx, p); break; case 10: - input_osc_10(ictx->wp, p); + input_osc_10(ictx, p); break; case 11: - input_osc_11(ictx->wp, p); + input_osc_11(ictx, p); break; case 12: if (utf8_isvalid(p) && *p != '?') /* ? is colour request */ screen_set_cursor_colour(ictx->ctx.s, p); break; case 52: - input_osc_52(ictx->wp, p); + input_osc_52(ictx, p); break; case 104: - input_osc_104(ictx->wp, p); + input_osc_104(ictx, p); break; case 112: if (*p == '\0') /* no arguments allowed */ @@ -2203,11 +2256,12 @@ input_top_bit_set(struct input_ctx *ictx) /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ static void -input_osc_4(struct window_pane *wp, const char *p) +input_osc_4(struct input_ctx *ictx, const char *p) { - char *copy, *s, *next = NULL; - long idx; - u_int r, g, b; + struct window_pane *wp = ictx->wp; + char *copy, *s, *next = NULL; + long idx; + u_int r, g, b; copy = s = xstrdup(p); while (s != NULL && *s != '\0') { @@ -2237,9 +2291,10 @@ bad: /* Handle the OSC 10 sequence for setting foreground colour. */ static void -input_osc_10(struct window_pane *wp, const char *p) +input_osc_10(struct input_ctx *ictx, const char *p) { - u_int r, g, b; + struct window_pane *wp = ictx->wp; + u_int r, g, b; if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; @@ -2255,9 +2310,10 @@ bad: /* Handle the OSC 11 sequence for setting background colour. */ static void -input_osc_11(struct window_pane *wp, const char *p) +input_osc_11(struct input_ctx *ictx, const char *p) { - u_int r, g, b; + struct window_pane *wp = ictx->wp; + u_int r, g, b; if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; @@ -2273,13 +2329,16 @@ bad: /* Handle the OSC 52 sequence for setting the clipboard. */ static void -input_osc_52(struct window_pane *wp, const char *p) +input_osc_52(struct input_ctx *ictx, const char *p) { + struct window_pane *wp = ictx->wp; char *end; + const char *buf; size_t len; u_char *out; int outlen, state; struct screen_write_ctx ctx; + struct paste_buffer *pb; state = options_get_number(global_options, "set-clipboard"); if (state != 2) @@ -2290,6 +2349,32 @@ input_osc_52(struct window_pane *wp, const char *p) end++; if (*end == '\0') return; + log_debug("%s: %s", __func__, end); + + if (strcmp(end, "?") == 0) { + if ((pb = paste_get_top(NULL)) != NULL) { + buf = paste_buffer_data(pb, &len); + outlen = 4 * ((len + 2) / 3) + 1; + out = xmalloc(outlen); + if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { + abort(); + free(out); + return; + } + } else { + outlen = 0; + out = NULL; + } + bufferevent_write(wp->event, "\033]52;;", 6); + if (outlen != 0) + bufferevent_write(wp->event, out, outlen); + if (ictx->input_end == INPUT_END_BEL) + bufferevent_write(wp->event, "\007", 1); + else + bufferevent_write(wp->event, "\033\\", 2); + free(out); + return; + } len = (strlen(end) / 4) * 3; if (len == 0) @@ -2311,10 +2396,11 @@ input_osc_52(struct window_pane *wp, const char *p) /* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */ static void -input_osc_104(struct window_pane *wp, const char *p) +input_osc_104(struct input_ctx *ictx, const char *p) { - char *copy, *s; - long idx; + struct window_pane *wp = ictx->wp; + char *copy, *s; + long idx; if (*p == '\0') { window_pane_reset_palette(wp); @@ -36,8 +36,33 @@ static void job_read_callback(struct bufferevent *, void *); static void job_write_callback(struct bufferevent *, void *); static void job_error_callback(struct bufferevent *, short, void *); +/* A single job. */ +struct job { + enum { + JOB_RUNNING, + JOB_DEAD, + JOB_CLOSED + } state; + + int flags; + + char *cmd; + pid_t pid; + int status; + + int fd; + struct bufferevent *event; + + job_update_cb updatecb; + job_complete_cb completecb; + job_free_cb freecb; + void *data; + + LIST_ENTRY(job) entry; +}; + /* All jobs list. */ -struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); +static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * @@ -54,6 +79,7 @@ job_run(const char *cmd, struct session *s, const char *cwd, if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); + log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd); /* * Do not set TERM during .tmux.conf, it is nice to be able to use @@ -207,8 +233,16 @@ job_error_callback(__unused struct bufferevent *bufev, __unused short events, /* Job died (waitpid() returned its pid). */ void -job_died(struct job *job, int status) +job_check_died(pid_t pid, int status) { + struct job *job; + + LIST_FOREACH(job, &all_jobs, entry) { + if (pid == job->pid) + break; + } + if (job == NULL) + return; log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); job->status = status; @@ -222,3 +256,67 @@ job_died(struct job *job, int status) job->state = JOB_DEAD; } } + +/* Get job status. */ +int +job_get_status(struct job *job) +{ + return (job->status); +} + +/* Get job data. */ +void * +job_get_data(struct job *job) +{ + return (job->data); +} + +/* Get job event. */ +struct bufferevent * +job_get_event(struct job *job) +{ + return (job->event); +} + +/* Kill all jobs. */ +void +job_kill_all(void) +{ + struct job *job; + + LIST_FOREACH(job, &all_jobs, entry) { + if (job->pid != -1) + kill(job->pid, SIGTERM); + } +} + +/* Are any jobs still running? */ +int +job_still_running(void) +{ + struct job *job; + + LIST_FOREACH(job, &all_jobs, entry) { + if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING) + return (1); + } + return (0); +} + +/* Print job summary. */ +void +job_print_summary(struct cmdq_item *item, int blank) +{ + struct job *job; + u_int n = 0; + + LIST_FOREACH(job, &all_jobs, entry) { + if (blank) { + cmdq_print(item, "%s", ""); + blank = 0; + } + cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]", + n, job->cmd, job->fd, (long)job->pid, job->status); + n++; + } +} diff --git a/key-bindings.c b/key-bindings.c index 41afd99c..fbc54fb8 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -228,7 +228,7 @@ key_bindings_init(void) "bind ] paste-buffer", "bind c new-window", "bind d detach-client", - "bind f command-prompt \"find-window -- '%%'\"", + "bind f command-prompt \"find-window -Z -- '%%'\"", "bind i display-message", "bind l last-window", "bind m select-pane -m", @@ -258,6 +258,11 @@ key_bindings_init(void) "bind M-n next-window -a", "bind M-o rotate-window -D", "bind M-p previous-window -a", + "bind -r S-Up refresh-client -U 10", + "bind -r S-Down refresh-client -D 10", + "bind -r S-Left refresh-client -L 10", + "bind -r S-Right refresh-client -R 10", + "bind -r DC refresh-client -c", "bind -r M-Up resize-pane -U 5", "bind -r M-Down resize-pane -D 5", "bind -r M-Left resize-pane -L 5", diff --git a/key-string.c b/key-string.c index 45073efd..8442727d 100644 --- a/key-string.c +++ b/key-string.c @@ -43,7 +43,9 @@ static const struct { { "F11", KEYC_F11 }, { "F12", KEYC_F12 }, { "IC", KEYC_IC }, + { "Insert", KEYC_IC }, { "DC", KEYC_DC }, + { "Delete", KEYC_DC }, { "Home", KEYC_HOME }, { "End", KEYC_END }, { "NPage", KEYC_NPAGE }, @@ -271,6 +273,10 @@ key_string_lookup_key(key_code key) return ("MouseMovePane"); if (key == KEYC_MOUSEMOVE_STATUS) return ("MouseMoveStatus"); + if (key == KEYC_MOUSEMOVE_STATUS_LEFT) + return ("MouseMoveStatusLeft"); + if (key == KEYC_MOUSEMOVE_STATUS_RIGHT) + return ("MouseMoveStatusRight"); if (key == KEYC_MOUSEMOVE_BORDER) return ("MouseMoveBorder"); if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) { diff --git a/layout-custom.c b/layout-custom.c index 1b8f576e..9886afe1 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -167,7 +167,7 @@ layout_parse(struct window *w, const char *layout) /* Update pane offsets and sizes. */ layout_fix_offsets(lc); - layout_fix_panes(w, lc->sx, lc->sy); + layout_fix_panes(w); /* Then resize the layout back to the original window size. */ layout_resize(w, sx, sy); diff --git a/layout-set.c b/layout-set.c index 5055f672..b9769ed5 100644 --- a/layout-set.c +++ b/layout-set.c @@ -148,7 +148,7 @@ layout_set_even(struct window *w, enum layout_type type) /* Fix cell offsets. */ layout_fix_offsets(lc); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -284,7 +284,7 @@ layout_set_main_h(struct window *w) /* Fix cell offsets. */ layout_fix_offsets(lc); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -408,7 +408,7 @@ layout_set_main_v(struct window *w) /* Fix cell offsets. */ layout_fix_offsets(lc); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -511,7 +511,7 @@ layout_set_tiled(struct window *w) /* Fix cell offsets. */ layout_fix_offsets(lc); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); @@ -236,7 +236,7 @@ layout_need_status(struct layout_cell *lc, int at_top) { struct layout_cell *first_lc; - if (lc->parent) { + if (lc->parent != NULL) { if (lc->parent->type == LAYOUT_LEFTRIGHT) return (layout_need_status(lc->parent, at_top)); @@ -253,71 +253,29 @@ layout_need_status(struct layout_cell *lc, int at_top) /* Update pane offsets and sizes based on their cells. */ void -layout_fix_panes(struct window *w, u_int wsx, u_int wsy) +layout_fix_panes(struct window *w) { struct window_pane *wp; struct layout_cell *lc; - u_int sx, sy; - int shift, status, at_top; + int shift, status; status = options_get_number(w->options, "pane-border-status"); - at_top = (status == 1); TAILQ_FOREACH(wp, &w->panes, entry) { if ((lc = wp->layout_cell) == NULL) continue; if (status != 0) - shift = layout_need_status(lc, at_top); + shift = layout_need_status(lc, status == 1); else shift = 0; wp->xoff = lc->xoff; wp->yoff = lc->yoff; - if (shift && at_top) + if (shift && status == 1) wp->yoff += 1; - /* - * Layout cells are limited by the smallest size of other cells - * within the same row or column; if this isn't the case - * resizing becomes difficult. - * - * However, panes do not have to take up their entire cell, so - * they can be cropped to the window edge if the layout - * overflows and they are partly visible. - * - * This stops cells being hidden unnecessarily. - */ - - /* - * Work out the horizontal size. If the pane is actually - * outside the window or the entire pane is already visible, - * don't crop. - */ - if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx) - sx = lc->sx; - else { - sx = wsx - lc->xoff; - if (sx < 1) - sx = lc->sx; - } - - /* - * Similarly for the vertical size; the minimum vertical size - * is two because scroll regions cannot be one line. - */ - if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy) - sy = lc->sy; - else { - sy = wsy - lc->yoff; - if (sy < 2) - sy = lc->sy; - } - - if (shift) - sy -= 1; - - window_pane_resize(wp, sx, sy); + window_pane_resize(wp, lc->sx, lc->sy - shift); } } @@ -349,7 +307,9 @@ layout_resize_check(struct window *w, struct layout_cell *lc, { struct layout_cell *lcchild; u_int available, minimum; + int status; + status = options_get_number(w->options, "pane-border-status"); if (lc->type == LAYOUT_WINDOWPANE) { /* Space available in this cell only. */ minimum = PANE_MINIMUM; @@ -357,9 +317,8 @@ layout_resize_check(struct window *w, struct layout_cell *lc, available = lc->sx; else { available = lc->sy; - minimum += layout_need_status(lc, - options_get_number(w->options, - "pane-border-status") == 1); + if (status != 0) + minimum += layout_need_status(lc, status == 1); } if (available > minimum) available -= minimum; @@ -491,8 +450,7 @@ layout_init(struct window *w, struct window_pane *wp) lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, w->sx, w->sy, 0, 0); layout_make_leaf(lc, wp); - - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); } void @@ -550,7 +508,7 @@ layout_resize(struct window *w, u_int sx, u_int sy) /* Fix cell offsets. */ layout_fix_offsets(lc); - layout_fix_panes(w, sx, sy); + layout_fix_panes(w); } /* Resize a pane to an absolute size. */ @@ -610,7 +568,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc, /* Fix cell offsets. */ layout_fix_offsets(w->layout_root); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); notify_window("window-layout-changed", w); } @@ -717,7 +675,7 @@ void layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) { layout_make_leaf(lc, wp); - layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); + layout_fix_panes(wp->window); } /* Calculate the new pane size for resized parent. */ @@ -1037,7 +995,7 @@ layout_close_pane(struct window_pane *wp) /* Fix pane offsets and sizes. */ if (w->layout_root != NULL) { layout_fix_offsets(w->layout_root); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); } notify_window("window-layout-changed", w); } @@ -1094,7 +1052,7 @@ layout_spread_out(struct window_pane *wp) do { if (layout_spread_cell(w, parent)) { layout_fix_offsets(parent); - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); break; } } while ((parent = parent->parent) != NULL); diff --git a/options-table.c b/options-table.c index 2b6b794b..4bc7982f 100644 --- a/options-table.c +++ b/options-table.c @@ -59,6 +59,9 @@ static const char *options_table_pane_status_list[] = { static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; +static const char *options_table_window_size_list[] = { + "largest", "smallest", "manual", NULL +}; /* Top-level options. */ const struct options_table_entry options_table[] = { @@ -193,6 +196,13 @@ const struct options_table_entry options_table[] = { .default_str = _PATH_BSHELL }, + { .name = "default-size", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .pattern = "[0-9]*x[0-9]*", + .default_str = "80x24" + }, + { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, @@ -466,7 +476,9 @@ const struct options_table_entry options_table[] = { { .name = "status-right", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y" + .default_str = "#{?window_bigger," + "[#{window_offset_x}#,#{window_offset_y}] ,}" + "\"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", @@ -588,22 +600,6 @@ const struct options_table_entry options_table[] = { .default_num = 1 }, - { .name = "force-height", - .type = OPTIONS_TABLE_NUMBER, - .scope = OPTIONS_TABLE_WINDOW, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - - { .name = "force-width", - .type = OPTIONS_TABLE_NUMBER, - .scope = OPTIONS_TABLE_WINDOW, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, @@ -770,6 +766,13 @@ const struct options_table_entry options_table[] = { .default_str = "default" }, + { .name = "window-size", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_window_size_list, + .default_num = WINDOW_SIZE_LARGEST + }, + { .name = "window-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, @@ -22,151 +22,228 @@ #include "tmux.h" -/* - * Recalculate window and session sizes. - * - * Every session has the size of the smallest client it is attached to and - * every window the size of the smallest session it is attached to. - * - * So, when a client is resized or a session attached to or detached from a - * client, the window sizes must be recalculated. For each session, find the - * smallest client it is attached to, and resize it to that size. Then for - * every window, find the smallest session it is attached to, resize it to that - * size and clear and redraw every client with it as the current window. - * - * This is quite inefficient - better/additional data structures are needed - * to make it better. - * - * As a side effect, this function updates the SESSION_UNATTACHED flag. This - * flag is necessary to make sure unattached sessions do not limit the size of - * windows that are attached both to them and to other (attached) sessions. - */ +void +resize_window(struct window *w, u_int sx, u_int sy) +{ + int zoomed; + + /* Check size limits. */ + if (sx < WINDOW_MINIMUM) + sx = WINDOW_MINIMUM; + if (sx > WINDOW_MAXIMUM) + sx = WINDOW_MAXIMUM; + if (sy < WINDOW_MINIMUM) + sy = WINDOW_MINIMUM; + if (sy > WINDOW_MAXIMUM) + sy = WINDOW_MAXIMUM; + + /* If the window is zoomed, unzoom. */ + zoomed = w->flags & WINDOW_ZOOMED; + if (zoomed) + window_unzoom(w); + + /* Resize the layout first. */ + layout_resize(w, sx, sy); + + /* Resize the window, it can be no smaller than the layout. */ + if (sx < w->layout_root->sx) + sx = w->layout_root->sx; + if (sy < w->layout_root->sy) + sy = w->layout_root->sy; + window_resize(w, sx, sy); + + /* Restore the window zoom state. */ + if (zoomed) + window_zoom(w->active); + + tty_update_window_offset(w); + server_redraw_window(w); + notify_window("window-layout-changed", w); +} void -recalculate_sizes(void) +default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy, + int type) { - struct session *s; - struct client *c; - struct window *w; - struct window_pane *wp; - u_int ssx, ssy, has, limit, lines; - int flag, is_zoomed, forced; + struct client *c; + u_int cx, cy; + const char *value; - RB_FOREACH(s, sessions, &sessions) { - lines = status_line_size(s); + if (type == -1) + type = options_get_number(global_w_options, "window-size"); + if (type == WINDOW_SIZE_MANUAL) + goto manual; - s->attached = 0; - ssx = ssy = UINT_MAX; + if (type == WINDOW_SIZE_LARGEST) { + *sx = *sy = 0; TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_SUSPENDED) + if (c->session == NULL) continue; - if ((c->flags & (CLIENT_CONTROL|CLIENT_SIZECHANGED)) == - CLIENT_CONTROL) + if (c->flags & CLIENT_NOSIZEFLAGS) continue; - if (c->session == s) { - if (c->tty.sx < ssx) - ssx = c->tty.sx; - c->flags &= ~CLIENT_STATUSOFF; - if (lines != 0 && lines + PANE_MINIMUM > c->tty.sy) - c->flags |= CLIENT_STATUSOFF; - if ((~c->flags & CLIENT_STATUSOFF) && - !(c->flags & CLIENT_CONTROL) && - c->tty.sy > lines && - c->tty.sy - lines < ssy) - ssy = c->tty.sy - lines; - else if (c->tty.sy < ssy) - ssy = c->tty.sy; - s->attached++; - } + if (w != NULL && !session_has(c->session, w)) + continue; + if (w == NULL && c->session != s) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx > *sx) + *sx = cx; + if (cy > *sy) + *sy = cy; } - if (ssx == UINT_MAX || ssy == UINT_MAX) { - s->flags |= SESSION_UNATTACHED; - continue; + if (*sx == 0 || *sy == 0) + goto manual; + } else { + *sx = *sy = UINT_MAX; + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL) + continue; + if (c->flags & CLIENT_NOSIZEFLAGS) + continue; + if (w != NULL && !session_has(c->session, w)) + continue; + if (w == NULL && c->session != s) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx < *sx) + *sx = cx; + if (cy < *sy) + *sy = cy; } - s->flags &= ~SESSION_UNATTACHED; + if (*sx == UINT_MAX || *sy == UINT_MAX) + goto manual; + } + goto done; + +manual: + value = options_get_string(s->options, "default-size"); + if (sscanf(value, "%ux%u", sx, sy) != 2) { + *sx = 80; + *sy = 24; + } + +done: + if (*sx < WINDOW_MINIMUM) + *sx = WINDOW_MINIMUM; + if (*sx > WINDOW_MAXIMUM) + *sx = WINDOW_MAXIMUM; + if (*sy < WINDOW_MINIMUM) + *sy = WINDOW_MINIMUM; + if (*sy > WINDOW_MAXIMUM) + *sy = WINDOW_MAXIMUM; +} - if (lines != 0 && ssy == 0) - ssy = lines; +void +recalculate_sizes(void) +{ + struct session *s; + struct client *c; + struct window *w; + u_int sx, sy, cx, cy; + int flags, type, current, has, changed; + + /* + * Clear attached count and update saved status line information for + * each session. + */ + RB_FOREACH(s, sessions, &sessions) { + s->attached = 0; + status_update_saved(s); + } - if (s->sx == ssx && s->sy == ssy) + /* + * Increment attached count and check the status line size for each + * client. + */ + TAILQ_FOREACH(c, &clients, entry) { + if ((s = c->session) == NULL) continue; - log_debug("session $%u size %u,%u (was %u,%u)", s->id, ssx, ssy, - s->sx, s->sy); + flags = c->flags; + if (flags & CLIENT_SUSPENDED) + continue; + if ((flags & CLIENT_CONTROL) && (~flags & CLIENT_SIZECHANGED)) + continue; - s->sx = ssx; - s->sy = ssy; + if (c->tty.sy <= status_line_size(c)) + c->flags |= CLIENT_STATUSOFF; + else + c->flags &= ~CLIENT_STATUSOFF; - status_update_saved(s); + s->attached++; } + /* Walk each window and adjust the size. */ RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; - flag = options_get_number(w->options, "aggressive-resize"); + log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); - ssx = ssy = UINT_MAX; - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - if (flag) - has = s->curw->window == w; - else - has = session_has(s, w); - if (has) { - if (s->sx < ssx) - ssx = s->sx; - if (s->sy < ssy) - ssy = s->sy; - } - } - if (ssx == UINT_MAX || ssy == UINT_MAX) + type = options_get_number(w->options, "window-size"); + if (type == WINDOW_SIZE_MANUAL) continue; - - forced = 0; - limit = options_get_number(w->options, "force-width"); - if (limit >= PANE_MINIMUM && ssx > limit) { - ssx = limit; - forced |= WINDOW_FORCEWIDTH; - } - limit = options_get_number(w->options, "force-height"); - if (limit >= PANE_MINIMUM && ssy > limit) { - ssy = limit; - forced |= WINDOW_FORCEHEIGHT; + current = !options_get_number(w->options, "aggressive-resize"); + + changed = 1; + if (type == WINDOW_SIZE_LARGEST) { + sx = sy = 0; + TAILQ_FOREACH(c, &clients, entry) { + if ((s = c->session) == NULL) + continue; + if (current) + has = (s->curw->window == w); + else + has = session_has(s, w); + if (!has) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx > sx) + sx = cx; + if (cy > sy) + sy = cy; + } + if (sx == 0 || sy == 0) + changed = 0; + } else { + sx = sy = UINT_MAX; + TAILQ_FOREACH(c, &clients, entry) { + if ((s = c->session) == NULL) + continue; + if (current) + has = (s->curw->window == w); + else + has = session_has(s, w); + if (!has) + continue; + + cx = c->tty.sx; + cy = c->tty.sy - status_line_size(c); + + if (cx < sx) + sx = cx; + if (cy < sy) + sy = cy; + } + if (sx == UINT_MAX || sy == UINT_MAX) + changed = 0; } + if (w->sx == sx && w->sy == sy) + changed = 0; - if (w->sx == ssx && w->sy == ssy) + if (!changed) { + tty_update_window_offset(w); continue; - log_debug("window @%u size %u,%u (was %u,%u)", w->id, ssx, ssy, - w->sx, w->sy); - - w->flags &= ~(WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); - w->flags |= forced; - - is_zoomed = w->flags & WINDOW_ZOOMED; - if (is_zoomed) - window_unzoom(w); - layout_resize(w, ssx, ssy); - window_resize(w, ssx, ssy); - if (is_zoomed && window_pane_visible(w->active)) - window_zoom(w->active); - - /* - * If the current pane is now not visible, move to the next - * that is. - */ - wp = w->active; - while (!window_pane_visible(w->active)) { - w->active = TAILQ_PREV(w->active, window_panes, entry); - if (w->active == NULL) - w->active = TAILQ_LAST(&w->panes, window_panes); - if (w->active == wp) - break; } - if (w->active == w->last) - w->last = NULL; - - server_redraw_window(w); - notify_window("window-layout-changed", w); + log_debug("%s: @%u changed to %u,%u", __func__, w->id, sx, sy); + resize_window(w, sx, sy); } } diff --git a/screen-redraw.c b/screen-redraw.c index 931bb20f..167389b4 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -33,22 +33,15 @@ struct screen_redraw_ctx { u_int sx; u_int sy; + u_int ox; + u_int oy; }; -static int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); -static int screen_redraw_cell_border(struct client *, u_int, u_int); -static int screen_redraw_check_cell(struct client *, u_int, u_int, int, - struct window_pane **); -static int screen_redraw_check_is(u_int, u_int, int, int, struct window *, - struct window_pane *, struct window_pane *); - -static int screen_redraw_make_pane_status(struct client *, struct window *, - struct window_pane *); -static void screen_redraw_draw_pane_status(struct client *, int); - static void screen_redraw_draw_borders(struct screen_redraw_ctx *); static void screen_redraw_draw_panes(struct screen_redraw_ctx *); static void screen_redraw_draw_status(struct screen_redraw_ctx *); +static void screen_redraw_draw_pane(struct screen_redraw_ctx *, + struct window_pane *); static void screen_redraw_draw_number(struct screen_redraw_ctx *, struct window_pane *); @@ -327,35 +320,67 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, /* Draw pane status. */ static void -screen_redraw_draw_pane_status(struct client *c, int pane_status) +screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) { + struct client *c = ctx->c; struct window *w = c->session->curw->window; - struct options *oo = c->session->options; struct tty *tty = &c->tty; struct window_pane *wp; - int spos; - u_int yoff; + struct screen *s; + u_int i, x, width, xoff, yoff, size; + + log_debug("%s: %s @%u", __func__, c->name, w->id); - spos = options_get_number(oo, "status-position"); TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - if (pane_status == CELL_STATUS_TOP) + s = &wp->status_screen; + + size = wp->status_size; + if (ctx->pane_status == CELL_STATUS_TOP) yoff = wp->yoff - 1; else yoff = wp->yoff + wp->sy; - if (spos == 0) - yoff += 1; + xoff = wp->xoff + 2; + + if (xoff + size <= ctx->ox || + xoff >= ctx->ox + ctx->sx || + yoff < ctx->oy || + yoff >= ctx->oy + ctx->sy) + continue; - tty_draw_line(tty, NULL, &wp->status_screen, 0, wp->xoff + 2, - yoff); + if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) { + /* All visible. */ + i = 0; + x = xoff - ctx->ox; + width = size; + } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) { + /* Both left and right not visible. */ + i = ctx->ox; + x = 0; + width = ctx->sx; + } else if (xoff < ctx->ox) { + /* Left not visible. */ + i = ctx->ox - xoff; + x = 0; + width = size - i; + } else { + /* Right not visible. */ + i = 0; + x = xoff - ctx->ox; + width = size - x; + } + + if (ctx->top) + yoff += ctx->lines; + tty_draw_line(tty, NULL, s, i, 0, width, x, yoff - ctx->oy); } tty_cursor(tty, 0, 0); } /* Update status line and change flags if unchanged. */ -void -screen_redraw_update(struct client *c) +static int +screen_redraw_update(struct client *c, int flags) { struct window *w = c->session->curw->window; struct window_pane *wp; @@ -368,8 +393,8 @@ screen_redraw_update(struct client *c) redraw = status_prompt_redraw(c); else redraw = status_redraw(c); - if (!redraw) - c->flags &= ~CLIENT_STATUS; + if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS)) + flags &= ~CLIENT_REDRAWSTATUS; if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) { redraw = 0; @@ -378,99 +403,96 @@ screen_redraw_update(struct client *c) redraw = 1; } if (redraw) - c->flags |= CLIENT_BORDERS; + flags |= CLIENT_REDRAWBORDERS; } + return (flags); } -/* Redraw entire screen. */ -void -screen_redraw_screen(struct client *c, int draw_panes, int draw_status, - int draw_borders) +/* Set up redraw context. */ +static void +screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) { - struct options *oo = c->session->options; - struct tty *tty = &c->tty; - struct window *w = c->session->curw->window; - struct options *wo = w->options; - struct screen_redraw_ctx ctx; - - if (c->flags & CLIENT_SUSPENDED) - return; + struct session *s = c->session; + struct options *oo = s->options; + struct window *w = s->curw->window; + struct options *wo = w->options; - memset(&ctx, 0, sizeof ctx); - ctx.c = c; + memset(ctx, 0, sizeof *ctx); + ctx->c = c; - if (c->flags & CLIENT_STATUSOFF) - ctx.lines = 0; - else - ctx.lines = status_line_size(c->session); + ctx->lines = status_line_size(c); if (c->message_string != NULL || c->prompt_string != NULL) - ctx.lines = (ctx.lines == 0) ? 1 : ctx.lines; + ctx->lines = (ctx->lines == 0) ? 1 : ctx->lines; + if (ctx->lines != 0 && options_get_number(oo, "status-position") == 0) + ctx->top = 1; + ctx->pane_status = options_get_number(wo, "pane-border-status"); + + tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); - if (ctx.lines != 0 && options_get_number(oo, "status-position") == 0) - ctx.top = 1; - ctx.pane_status = options_get_number(wo, "pane-border-status"); + log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name, + w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->lines, ctx->top); +} + +/* Redraw entire screen. */ +void +screen_redraw_screen(struct client *c) +{ + struct screen_redraw_ctx ctx; + int flags; - ctx.sx = tty->sx; - ctx.sy = tty->sy - ctx.lines; + if (c->flags & CLIENT_SUSPENDED) + return; - if (ctx.lines == 0) - draw_status = 0; + flags = screen_redraw_update(c, c->flags); + screen_redraw_set_context(c, &ctx); - if (draw_borders) { + if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { if (ctx.pane_status != CELL_STATUS_OFF) - screen_redraw_draw_pane_status(c, ctx.pane_status); + screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } - if (draw_panes) + if (flags & CLIENT_REDRAWWINDOW) screen_redraw_draw_panes(&ctx); - if (draw_status) + if (ctx.lines != 0 && + (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) screen_redraw_draw_status(&ctx); - tty_reset(tty); + tty_reset(&c->tty); } -/* Draw a single pane. */ +/* Redraw a single pane. */ void screen_redraw_pane(struct client *c, struct window_pane *wp) { - u_int i, yoff; + struct screen_redraw_ctx ctx; if (!window_pane_visible(wp)) return; - yoff = wp->yoff; - if (status_at_line(c) == 0) - yoff += status_line_size(c->session); - - log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id, - wp->xoff, yoff); + screen_redraw_set_context(c, &ctx); - for (i = 0; i < wp->sy; i++) - tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff); + screen_redraw_draw_pane(&ctx, wp); tty_reset(&c->tty); } /* Draw a border cell. */ static void -screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int x, u_int y, - int small, u_int msgx, u_int msgy, struct grid_cell *m_active_gc, - struct grid_cell *active_gc, struct grid_cell *m_other_gc, - struct grid_cell *other_gc) +screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j, + struct grid_cell *m_active_gc, struct grid_cell *active_gc, + struct grid_cell *m_other_gc, struct grid_cell *other_gc) { - struct client *c = ctx->c; - struct session *s = c->session; - struct window *w = s->curw->window; - struct tty *tty = &c->tty; - struct window_pane *wp; - struct window_pane *active = w->active; - struct window_pane *marked = marked_pane.wp; - u_int type; - int flag, pane_status = ctx->pane_status; + struct client *c = ctx->c; + struct session *s = c->session; + struct window *w = s->curw->window; + struct tty *tty = &c->tty; + struct window_pane *wp; + struct window_pane *active = w->active; + struct window_pane *marked = marked_pane.wp; + u_int type, x = ctx->ox + i, y = ctx->oy + j; + int flag, pane_status = ctx->pane_status; type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (type == CELL_INSIDE) return; - if (type == CELL_OUTSIDE && small && x > msgx && y == msgy) - return; flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp); if (server_is_marked(s, s->curw, marked_pane.wp) && @@ -484,9 +506,9 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int x, u_int y, else tty_attributes(tty, other_gc, NULL); if (ctx->top) - tty_cursor(tty, x, ctx->lines + y); + tty_cursor(tty, i, ctx->lines + j); else - tty_cursor(tty, x, y); + tty_cursor(tty, i, j); tty_putc(tty, CELL_BORDERS[type]); } @@ -497,42 +519,12 @@ screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; - struct options *oo = w->options; struct tty *tty = &c->tty; + struct options *oo = w->options; struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; - struct grid_cell msg_gc; - u_int i, j, msgx = 0, msgy = 0; - int small, flags; - char msg[256]; - const char *tmp; - size_t msglen = 0; - - small = (ctx->sy + ctx->top > w->sy) || (ctx->sx > w->sx); - if (small) { - flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); - if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT)) - tmp = "force-width, force-height"; - else if (flags == WINDOW_FORCEWIDTH) - tmp = "force-width"; - else if (flags == WINDOW_FORCEHEIGHT) - tmp = "force-height"; - else if (c->flags & CLIENT_STATUSOFF) - tmp = "status line"; - else - tmp = "a smaller client"; - xsnprintf(msg, sizeof msg, "(size %ux%u from %s)", - w->sx, w->sy, tmp); - msglen = strlen(msg); - - if (ctx->sy - 1 + ctx->top > w->sy && ctx->sx >= msglen) { - msgx = ctx->sx - msglen; - msgy = ctx->sy - 1 + ctx->top; - } else if (ctx->sx - w->sx > msglen) { - msgx = ctx->sx - msglen; - msgy = ctx->sy - 1 + ctx->top; - } else - small = 0; - } + u_int i, j; + + log_debug("%s: %s @%u", __func__, c->name, w->id); style_apply(&other_gc, oo, "pane-border-style"); style_apply(&active_gc, oo, "pane-active-border-style"); @@ -543,20 +535,12 @@ screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); m_active_gc.attr ^= GRID_ATTR_REVERSE; - for (j = 0; j < ctx->sy; j++) { - for (i = 0; i < ctx->sx; i++) { - screen_redraw_draw_borders_cell(ctx, i, j, small, - msgx, msgy, &m_active_gc, &active_gc, &m_other_gc, - &other_gc); + for (j = 0; j < tty->sy - ctx->lines; j++) { + for (i = 0; i < tty->sx; i++) { + screen_redraw_draw_borders_cell(ctx, i, j, + &m_active_gc, &active_gc, &m_other_gc, &other_gc); } } - - if (small) { - memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc); - tty_attributes(tty, &msg_gc, NULL); - tty_cursor(tty, msgx, msgy); - tty_puts(tty, msg); - } } /* Draw the panes. */ @@ -565,19 +549,14 @@ screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) { struct client *c = ctx->c; struct window *w = c->session->curw->window; - struct tty *tty = &c->tty; struct window_pane *wp; - u_int i, y; - if (ctx->top) - y = ctx->lines; - else - y = 0; + log_debug("%s: %s @%u", __func__, c->name, w->id); + TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - for (i = 0; i < wp->sy; i++) - tty_draw_pane(tty, wp, i, wp->xoff, y + wp->yoff); + screen_redraw_draw_pane(ctx, wp); if (c->flags & CLIENT_IDENTIFY) screen_redraw_draw_number(ctx, wp); } @@ -588,15 +567,74 @@ static void screen_redraw_draw_status(struct screen_redraw_ctx *ctx) { struct client *c = ctx->c; + struct window *w = c->session->curw->window; struct tty *tty = &c->tty; + struct screen *s = &c->status.status; u_int i, y; + log_debug("%s: %s @%u", __func__, c->name, w->id); + if (ctx->top) y = 0; else - y = ctx->sy; + y = c->tty.sy - ctx->lines; for (i = 0; i < ctx->lines; i++) - tty_draw_line(tty, NULL, &c->status.status, i, 0, y); + tty_draw_line(tty, NULL, s, 0, i, UINT_MAX, 0, y + i); +} + +/* Draw one pane. */ +static void +screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) +{ + struct client *c = ctx->c; + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct screen *s; + u_int i, j, top, x, y, width; + + log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); + + if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx) + return; + if (ctx->top) + top = ctx->lines; + else + top = 0; + + s = wp->screen; + for (j = 0; j < wp->sy; j++) { + if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy) + continue; + y = top + wp->yoff + j - ctx->oy; + + if (wp->xoff >= ctx->ox && + wp->xoff + wp->sx <= ctx->ox + ctx->sx) { + /* All visible. */ + i = 0; + x = wp->xoff - ctx->ox; + width = wp->sx; + } else if (wp->xoff < ctx->ox && + wp->xoff + wp->sx > ctx->ox + ctx->sx) { + /* Both left and right not visible. */ + i = ctx->ox; + x = 0; + width = ctx->sx; + } else if (wp->xoff < ctx->ox) { + /* Left not visible. */ + i = ctx->ox - wp->xoff; + x = 0; + width = wp->sx - i; + } else { + /* Right not visible. */ + i = 0; + x = wp->xoff - ctx->ox; + width = ctx->sx - x; + } + log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", + __func__, c->name, wp->id, i, j, x, y, width); + + tty_draw_line(tty, wp, s, i, j, width, x, y); + } } /* Draw number on a pane. */ @@ -609,27 +647,69 @@ screen_redraw_draw_number(struct screen_redraw_ctx *ctx, struct window_pane *wp) struct options *oo = s->options; struct window *w = wp->window; struct grid_cell gc; - u_int idx, px, py, i, j, xoff, yoff; + u_int idx, px, py, i, j, xoff, yoff, sx, sy; int colour, active_colour; char buf[16], *ptr; size_t len; + if (wp->xoff + wp->sx <= ctx->ox || + wp->xoff >= ctx->ox + ctx->sx || + wp->yoff + wp->sy <= ctx->oy || + wp->yoff >= ctx->oy + ctx->sy) + return; + + if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) { + /* All visible. */ + xoff = wp->xoff - ctx->ox; + sx = wp->sx; + } else if (wp->xoff < ctx->ox && + wp->xoff + wp->sx > ctx->ox + ctx->sx) { + /* Both left and right not visible. */ + xoff = 0; + sx = ctx->sx; + } else if (wp->xoff < ctx->ox) { + /* Left not visible. */ + xoff = 0; + sx = wp->sx - (ctx->ox - wp->xoff); + } else { + /* Right not visible. */ + xoff = wp->xoff - ctx->ox; + sx = wp->sx - xoff; + } + if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) { + /* All visible. */ + yoff = wp->yoff - ctx->oy; + sy = wp->sy; + } else if (wp->yoff < ctx->oy && + wp->yoff + wp->sy > ctx->oy + ctx->sy) { + /* Both top and bottom not visible. */ + yoff = 0; + sy = ctx->sy; + } else if (wp->yoff < ctx->oy) { + /* Top not visible. */ + yoff = 0; + sy = wp->sy - (ctx->oy - wp->yoff); + } else { + /* Bottom not visible. */ + yoff = wp->yoff - ctx->oy; + sy = wp->sy - yoff; + } + + if (ctx->top) + yoff += ctx->lines; + px = sx / 2; + py = sy / 2; + if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); len = xsnprintf(buf, sizeof buf, "%u", idx); - if (wp->sx < len) + if (sx < len) return; colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); - px = wp->sx / 2; py = wp->sy / 2; - xoff = wp->xoff; yoff = wp->yoff; - - if (ctx->top) - yoff += ctx->lines; - - if (wp->sx < len * 6 || wp->sy < 5) { + if (sx < len * 6 || sy < 5) { tty_cursor(tty, xoff + px - len / 2, yoff + py); goto draw_text; } @@ -661,9 +741,9 @@ screen_redraw_draw_number(struct screen_redraw_ctx *ctx, struct window_pane *wp) } len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); - if (wp->sx < len || wp->sy < 6) + if (sx < len || sy < 6) return; - tty_cursor(tty, xoff + wp->sx - len, yoff); + tty_cursor(tty, xoff + sx - len, yoff); draw_text: memcpy(&gc, &grid_default_cell, sizeof gc); diff --git a/screen-write.c b/screen-write.c index fac5229e..4f5bee61 100644 --- a/screen-write.c +++ b/screen-write.c @@ -54,12 +54,47 @@ struct screen_write_collect_line { TAILQ_HEAD(, screen_write_collect_item) items; }; +static void +screen_write_offset_timer(__unused int fd, __unused short events, void *data) +{ + struct window *w = data; + + tty_update_window_offset(w); +} + +/* Set cursor position. */ +static void +screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) +{ + struct window_pane *wp = ctx->wp; + struct window *w; + struct screen *s = ctx->s; + struct timeval tv = { .tv_usec = 10000 }; + + if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy) + return; + + if (cx != -1) + s->cx = cx; + if (cy != -1) + s->cy = cy; + + if (wp == NULL) + return; + w = wp->window; + + if (!event_initialized(&w->offset_timer)) + evtimer_set(&w->offset_timer, screen_write_offset_timer, w); + if (!evtimer_pending(&w->offset_timer, NULL)) + evtimer_add(&w->offset_timer, &tv); +} + /* Initialize writing with a window. */ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { - char tmp[16]; + char tmp[32]; u_int y; memset(ctx, 0, sizeof *ctx); @@ -78,8 +113,10 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, ctx->scrolled = 0; ctx->bg = 8; - if (wp != NULL) - snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); + if (wp != NULL) { + snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, + wp->xoff, wp->yoff); + } log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); } @@ -603,25 +640,26 @@ void screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; if (ny == 0) ny = 1; - if (s->cy < s->rupper) { + if (cy < s->rupper) { /* Above region. */ - if (ny > s->cy) - ny = s->cy; + if (ny > cy) + ny = cy; } else { /* Below region. */ - if (ny > s->cy - s->rupper) - ny = s->cy - s->rupper; + if (ny > cy - s->rupper) + ny = cy - s->rupper; } - if (s->cx == screen_size_x(s)) - s->cx--; - if (ny == 0) - return; + if (cx == screen_size_x(s)) + cx--; - s->cy -= ny; + cy -= ny; + + screen_write_set_cursor(ctx, cx, cy); } /* Cursor down by ny. */ @@ -629,25 +667,28 @@ void screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; if (ny == 0) ny = 1; - if (s->cy > s->rlower) { + if (cy > s->rlower) { /* Below region. */ - if (ny > screen_size_y(s) - 1 - s->cy) - ny = screen_size_y(s) - 1 - s->cy; + if (ny > screen_size_y(s) - 1 - cy) + ny = screen_size_y(s) - 1 - cy; } else { /* Above region. */ - if (ny > s->rlower - s->cy) - ny = s->rlower - s->cy; + if (ny > s->rlower - cy) + ny = s->rlower - cy; } - if (s->cx == screen_size_x(s)) - s->cx--; - if (ny == 0) + if (cx == screen_size_x(s)) + cx--; + else if (ny == 0) return; - s->cy += ny; + cy += ny; + + screen_write_set_cursor(ctx, cx, cy); } /* Cursor right by nx. */ @@ -655,16 +696,19 @@ void screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; if (nx == 0) nx = 1; - if (nx > screen_size_x(s) - 1 - s->cx) - nx = screen_size_x(s) - 1 - s->cx; + if (nx > screen_size_x(s) - 1 - cx) + nx = screen_size_x(s) - 1 - cx; if (nx == 0) return; - s->cx += nx; + cx += nx; + + screen_write_set_cursor(ctx, cx, cy); } /* Cursor left by nx. */ @@ -672,16 +716,19 @@ void screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; + u_int cx = s->cx, cy = s->cy; if (nx == 0) nx = 1; - if (nx > s->cx) - nx = s->cx; + if (nx > cx) + nx = cx; if (nx == 0) return; - s->cx -= nx; + cx -= nx; + + screen_write_set_cursor(ctx, cx, cy); } /* Backspace; cursor left unless at start of wrapped line when can move up. */ @@ -690,17 +737,20 @@ screen_write_backspace(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct grid_line *gl; + u_int cx = s->cx, cy = s->cy; - if (s->cx == 0) { - if (s->cy == 0) + if (cx == 0) { + if (cy == 0) return; - gl = grid_get_line(s->grid, s->grid->hsize + s->cy - 1); + gl = grid_get_line(s->grid, s->grid->hsize + cy - 1); if (gl->flags & GRID_LINE_WRAPPED) { - s->cy--; - s->cx = screen_size_x(s) - 1; + cy--; + cx = screen_size_x(s) - 1; } } else - s->cx--; + cx--; + + screen_write_set_cursor(ctx, cx, cy); } /* VT100 alignment test. */ @@ -712,8 +762,6 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) struct grid_cell gc; u_int xx, yy; - screen_write_initctx(ctx, &ttyctx); - memcpy(&gc, &grid_default_cell, sizeof gc); utf8_set(&gc.data, 'E'); @@ -722,8 +770,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) grid_view_set_cell(s->grid, xx, yy, &gc); } - s->cx = 0; - s->cy = 0; + screen_write_set_cursor(ctx, 0, 0); s->rupper = 0; s->rlower = screen_size_y(s) - 1; @@ -988,8 +1035,7 @@ screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) if (py > screen_size_y(s) - 1) py = screen_size_y(s) - 1; - s->cx = px; - s->cy = py; + screen_write_set_cursor(ctx, px, py); } /* Reverse index (up with scroll). */ @@ -1005,7 +1051,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) if (s->cy == s->rupper) grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); else if (s->cy > 0) - s->cy--; + screen_write_set_cursor(ctx, -1, s->cy - 1); screen_write_collect_flush(ctx, 0); tty_write(tty_cmd_reverseindex, &ttyctx); @@ -1028,8 +1074,7 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, screen_write_collect_flush(ctx, 0); /* Cursor moves to top-left. */ - s->cx = 0; - s->cy = 0; + screen_write_set_cursor(ctx, 0, 0); s->rupper = rupper; s->rlower = rlower; @@ -1062,7 +1107,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) screen_write_collect_scroll(ctx); ctx->scrolled++; } else if (s->cy < screen_size_y(s) - 1) - s->cy++; + screen_write_set_cursor(ctx, -1, s->cy + 1); } /* Scroll up. */ @@ -1094,9 +1139,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) void screen_write_carriagereturn(struct screen_write_ctx *ctx) { - struct screen *s = ctx->s; - - s->cx = 0; + screen_write_set_cursor(ctx, 0, -1); } /* Clear to end of screen from cursor. */ @@ -1300,14 +1343,15 @@ screen_write_collect_end(struct screen_write_ctx *ctx) grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); } - if (gc.data.width > 1) + if (gc.data.width > 1) { grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); + } } memcpy(&gc, &ci->gc, sizeof gc); grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); - s->cx += ci->used; + screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { grid_view_get_cell(s->grid, xx, s->cy, &gc); @@ -1361,7 +1405,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx, log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); ci->wrapped = 1; screen_write_linefeed(ctx, 1, 8); - s->cx = 0; + screen_write_set_cursor(ctx, 0, -1); } if (ci->used == 0) @@ -1423,7 +1467,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) if ((s->mode & MODE_WRAP) && s->cx > sx - width) { log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); screen_write_linefeed(ctx, 1, 8); - s->cx = 0; + screen_write_set_cursor(ctx, 0, -1); screen_write_collect_flush(ctx, 1); } @@ -1496,9 +1540,9 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) */ last = !(s->mode & MODE_WRAP); if (s->cx <= sx - last - width) - s->cx += width; + screen_write_set_cursor(ctx, s->cx + width, -1); else - s->cx = sx - last; + screen_write_set_cursor(ctx, sx - last, -1); /* Create space for character in insert mode. */ if (s->mode & MODE_INSERT) { diff --git a/server-client.c b/server-client.c index 84b93272..410b8c02 100644 --- a/server-client.c +++ b/server-client.c @@ -192,6 +192,7 @@ server_client_create(int fd) c->session = NULL; c->last_session = NULL; + c->tty.sx = 80; c->tty.sy = 24; @@ -300,6 +301,7 @@ server_client_lost(struct client *c) free(msg); } + free(c->prompt_saved); free(c->prompt_string); free(c->prompt_buffer); @@ -409,18 +411,18 @@ server_client_check_mouse(struct client *c) struct mouse_event *m = &c->tty.mouse; struct window *w; struct window_pane *wp; - u_int x, y, b; + u_int x, y, b, sx, sy, px, py; int flag; key_code key; struct timeval tv; enum { NOTYPE, MOVE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type; - enum { NOWHERE, PANE, STATUS, BORDER } where; + enum { NOWHERE, PANE, STATUS, STATUS_LEFT, STATUS_RIGHT, BORDER } where; type = NOTYPE; where = NOWHERE; - log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, - m->lx, m->ly, c->tty.mouse_drag_flag); + log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b, + m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); /* What type of event is this? */ if ((m->sgr_type != ' ' && @@ -439,7 +441,7 @@ server_client_check_mouse(struct client *c) x = m->x, y = m->y, b = m->b; log_debug("drag update at %u,%u", x, y); } else { - x = m->lx, y = m->ly, b = m->lb; + x = m->lx - m->ox, y = m->ly - m->oy, b = m->lb; log_debug("drag start at %u,%u", x, y); } } else if (MOUSE_WHEEL(m->b)) { @@ -493,48 +495,74 @@ have_event: if (type == NOTYPE) return (KEYC_UNKNOWN); - /* Always save the session. */ + /* Save the session. */ m->s = s->id; + m->w = -1; /* Is this on the status line? */ m->statusat = status_at_line(c); if (m->statusat != -1 && y == (u_int)m->statusat) { - w = status_get_window_at(c, x); - if (w == NULL) - return (KEYC_UNKNOWN); - m->w = w->id; - where = STATUS; - } else - m->w = -1; + if (x < c->status.left_size) + where = STATUS_LEFT; + else if (x > c->tty.sx - c->status.right_size) + where = STATUS_RIGHT; + else { + w = status_get_window_at(c, x); + if (w == NULL) + return (KEYC_UNKNOWN); + m->w = w->id; + where = STATUS; + } + } /* Not on status line. Adjust position and check for border or pane. */ if (where == NOWHERE) { + px = x; if (m->statusat == 0 && y > 0) - y--; + py = y - 1; else if (m->statusat > 0 && y >= (u_int)m->statusat) - y = m->statusat - 1; - - TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { - if ((wp->xoff + wp->sx == x && - wp->yoff <= 1 + y && - wp->yoff + wp->sy >= y) || - (wp->yoff + wp->sy == y && - wp->xoff <= 1 + x && - wp->xoff + wp->sx >= x)) - break; + py = m->statusat - 1; + else + py = y; + + tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy); + log_debug("mouse window @%u at %u,%u (%ux%u)", + s->curw->window->id, m->ox, m->oy, sx, sy); + if (px > sx || py > sy) + return (KEYC_UNKNOWN); + px = px + m->ox; + py = py + m->oy; + m->x = x + m->ox; + m->y = y + m->oy; + + /* Try the pane borders if not zoomed. */ + if (~s->curw->window->flags & WINDOW_ZOOMED) { + TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { + if ((wp->xoff + wp->sx == px && + wp->yoff <= 1 + py && + wp->yoff + wp->sy >= py) || + (wp->yoff + wp->sy == py && + wp->xoff <= 1 + px && + wp->xoff + wp->sx >= px)) + break; + } + if (wp != NULL) + where = BORDER; } - if (wp != NULL) - where = BORDER; - else { - wp = window_get_active_at(s->curw->window, x, y); - if (wp != NULL) { + + /* Otherwise try inside the pane. */ + if (where == NOWHERE) { + wp = window_get_active_at(s->curw->window, px, py); + if (wp != NULL) where = PANE; - log_debug("mouse at %u,%u is on pane %%%u", - x, y, wp->id); - } } + if (where == NOWHERE) return (KEYC_UNKNOWN); + if (where == PANE) + log_debug("mouse %u,%u on pane %%%u", x, y, wp->id); + else if (where == BORDER) + log_debug("mouse on pane %%%u border", wp->id); m->wp = wp->id; m->w = wp->window->id; } else @@ -558,6 +586,10 @@ have_event: key = KEYC_MOUSEDRAGEND1_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND1_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDRAGEND1_BORDER; break; @@ -566,6 +598,10 @@ have_event: key = KEYC_MOUSEDRAGEND2_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND2_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDRAGEND2_BORDER; break; @@ -574,6 +610,10 @@ have_event: key = KEYC_MOUSEDRAGEND3_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAGEND3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAGEND3_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDRAGEND3_BORDER; break; @@ -609,6 +649,10 @@ have_event: key = KEYC_MOUSEDRAG1_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG1_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDRAG1_BORDER; break; @@ -617,6 +661,10 @@ have_event: key = KEYC_MOUSEDRAG2_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG2_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDRAG2_BORDER; break; @@ -625,6 +673,10 @@ have_event: key = KEYC_MOUSEDRAG3_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDRAG3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDRAG3_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDRAG3_BORDER; break; @@ -643,6 +695,10 @@ have_event: key = KEYC_WHEELUP_PANE; if (where == STATUS) key = KEYC_WHEELUP_STATUS; + if (where == STATUS_LEFT) + key = KEYC_WHEELUP_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_WHEELUP_STATUS_RIGHT; if (where == BORDER) key = KEYC_WHEELUP_BORDER; } else { @@ -661,6 +717,10 @@ have_event: key = KEYC_MOUSEUP1_PANE; if (where == STATUS) key = KEYC_MOUSEUP1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP1_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEUP1_BORDER; break; @@ -669,6 +729,10 @@ have_event: key = KEYC_MOUSEUP2_PANE; if (where == STATUS) key = KEYC_MOUSEUP2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP2_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEUP2_BORDER; break; @@ -677,6 +741,10 @@ have_event: key = KEYC_MOUSEUP3_PANE; if (where == STATUS) key = KEYC_MOUSEUP3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEUP3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEUP3_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEUP3_BORDER; break; @@ -689,6 +757,10 @@ have_event: key = KEYC_MOUSEDOWN1_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN1_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDOWN1_BORDER; break; @@ -697,6 +769,10 @@ have_event: key = KEYC_MOUSEDOWN2_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN2_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDOWN2_BORDER; break; @@ -705,6 +781,10 @@ have_event: key = KEYC_MOUSEDOWN3_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_MOUSEDOWN3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_MOUSEDOWN3_STATUS_RIGHT; if (where == BORDER) key = KEYC_MOUSEDOWN3_BORDER; break; @@ -717,6 +797,10 @@ have_event: key = KEYC_DOUBLECLICK1_PANE; if (where == STATUS) key = KEYC_DOUBLECLICK1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK1_STATUS_RIGHT; if (where == BORDER) key = KEYC_DOUBLECLICK1_BORDER; break; @@ -725,6 +809,10 @@ have_event: key = KEYC_DOUBLECLICK2_PANE; if (where == STATUS) key = KEYC_DOUBLECLICK2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK2_STATUS_RIGHT; if (where == BORDER) key = KEYC_DOUBLECLICK2_BORDER; break; @@ -733,6 +821,10 @@ have_event: key = KEYC_DOUBLECLICK3_PANE; if (where == STATUS) key = KEYC_DOUBLECLICK3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_DOUBLECLICK3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_DOUBLECLICK3_STATUS_RIGHT; if (where == BORDER) key = KEYC_DOUBLECLICK3_BORDER; break; @@ -745,6 +837,10 @@ have_event: key = KEYC_TRIPLECLICK1_PANE; if (where == STATUS) key = KEYC_TRIPLECLICK1_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK1_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK1_STATUS_RIGHT; if (where == BORDER) key = KEYC_TRIPLECLICK1_BORDER; break; @@ -753,6 +849,10 @@ have_event: key = KEYC_TRIPLECLICK2_PANE; if (where == STATUS) key = KEYC_TRIPLECLICK2_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK2_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK2_STATUS_RIGHT; if (where == BORDER) key = KEYC_TRIPLECLICK2_BORDER; break; @@ -761,6 +861,10 @@ have_event: key = KEYC_TRIPLECLICK3_PANE; if (where == STATUS) key = KEYC_TRIPLECLICK3_STATUS; + if (where == STATUS_LEFT) + key = KEYC_TRIPLECLICK3_STATUS_LEFT; + if (where == STATUS_RIGHT) + key = KEYC_TRIPLECLICK3_STATUS_RIGHT; if (where == BORDER) key = KEYC_TRIPLECLICK3_BORDER; break; @@ -836,8 +940,6 @@ server_client_handle_key(struct client *c, key_code key) return; window_unzoom(w); wp = window_pane_at_index(w, key - '0'); - if (wp != NULL && !window_pane_visible(wp)) - wp = NULL; server_client_clear_identify(c, wp); return; } @@ -1183,7 +1285,7 @@ server_client_check_focus(struct window_pane *wp) TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) continue; - if (c->session->flags & SESSION_UNATTACHED) + if (c->session->attached == 0) continue; if (c->session->curw->window == wp->window) @@ -1224,28 +1326,37 @@ server_client_reset_state(struct client *c) struct window_pane *wp = w->active, *loop; struct screen *s = wp->screen; struct options *oo = c->session->options; - int lines, mode; + int mode, cursor = 0; + u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; + mode = s->mode; tty_region_off(&c->tty); tty_margin_off(&c->tty); - if (status_at_line(c) != 0) - lines = 0; - else - lines = status_line_size(c->session); - if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - lines) - tty_cursor(&c->tty, 0, 0); - else - tty_cursor(&c->tty, wp->xoff + s->cx, lines + wp->yoff + s->cy); + /* Move cursor to pane cursor and offset. */ + cursor = 0; + tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); + if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && + wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { + cursor = 1; + + cx = wp->xoff + s->cx - ox; + cy = wp->yoff + s->cy - oy; + + if (status_at_line(c) == 0) + cy += status_line_size(c); + } + if (!cursor) + mode &= ~MODE_CURSOR; + tty_cursor(&c->tty, cx, cy); /* * Set mouse mode if requested. To support dragging, always use button * mode. */ - mode = s->mode; if (options_get_number(oo, "mouse")) { mode &= ~ALL_MOUSE_MODES; TAILQ_FOREACH(loop, &w->panes, entry) { @@ -1322,13 +1433,19 @@ server_client_check_redraw(struct client *c) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - int needed, flags, masked; + int needed, flags; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; + if (c->flags & CLIENT_ALLREDRAWFLAGS) { + log_debug("%s: redraw%s%s%s", c->name, + (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "", + (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "", + (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : ""); + } /* * If there is outstanding data, defer the redraw until it has been @@ -1336,7 +1453,7 @@ server_client_check_redraw(struct client *c) * end up back here. */ needed = 0; - if (c->flags & CLIENT_REDRAW) + if (c->flags & CLIENT_ALLREDRAWFLAGS) needed = 1; else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { @@ -1359,25 +1476,19 @@ server_client_check_redraw(struct client *c) * We may have got here for a single pane redraw, but force a * full redraw next time in case other panes have been updated. */ - c->flags |= CLIENT_REDRAW; + c->flags |= CLIENT_ALLREDRAWFLAGS; return; } else if (needed) log_debug("%s: redraw needed", c->name); - if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { - if (options_get_number(s->options, "set-titles")) - server_client_set_title(c); - screen_redraw_update(c); /* will adjust flags */ - } - flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR); tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR; - if (c->flags & CLIENT_REDRAW) { - tty_update_mode(tty, tty->mode, NULL); - screen_redraw_screen(c, 1, 1, 1); - c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); - } else { + if (~c->flags & CLIENT_REDRAWWINDOW) { + /* + * If not redrawing the entire window, check whether each pane + * needs to be redrawn. + */ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { tty_update_mode(tty, tty->mode, NULL); @@ -1386,21 +1497,16 @@ server_client_check_redraw(struct client *c) } } - masked = c->flags & (CLIENT_BORDERS|CLIENT_STATUS); - if (masked != 0) - tty_update_mode(tty, tty->mode, NULL); - if (masked == CLIENT_BORDERS) - screen_redraw_screen(c, 0, 0, 1); - else if (masked == CLIENT_STATUS) - screen_redraw_screen(c, 0, 1, 0); - else if (masked != 0) - screen_redraw_screen(c, 0, 1, 1); + if (c->flags & CLIENT_ALLREDRAWFLAGS) { + if (options_get_number(s->options, "set-titles")) + server_client_set_title(c); + screen_redraw_screen(c); + } tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; tty_update_mode(tty, tty->mode, NULL); - c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS| - CLIENT_STATUSFORCE); + c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE); if (needed) { /* diff --git a/server-fn.c b/server-fn.c index df4cc5b9..9c348341 100644 --- a/server-fn.c +++ b/server-fn.c @@ -33,13 +33,13 @@ static void server_destroy_session_group(struct session *); void server_redraw_client(struct client *c) { - c->flags |= CLIENT_REDRAW; + c->flags |= CLIENT_ALLREDRAWFLAGS; } void server_status_client(struct client *c) { - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; } void @@ -108,7 +108,7 @@ server_redraw_window_borders(struct window *w) TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && c->session->curw->window == w) - c->flags |= CLIENT_BORDERS; + c->flags |= CLIENT_REDRAWBORDERS; } } @@ -410,6 +410,7 @@ server_destroy_session(struct session *s) c->last_session = NULL; c->session = s_new; server_client_set_key_table(c, NULL); + tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s_new, NULL); @@ -431,7 +432,7 @@ server_check_unattached(void) * set, collect them. */ RB_FOREACH(s, sessions, &sessions) { - if (!(s->flags & SESSION_UNATTACHED)) + if (s->attached != 0) continue; if (options_get_number (s->options, "destroy-unattached")) session_destroy(s, __func__); @@ -161,7 +161,6 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, char *lockfile) { int pair[2]; - struct job *job; sigset_t set, oldset; struct client *c; char *cause = NULL; @@ -222,17 +221,13 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd, } start_cfg(); - server_add_accept(0); proc_loop(server_proc, server_loop); - LIST_FOREACH(job, &all_jobs, entry) { - if (job->pid != -1) - kill(job->pid, SIGTERM); - } - + job_kill_all(); status_prompt_save_history(); + exit(0); } @@ -242,7 +237,6 @@ server_loop(void) { struct client *c; u_int items; - struct job *job; do { items = cmdq_next(NULL); @@ -275,10 +269,8 @@ server_loop(void) if (!TAILQ_EMPTY(&clients)) return (0); - LIST_FOREACH(job, &all_jobs, entry) { - if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING) - return (0); - } + if (job_still_running()) + return (0); return (1); } @@ -318,7 +310,7 @@ server_update_socket(void) n = 0; RB_FOREACH(s, sessions, &sessions) { - if (!(s->flags & SESSION_UNATTACHED)) { + if (s->attached != 0) { n++; break; } @@ -456,7 +448,6 @@ server_child_exited(pid_t pid, int status) { struct window *w, *w1; struct window_pane *wp; - struct job *job; RB_FOREACH_SAFE(w, windows, &windows, w1) { TAILQ_FOREACH(wp, &w->panes, entry) { @@ -473,13 +464,7 @@ server_child_exited(pid_t pid, int status) } } } - - LIST_FOREACH(job, &all_jobs, entry) { - if (pid == job->pid) { - job_died(job, status); /* might free job */ - break; - } - } + job_check_died(pid, status); } /* Handle stopped children. */ @@ -112,8 +112,8 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * session_create(const char *prefix, const char *name, int argc, char **argv, - const char *path, const char *cwd, struct environ *env, struct termios *tio, - int idx, u_int sx, u_int sy, char **cause) + const char *path, const char *cwd, struct environ *env, struct options *oo, + struct termios *tio, int idx, char **cause) { struct session *s; struct winlink *wl; @@ -132,7 +132,7 @@ session_create(const char *prefix, const char *name, int argc, char **argv, if (env != NULL) environ_copy(env, s->environ); - s->options = options_create(global_s_options); + s->options = oo; s->hooks = hooks_create(global_hooks); status_update_saved(s); @@ -143,9 +143,6 @@ session_create(const char *prefix, const char *name, int argc, char **argv, memcpy(s->tio, tio, sizeof *s->tio); } - s->sx = sx; - s->sy = sy; - if (name != NULL) { s->name = xstrdup(name); s->id = next_session_id++; @@ -265,7 +262,7 @@ session_lock_timer(__unused int fd, __unused short events, void *arg) { struct session *s = arg; - if (s->flags & SESSION_UNATTACHED) + if (s->attached == 0) return; log_debug("session %s locked, activity time %lld", s->name, @@ -298,7 +295,7 @@ session_update_activity(struct session *s, struct timeval *from) else evtimer_set(&s->lock_timer, session_lock_timer, s); - if (~s->flags & SESSION_UNATTACHED) { + if (s->attached != 0) { timerclear(&tv); tv.tv_sec = options_get_number(s->options, "lock-after-time"); if (tv.tv_sec != 0) @@ -349,7 +346,7 @@ session_new(struct session *s, const char *name, int argc, char **argv, struct winlink *wl; struct environ *env; const char *shell; - u_int hlimit; + u_int hlimit, sx, sy; if ((wl = winlink_add(&s->windows, idx)) == NULL) { xasprintf(cause, "index in use: %d", idx); @@ -361,10 +358,11 @@ session_new(struct session *s, const char *name, int argc, char **argv, if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; + default_window_size(s, NULL, &sx, &sy, -1); hlimit = options_get_number(s->options, "history-limit"); env = environ_for_session(s, 0); w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio, - s->sx, s->sy, hlimit, cause); + sx, sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(env); @@ -547,6 +545,7 @@ session_set_current(struct session *s, struct winlink *wl) s->curw = wl; winlink_clear_flags(wl); window_update_activity(wl->window); + tty_update_window_offset(wl->window); notify_session("session-window-changed", s); return (0); } @@ -157,7 +157,7 @@ status_timer_callback(__unused int fd, __unused short events, void *arg) return; if (c->message_string == NULL && c->prompt_string == NULL) - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; timerclear(&tv); tv.tv_sec = options_get_number(s->options, "status-interval"); @@ -214,17 +214,17 @@ status_at_line(struct client *c) return (-1); if (s->statusat != 1) return (s->statusat); - return (c->tty.sy - status_line_size(s)); + return (c->tty.sy - status_line_size(c)); } -/* - * Get size of status line for session. 0 means off. Note that status line may - * be forced off for an individual client if it is too small (the - * CLIENT_STATUSOFF flag is set for this). - */ +/* Get size of status line for client's session. 0 means off. */ u_int -status_line_size(struct session *s) +status_line_size(struct client *c) { + struct session *s = c->session; + + if (c->flags & CLIENT_STATUSOFF) + return (0); if (s->statusat == -1) return (0); return (1); @@ -284,7 +284,7 @@ status_get_window_at(struct client *c, u_int x) const char *sep; size_t seplen; - x += c->wlmouse; + x += c->status.window_list_offset; RB_FOREACH(wl, winlinks, &s->windows) { oo = wl->window->options; @@ -324,7 +324,7 @@ status_redraw(struct client *c) } /* No status line? */ - lines = status_line_size(s); + lines = status_line_size(c); if (c->tty.sy == 0 || lines == 0) return (1); left = right = NULL; @@ -506,11 +506,15 @@ draw: wloffset++; /* Copy the window list. */ - c->wlmouse = -wloffset + wlstart; + c->status.window_list_offset = -wloffset + wlstart; screen_write_cursormove(&ctx, wloffset, 0); screen_write_fast_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1); screen_free(&window_list); + /* Save left and right size. */ + c->status.left_size = llen; + c->status.right_size = rlen; + screen_write_stop(&ctx); out: @@ -615,7 +619,7 @@ status_message_set(struct client *c, const char *fmt, ...) } c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; } /* Clear status line message. */ @@ -630,7 +634,7 @@ status_message_clear(struct client *c) if (c->prompt_string == NULL) c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); - c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ + c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */ screen_reinit(&c->status.status); } @@ -659,7 +663,7 @@ status_message_redraw(struct client *c) return (0); memcpy(&old_status, &c->status.status, sizeof old_status); - lines = status_line_size(c->session); + lines = status_line_size(c); if (lines <= 1) { lines = 1; screen_init(&c->status.status, c->tty.sx, 1, 0); @@ -734,7 +738,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, if (~flags & PROMPT_INCREMENTAL) c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') { xasprintf(&cp, "=%s", tmp); @@ -762,8 +766,11 @@ status_prompt_clear(struct client *c) free(c->prompt_buffer); c->prompt_buffer = NULL; + free(c->prompt_saved); + c->prompt_saved = NULL; + c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); - c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ + c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */ screen_reinit(&c->status.status); } @@ -791,7 +798,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) c->prompt_hindex = 0; - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; free(tmp); format_free(ft); @@ -812,7 +819,7 @@ status_prompt_redraw(struct client *c) return (0); memcpy(&old_status, &c->status.status, sizeof old_status); - lines = status_line_size(c->session); + lines = status_line_size(c); if (lines <= 1) { lines = 1; screen_init(&c->status.status, c->tty.sx, 1, 0); @@ -938,7 +945,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) return (1); case '\033': /* Escape */ c->prompt_mode = PROMPT_COMMAND; - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; return (0); } *new_key = key; @@ -952,17 +959,17 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) case 's': case 'a': c->prompt_mode = PROMPT_ENTRY; - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; break; /* switch mode and... */ case 'S': c->prompt_mode = PROMPT_ENTRY; - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; *new_key = '\025'; /* C-u */ return (1); case 'i': case '\033': /* Escape */ c->prompt_mode = PROMPT_ENTRY; - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; return (0); } @@ -1055,6 +1062,7 @@ status_prompt_key(struct client *c, key_code key) free(s); return (1); } + key &= ~KEYC_XTERM; keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_VI) { @@ -1212,6 +1220,12 @@ process_key: } } + free(c->prompt_saved); + c->prompt_saved = xcalloc(sizeof *c->prompt_buffer, + (c->prompt_index - idx) + 1); + memcpy(c->prompt_saved, c->prompt_buffer + idx, + (c->prompt_index - idx) * sizeof *c->prompt_buffer); + memmove(c->prompt_buffer + idx, c->prompt_buffer + c->prompt_index, (size + 1 - c->prompt_index) * @@ -1222,6 +1236,7 @@ process_key: goto changed; case 'f'|KEYC_ESCAPE: + case KEYC_RIGHT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); /* Find a word. */ @@ -1245,6 +1260,7 @@ process_key: goto changed; case 'b'|KEYC_ESCAPE: + case KEYC_LEFT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); /* Find a non-separator. */ @@ -1283,22 +1299,28 @@ process_key: c->prompt_index = utf8_strlen(c->prompt_buffer); goto changed; case '\031': /* C-y */ - if ((pb = paste_get_top(NULL)) == NULL) - break; - bufdata = paste_buffer_data(pb, &bufsize); - for (n = 0; n < bufsize; n++) { - ch = (u_char)bufdata[n]; - if (ch < 32 || ch >= 127) + if (c->prompt_saved != NULL) { + ud = c->prompt_saved; + n = utf8_strlen(c->prompt_saved); + } else { + if ((pb = paste_get_top(NULL)) == NULL) break; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; + if (ch < 32 || ch >= 127) + break; + } + ud = xreallocarray(NULL, n, sizeof *ud); + for (idx = 0; idx < n; idx++) + utf8_set(&ud[idx], bufdata[idx]); } c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1, sizeof *c->prompt_buffer); if (c->prompt_index == size) { - for (idx = 0; idx < n; idx++) { - ud = &c->prompt_buffer[c->prompt_index + idx]; - utf8_set(ud, bufdata[idx]); - } + memcpy(c->prompt_buffer + c->prompt_index, ud, + n * sizeof *c->prompt_buffer); c->prompt_index += n; c->prompt_buffer[c->prompt_index].size = 0; } else { @@ -1306,12 +1328,13 @@ process_key: c->prompt_buffer + c->prompt_index, (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer); - for (idx = 0; idx < n; idx++) { - ud = &c->prompt_buffer[c->prompt_index + idx]; - utf8_set(ud, bufdata[idx]); - } + memcpy(c->prompt_buffer + c->prompt_index, ud, + n * sizeof *c->prompt_buffer); c->prompt_index += n; } + + if (ud != c->prompt_saved) + free(ud); goto changed; case '\024': /* C-t */ idx = c->prompt_index; @@ -1357,7 +1380,7 @@ process_key: goto append_key; } - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; return (0); append_key: @@ -1392,7 +1415,7 @@ append_key: } changed: - c->flags |= CLIENT_STATUS; + c->flags |= CLIENT_REDRAWSTATUS; if (c->prompt_flags & PROMPT_INCREMENTAL) { s = utf8_tocstr(c->prompt_buffer); xasprintf(&cp, "%c%s", prefix, s); @@ -628,13 +628,13 @@ refers to a .Nm command, passed with the command and arguments separately, for example: .Bd -literal -offset indent -bind-key F1 set-window-option force-width 81 +bind-key F1 set-window-option synchronize-panes on .Ed .Pp Or if using .Xr sh 1 : .Bd -literal -offset indent -$ tmux bind-key F1 set-window-option force-width 81 +$ tmux bind-key F1 set-window-option synchronize-panes on .Ed .Pp Multiple commands may be specified together as part of a @@ -850,13 +850,22 @@ and are the name of and shell command to execute in the initial window. With .Fl d , -the initial size is 80 x 24; +the initial size comes from the global +.Ar default-size +option; .Fl x and .Fl y can be used to specify a different size. .Ql - uses the size of the current client if any. +If +.Fl x +or +.Fl y +is given, the +.Ar default-size +option is set for the session. .Pp If run from a terminal, any .Xr termios 4 @@ -921,9 +930,10 @@ is used, the .Ic update-environment option will not be applied. .It Xo Ic refresh-client -.Op Fl C Ar width,height -.Op Fl S +.Op Fl cDlLRSU +.Op Fl C Ar XxY .Op Fl t Ar target-client +.Op Ar adjustment .Xc .D1 (alias: Ic refresh ) Refresh the current client if bound to a key, or a single client if one is given @@ -933,8 +943,42 @@ If .Fl S is specified, only update the client's status line. .Pp +The +.Fl U , +.Fl D , +.Fl L +.Fl R , +and +.Fl c +flags allow the visible portion of a window which is larger than the client +to be changed. +.Fl U +moves the visible part up by +.Ar adjustment +rows and +.Fl D +down, +.Fl L +left by +.Ar adjustment +columns and +.Fl R +right. +.Fl c +returns to tracking the cursor automatically. +If +.Ar adjustment +is omitted, 1 is used. +Note that the visible position is a property of the client not of the +window, changing the current window in the attached session will reset +it. +.Pp .Fl C sets the width and height of a control client. +.Fl l +requests the clipboard from the client using the +.Xr xterm 1 +escape sequence and stores it in a new paste buffer. .It Xo Ic rename-session .Op Fl t Ar target-session .Ar new-name @@ -1544,7 +1588,7 @@ The default .Ar template is "select-pane -t '%%'". .It Xo Ic find-window -.Op Fl CNT +.Op Fl CNTZ .Op Fl t Ar target-pane .Ar match-string .Xc @@ -1563,6 +1607,8 @@ matches only the window name and matches only the window title. The default is .Fl CNT . +.Fl Z +zooms the pane. .Pp This command works only if at least one client is attached. .It Xo Ic join-pane @@ -1916,6 +1962,40 @@ and unzoomed (its normal position in the layout). .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.It Xo Ic resize-window +.Op Fl aADLUR +.Op Fl t Ar target-window +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar adjustment +.Xc +.D1 (alias: Ic resizew ) +Resize a window, up, down, left or right by +.Ar adjustment +with +.Fl U , +.Fl D , +.Fl L +or +.Fl R , +to an absolute size +with +.Fl x +or +.Fl y , +or to the size of the smallest or largest session (with +.Fl a +or +.Fl A ) . +The +.Ar adjustment +is given in lines or cells (the default is 1). +.Pp +This command automatically sets the +.Ic window-size +option to +.Ar manual +for the window. .It Xo Ic respawn-pane .Op Fl c Ar start-directory .Op Fl k @@ -2659,6 +2739,16 @@ The default is an empty string, which instructs to create a login shell using the value of the .Ic default-shell option. +.It Ic default-size Ar XxY +Set the default size of new windows when the +.Ar window-size +option is set to manual or when a session is created with +.Ic new-session +.Fl d . +The value is the width and height separated by an +.Ql x +character. +The default is 80x24. .It Ic default-shell Ar path Specify the default shell. This is used as the login shell for new windows when the @@ -2771,8 +2861,12 @@ or a comma-delimited list of one or more of: .Ic reverse , .Ic hidden , .Ic italics , +.Ic strikethrough , +.Ic double-underscore +.Ic curly-underscore +.Ic dotted-underscore or -.Ic strikethrough +.Ic dashed-underscore to turn an attribute on, or an attribute prefixed with .Ql no to turn one off. @@ -3055,10 +3149,13 @@ Supported window options are: Aggressively resize the chosen window. This means that .Nm -will resize the window to the size of the smallest session for which it is the -current window, rather than the smallest session to which it is attached. -The window may resize when the current window is changed on another sessions; -this option is good for full-screen programs which support +will resize the window to the size of the smallest or largest session +(see the +.Ic window-size +option) for which it is the current window, rather than the session to +which it is attached. +The window may resize when the current window is changed on another +session; this option is good for full-screen programs which support .Dv SIGWINCH and poor for interactive programs such as shells. .Pp @@ -3121,16 +3218,6 @@ Set clock colour. .Xc Set clock hour format. .Pp -.It Ic force-height Ar height -.It Ic force-width Ar width -Prevent -.Nm -from resizing a window to greater than -.Ar width -or -.Ar height . -A value of zero restores the default unlimited setting. -.Pp .It Ic main-pane-height Ar height .It Ic main-pane-width Ar width Set the width or height of the main (left or top) pane in the @@ -3310,6 +3397,28 @@ see the .Ic message-command-style option. .Pp +.It Xo Ic Ic window-size +.Ar largest | Ar smallest | Ar manual +.Xc +Configure how +.Nm +determines the window size. +If set to +.Ar largest , +the size of the largest attached session is used; if +.Ar smallest , +the size of the smallest. +If +.Ar manual , +the size of a new window is set from the +.Ic default-size +option and windows are resized automatically. +See also the +.Ic resize-window +command and the +.Ic aggressive-resize +option. +.Pp .It Ic window-style Ar style Set the default window style. For how to specify @@ -3506,7 +3615,10 @@ and a location suffix (one of for the contents of a pane, .Ql Border for a pane border or -.Ql Status +.Ql Status , +.Ql StatusLeft , +or +.Ql StatusRight for the status line). The following mouse events are available: .Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent @@ -3675,6 +3787,10 @@ prefixes are and .Xr dirname 3 of the variable respectively. +.Ql q: +will escape +.Xr sh 1 +special characters. A prefix of the form .Ql s/foo/bar/: will substitute @@ -3801,12 +3917,10 @@ The following variables are available, where appropriate: .It Li "session_group_size" Ta "" Ta "Size of session group" .It Li "session_group_list" Ta "" Ta "List of sessions in group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" -.It Li "session_height" Ta "" Ta "Height of session" .It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" -.It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" @@ -3815,6 +3929,7 @@ The following variables are available, where appropriate: .It Li "window_activity_flag" Ta "" Ta "1 if window has activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" +.It Li "window_bigger" Ta "" Ta "1 if window is larger than client" .It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)" .It Li "window_height" Ta "" Ta "Height of window" @@ -3824,6 +3939,8 @@ The following variables are available, where appropriate: .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_name" Ta "#W" Ta "Name of window" +.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" +.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" @@ -3883,7 +4000,7 @@ option. .El .Pp When a pane is first created, its title is the hostname. -A pane's title can be set via the OSC title setting sequence, for example: +A pane's title can be set via the title setting escape sequence, for example: .Bd -literal -offset indent $ printf '\e033]2;My Title\e033\e\e' .Ed @@ -4416,6 +4533,11 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed +.It Em \&Smulx +Set a styled underline. +The single parameter is one of: 0 for no underline, 1 for normal +underline, 2 for double underline, 3 for curly underline, 4 for dotted +underline and 5 for dashed underline. .It Em \&Ss , Se Set or reset the cursor style. If set, a sequence such as this may be used @@ -4432,7 +4554,7 @@ Indicate that the terminal supports the .Ql direct colour RGB escape sequence (for example, \ee[38;2;255;255;255m). .Pp -If supported, this is used for the OSC initialize colour escape sequence (which +If supported, this is used for the initialize colour escape sequence (which may be enabled by adding the .Ql initc and @@ -46,6 +46,7 @@ struct cmdq_list; struct environ; struct format_job_tree; struct input_ctx; +struct job; struct mode_tree_data; struct mouse_event; struct options; @@ -62,11 +63,12 @@ struct tmuxproc; #define TMUX_CONF "/etc/tmux.conf" #endif -/* - * Minimum layout cell size, NOT including separator line. The scroll region - * cannot be one line in height so this must be at least two. - */ -#define PANE_MINIMUM 2 +/* Minimum layout cell size, NOT including border lines. */ +#define PANE_MINIMUM 1 + +/* Minimum and maximum window size. */ +#define WINDOW_MINIMUM PANE_MINIMUM +#define WINDOW_MAXIMUM 10000 /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 @@ -120,13 +122,17 @@ struct tmuxproc; #define KEYC_CLICK_TIMEOUT 300 /* Mouse key codes. */ -#define KEYC_MOUSE_KEY(name) \ - KEYC_ ## name ## _PANE, \ - KEYC_ ## name ## _STATUS, \ +#define KEYC_MOUSE_KEY(name) \ + KEYC_ ## name ## _PANE, \ + KEYC_ ## name ## _STATUS, \ + KEYC_ ## name ## _STATUS_LEFT, \ + KEYC_ ## name ## _STATUS_RIGHT, \ KEYC_ ## name ## _BORDER -#define KEYC_MOUSE_STRING(name, s) \ - { #s "Pane", KEYC_ ## name ## _PANE }, \ - { #s "Status", KEYC_ ## name ## _STATUS }, \ +#define KEYC_MOUSE_STRING(name, s) \ + { #s "Pane", KEYC_ ## name ## _PANE }, \ + { #s "Status", KEYC_ ## name ## _STATUS }, \ + { #s "StatusLeft", KEYC_ ## name ## _STATUS_LEFT }, \ + { #s "StatusRight", KEYC_ ## name ## _STATUS_RIGHT }, \ { #s "Border", KEYC_ ## name ## _BORDER } /* @@ -423,6 +429,7 @@ enum tty_code_code { TTYC_SMCUP, TTYC_SMKX, TTYC_SMSO, + TTYC_SMULX, TTYC_SMUL, TTYC_SMXX, TTYC_SS, @@ -548,6 +555,18 @@ enum utf8_state { #define GRID_ATTR_ITALICS 0x40 #define GRID_ATTR_CHARSET 0x80 /* alternative character set */ #define GRID_ATTR_STRIKETHROUGH 0x100 +#define GRID_ATTR_UNDERSCORE_2 0x200 +#define GRID_ATTR_UNDERSCORE_3 0x400 +#define GRID_ATTR_UNDERSCORE_4 0x800 +#define GRID_ATTR_UNDERSCORE_5 0x1000 + +/* All underscore attributes. */ +#define GRID_ATTR_ALL_UNDERSCORE \ + (GRID_ATTR_UNDERSCORE| \ + GRID_ATTR_UNDERSCORE_2| \ + GRID_ATTR_UNDERSCORE_3| \ + GRID_ATTR_UNDERSCORE_4| \ + GRID_ATTR_UNDERSCORE_5) /* Grid flags. */ #define GRID_FLAG_FG256 0x1 @@ -619,37 +638,6 @@ struct hook { RB_ENTRY(hook) entry; }; -/* Scheduled job. */ -struct job; -typedef void (*job_update_cb) (struct job *); -typedef void (*job_complete_cb) (struct job *); -typedef void (*job_free_cb) (void *); -struct job { - enum { - JOB_RUNNING, - JOB_DEAD, - JOB_CLOSED - } state; - - int flags; -#define JOB_NOWAIT 0x1 - - char *cmd; - pid_t pid; - int status; - - int fd; - struct bufferevent *event; - - job_update_cb updatecb; - job_complete_cb completecb; - job_free_cb freecb; - void *data; - - LIST_ENTRY(job) entry; -}; -LIST_HEAD(joblist, job); - /* Virtual screen. */ struct screen_sel; struct screen_titles; @@ -809,6 +797,7 @@ struct window { struct timeval name_time; struct event alerts_timer; + struct event offset_timer; struct timeval activity_time; @@ -829,9 +818,7 @@ struct window { #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 -#define WINDOW_FORCEWIDTH 0x10 -#define WINDOW_FORCEHEIGHT 0x20 -#define WINDOW_STYLECHANGED 0x40 +#define WINDOW_STYLECHANGED 0x10 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; @@ -872,6 +859,11 @@ struct winlink { RB_HEAD(winlinks, winlink); TAILQ_HEAD(winlink_stack, winlink); +/* Window size option. */ +#define WINDOW_SIZE_LARGEST 0 +#define WINDOW_SIZE_SMALLEST 1 +#define WINDOW_SIZE_MANUAL 2 + /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, @@ -930,9 +922,6 @@ struct session { struct event lock_timer; - u_int sx; - u_int sy; - struct winlink *curw; struct winlink_stack lastw; struct winlinks windows; @@ -942,9 +931,8 @@ struct session { struct hooks *hooks; struct options *options; -#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ -#define SESSION_PASTING 0x2 -#define SESSION_ALERTED 0x4 +#define SESSION_PASTING 0x1 +#define SESSION_ALERTED 0x2 int flags; u_int attached; @@ -970,7 +958,7 @@ RB_HEAD(sessions, session); /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 -#define MOUSE_WHEEL_DOWN 64 +#define MOUSE_WHEEL_DOWN 1 /* Mouse helpers. */ #define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) @@ -993,6 +981,9 @@ struct mouse_event { u_int ly; u_int lb; + u_int ox; + u_int oy; + int s; int w; int wp; @@ -1040,6 +1031,12 @@ struct tty { u_int cstyle; char *ccolour; + int oflag; + u_int oox; + u_int ooy; + u_int osx; + u_int osy; + int mode; u_int rlower; @@ -1120,11 +1117,19 @@ struct tty_ctx { u_int orupper; u_int orlower; + /* Pane offset. */ u_int xoff; u_int yoff; /* The background colour used for clearing (erasing). */ u_int bg; + + /* Window offset and size. */ + int bigger; + u_int ox; + u_int oy; + u_int sx; + u_int sy; }; /* Saved message entry. */ @@ -1283,8 +1288,14 @@ struct cmd_entry { /* Status line. */ struct status_line { struct event timer; + struct screen status; struct screen *old_status; + + int window_list_offset; + + u_int left_size; + u_int right_size; }; /* Client connection. */ @@ -1334,14 +1345,14 @@ struct client { #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 #define CLIENT_EXIT 0x4 -#define CLIENT_REDRAW 0x8 -#define CLIENT_STATUS 0x10 +#define CLIENT_REDRAWWINDOW 0x8 +#define CLIENT_REDRAWSTATUS 0x10 #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 #define CLIENT_ATTACHED 0x80 #define CLIENT_IDENTIFY 0x100 #define CLIENT_DEAD 0x200 -#define CLIENT_BORDERS 0x400 +#define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_READONLY 0x800 #define CLIENT_DETACHING 0x1000 #define CLIENT_CONTROL 0x2000 @@ -1355,6 +1366,17 @@ struct client { #define CLIENT_TRIPLECLICK 0x200000 #define CLIENT_SIZECHANGED 0x400000 #define CLIENT_STATUSOFF 0x800000 +#define CLIENT_REDRAWSTATUSALWAYS 0x1000000 +#define CLIENT_ALLREDRAWFLAGS \ + (CLIENT_REDRAWWINDOW| \ + CLIENT_REDRAWSTATUS| \ + CLIENT_REDRAWSTATUSALWAYS| \ + CLIENT_REDRAWBORDERS) +#define CLIENT_NOSIZEFLAGS \ + (CLIENT_EXIT| \ + CLIENT_DEAD| \ + CLIENT_SUSPENDED| \ + CLIENT_DETACHING) int flags; struct key_table *keytable; @@ -1377,6 +1399,7 @@ struct client { void *prompt_data; u_int prompt_hindex; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; + struct utf8_data *prompt_saved; #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 @@ -1391,6 +1414,10 @@ struct client { int references; + void *pan_window; + u_int pan_ox; + u_int pan_oy; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -1451,6 +1478,7 @@ struct options_table_entry { const char *separator; const char *style; + const char *pattern; }; /* Common command usages. */ @@ -1617,11 +1645,20 @@ void options_style_update_old(struct options *, extern const struct options_table_entry options_table[]; /* job.c */ -extern struct joblist all_jobs; +typedef void (*job_update_cb) (struct job *); +typedef void (*job_complete_cb) (struct job *); +typedef void (*job_free_cb) (void *); +#define JOB_NOWAIT 0x1 struct job *job_run(const char *, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int); void job_free(struct job *); -void job_died(struct job *, int); +void job_check_died(pid_t, int); +int job_get_status(struct job *); +void *job_get_data(struct job *); +struct bufferevent *job_get_event(struct job *); +void job_kill_all(void); +int job_still_running(void); +void job_print_summary(struct cmdq_item *, int); /* environ.c */ struct environ *environ_create(void); @@ -1642,6 +1679,10 @@ struct environ *environ_for_session(struct session *, int); /* tty.c */ void tty_create_log(void); +int tty_window_bigger(struct tty *); +int tty_window_offset(struct tty *, u_int *, u_int *, u_int *, u_int *); +void tty_update_window_offset(struct window *); +void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, const struct window_pane *); @@ -1666,10 +1707,8 @@ void tty_start_tty(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); -void tty_draw_pane(struct tty *, const struct window_pane *, u_int, u_int, - u_int); void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, - u_int, u_int, u_int); + u_int, u_int, u_int, u_int, u_int); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); @@ -1902,9 +1941,9 @@ void server_unzoom_window(struct window *); /* status.c */ void status_timer_start(struct client *); void status_timer_start_all(void); -void status_update_saved(struct session *s); +void status_update_saved(struct session *); int status_at_line(struct client *); -u_int status_line_size(struct session *); +u_int status_line_size(struct client *); struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); void printflike(2, 3) status_message_set(struct client *, const char *, ...); @@ -1920,6 +1959,9 @@ void status_prompt_load_history(void); void status_prompt_save_history(void); /* resize.c */ +void resize_window(struct window *, u_int, u_int); +void default_window_size(struct session *, struct window *, u_int *, + u_int *, int); void recalculate_sizes(void); /* input.c */ @@ -1987,10 +2029,10 @@ void grid_view_scroll_region_up(struct grid *, u_int, u_int, u_int); void grid_view_scroll_region_down(struct grid *, u_int, u_int, u_int); void grid_view_insert_lines(struct grid *, u_int, u_int, u_int); void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int, - u_int); + u_int); void grid_view_delete_lines(struct grid *, u_int, u_int, u_int); void grid_view_delete_lines_region(struct grid *, u_int, u_int, u_int, - u_int); + u_int); void grid_view_insert_cells(struct grid *, u_int, u_int, u_int, u_int); void grid_view_delete_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); @@ -2055,8 +2097,7 @@ void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); /* screen-redraw.c */ -void screen_redraw_update(struct client *); -void screen_redraw_screen(struct client *, int, int, int); +void screen_redraw_screen(struct client *); void screen_redraw_pane(struct client *, struct window_pane *); /* screen.c */ @@ -2179,7 +2220,7 @@ void layout_set_size(struct layout_cell *, u_int, u_int, u_int, void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct layout_cell *); -void layout_fix_panes(struct window *, u_int, u_int); +void layout_fix_panes(struct window *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); void layout_init(struct window *, struct window_pane *); @@ -2295,7 +2336,7 @@ struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, int, char **, const char *, const char *, struct environ *, - struct termios *, int, u_int, u_int, char **); + struct options *, struct termios *, int, char **); void session_destroy(struct session *, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); @@ -19,7 +19,10 @@ #include <sys/types.h> #include <sys/time.h> +#include <netinet/in.h> + #include <limits.h> +#include <resolv.h> #include <stdlib.h> #include <string.h> #include <termios.h> @@ -44,6 +47,8 @@ static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, size_t *, int); static void tty_keys_callback(int, short, void *); static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); +static int tty_keys_clipboard(struct tty *, const char *, size_t, + size_t *); static int tty_keys_device_attributes(struct tty *, const char *, size_t, size_t *); @@ -571,6 +576,17 @@ tty_keys_next(struct tty *tty) return (0); log_debug("%s: keys are %zu (%.*s)", c->name, len, (int)len, buf); + /* Is this a clipboard response? */ + switch (tty_keys_clipboard(tty, buf, len, &size)) { + case 0: /* yes */ + key = KEYC_UNKNOWN; + goto complete_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + /* Is this a device attributes response? */ switch (tty_keys_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ @@ -872,6 +888,93 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) } /* + * Handle OSC 52 clipboard input. Returns 0 for success, -1 for failure, 1 for + * partial. + */ +static int +tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, + size_t *size) +{ + size_t end, terminator, needed; + char *copy, *out; + int outlen; + + *size = 0; + + /* First three bytes are always \033]52;. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != ']') + return (-1); + if (len == 2) + return (1); + if (buf[2] != '5') + return (-1); + if (len == 3) + return (1); + if (buf[3] != '2') + return (-1); + if (len == 4) + return (1); + if (buf[4] != ';') + return (-1); + if (len == 5) + return (1); + + /* Find the terminator if any. */ + for (end = 5; end < len; end++) { + if (buf[end] == '\007') { + terminator = 1; + break; + } + if (end > 5 && buf[end - 1] == '\033' && buf[end] == '\\') { + terminator = 2; + break; + } + } + if (end == len) + return (1); + *size = end + terminator; + + /* Skip the initial part. */ + buf += 5; + end -= 5; + + /* Get the second argument. */ + while (end != 0 && *buf != ';') { + buf++; + end--; + } + if (end == 0 || end == 1) + return (0); + buf++; + end--; + + /* It has to be a string so copy it. */ + copy = xmalloc(end + 1); + memcpy(copy, buf, end); + copy[end] = '\0'; + + /* Convert from base64. */ + needed = (end / 4) * 3; + out = xmalloc(needed); + if ((outlen = b64_pton(copy, out, len)) == -1) { + free(out); + free(copy); + return (0); + } + free(copy); + + /* Create a new paste buffer. */ + log_debug("%s: %.*s", __func__, outlen, out); + paste_add(out, outlen); + + return (0); +} + +/* * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for * partial. */ @@ -256,6 +256,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMCUP] = { TTYCODE_STRING, "smcup" }, [TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, + [TTYC_SMULX] = { TTYCODE_STRING, "Smulx" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, @@ -302,25 +303,53 @@ tty_term_strip(const char *s) return (xstrdup(buf)); } +static char * +tty_term_override_next(const char *s, size_t *offset) +{ + static char value[BUFSIZ]; + size_t n = 0, at = *offset; + + if (s[at] == '\0') + return (NULL); + + while (s[at] != '\0') { + if (s[at] == ':') { + if (s[at + 1] == ':') { + value[n++] = ':'; + at += 2; + } else + break; + } else { + value[n++] = s[at]; + at++; + } + if (n == (sizeof value) - 1) + return (NULL); + } + if (s[at] != '\0') + *offset = at + 1; + else + *offset = at; + value[n] = '\0'; + return (value); +} + static void tty_term_override(struct tty_term *term, const char *override) { const struct tty_term_code_entry *ent; struct tty_code *code; - char *next, *s, *copy, *cp, *value; + size_t offset = 0; + char *cp, *value, *s; const char *errstr; u_int i; int n, remove; - copy = next = xstrdup(override); - - s = strsep(&next, ":"); - if (s == NULL || next == NULL || fnmatch(s, term->name, 0) != 0) { - free(copy); + s = tty_term_override_next(override, &offset); + if (s == NULL || fnmatch(s, term->name, 0) != 0) return; - } - while ((s = strsep(&next, ":")) != NULL) { + while ((s = tty_term_override_next(override, &offset)) != NULL) { if (*s == '\0') continue; value = NULL; @@ -341,6 +370,8 @@ tty_term_override(struct tty_term *term, const char *override) if (remove) log_debug("%s override: %s@", term->name, s); + else if (*value == '\0') + log_debug("%s override: %s", term->name, s); else log_debug("%s override: %s=%s", term->name, s, value); @@ -379,7 +410,6 @@ tty_term_override(struct tty_term *term, const char *override) free(value); } - free(s); } struct tty_term * @@ -616,14 +646,14 @@ tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, i const char * tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) { - return (tparm((char *) tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0)); + return (tparm((char *) tty_term_string(term, code), (long)a, 0, 0, 0, 0, 0, 0, 0, 0)); } const char * tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, const void *b) { - return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); + return (tparm((char *) tty_term_string(term, code), (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0)); } int @@ -64,6 +64,7 @@ static void tty_redraw_region(struct tty *, const struct tty_ctx *); static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_repeat_space(struct tty *, u_int); +static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_cell(struct tty *, const struct grid_cell *, const struct window_pane *); static void tty_default_colours(struct grid_cell *, @@ -179,7 +180,7 @@ tty_timer_callback(__unused int fd, __unused short events, void *data) log_debug("%s: %zu discarded", c->name, tty->discarded); - c->flags |= CLIENT_REDRAW; + c->flags |= CLIENT_ALLREDRAWFLAGS; c->discarded += tty->discarded; if (tty->discarded < TTY_BLOCK_STOP(tty)) { @@ -698,6 +699,125 @@ tty_repeat_space(struct tty *tty, u_int n) tty_putn(tty, s, n, n); } +/* Is this window bigger than the terminal? */ +int +tty_window_bigger(struct tty *tty) +{ + struct client *c = tty->client; + struct window *w = c->session->curw->window; + + return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy); +} + +/* What offset should this window be drawn at? */ +int +tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) +{ + *ox = tty->oox; + *oy = tty->ooy; + *sx = tty->osx; + *sy = tty->osy; + + return (tty->oflag); +} + +/* What offset should this window be drawn at? */ +static int +tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) +{ + struct client *c = tty->client; + struct window *w = c->session->curw->window; + struct window_pane *wp = w->active; + u_int cx, cy, lines; + + lines = status_line_size(c); + + if (tty->sx >= w->sx && tty->sy - lines >= w->sy) { + *ox = 0; + *oy = 0; + *sx = w->sx; + *sy = w->sy; + + c->pan_window = NULL; + return (0); + } + + *sx = tty->sx; + *sy = tty->sy - lines; + + if (c->pan_window == w) { + if (c->pan_ox + *sx > w->sx) + c->pan_ox = w->sx - *sx; + *ox = c->pan_ox; + if (c->pan_oy + *sy > w->sy) + c->pan_oy = w->sy - *sy; + *oy = c->pan_oy; + return (1); + } + + if (~wp->screen->mode & MODE_CURSOR) { + *ox = 0; + *oy = 0; + } else { + cx = wp->xoff + wp->screen->cx; + cy = wp->yoff + wp->screen->cy; + + if (cx < *sx) + *ox = 0; + else if (cx > w->sx - *sx) + *ox = w->sx - *sx; + else + *ox = cx - *sx / 2; + + if (cy < *sy) + *oy = 0; + else if (cy > w->sy - *sy) + *oy = w->sy - *sy; + else + *oy = cy - *sy / 2; + } + + c->pan_window = NULL; + return (1); +} + +/* Update stored offsets for a window and redraw if necessary. */ +void +tty_update_window_offset(struct window *w) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && c->session->curw->window == w) + tty_update_client_offset(c); + } +} + +/* Update stored offsets for a client and redraw if necessary. */ +void +tty_update_client_offset(struct client *c) +{ + u_int ox, oy, sx, sy; + + c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy); + if (ox == c->tty.oox && + oy == c->tty.ooy && + sx == c->tty.osx && + sy == c->tty.osy) + return; + + log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)", + __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy, + ox, oy, sx, sy); + + c->tty.oox = ox; + c->tty.ooy = oy; + c->tty.osx = sx; + c->tty.osy = sy; + + c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); +} + /* * Is the region large enough to be worth redrawing once later rather than * probably several times now? Currently yes if it is more than 50% of the @@ -755,18 +875,82 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { for (i = ctx->ocy; i < screen_size_y(s); i++) - tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, ctx, i); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, ctx, i); + } +} + +/* Is this position visible in the pane? */ +static int +tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, + u_int nx, u_int ny) +{ + u_int xoff = ctx->xoff + px, yoff = ctx->yoff + py, lines; + + if (!ctx->bigger) + return (1); + + if (status_at_line(tty->client) == 0) + lines = status_line_size(tty->client); + else + lines = 0; + + if (xoff + nx <= ctx->ox || xoff >= ctx->ox + ctx->sx || + yoff + ny <= ctx->oy || yoff >= lines + ctx->oy + ctx->sy) { + return (0); } + return (1); } +/* Clamp line position to visible part of pane. */ +static int +tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, + u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) +{ + struct window_pane *wp = ctx->wp; + u_int xoff = wp->xoff + px; + + if (!tty_is_visible(tty, ctx, px, py, nx, 1)) + return (0); + *ry = ctx->yoff + py - ctx->oy; + + if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { + /* All visible. */ + *i = 0; + *x = ctx->xoff + px - ctx->ox; + *rx = nx; + } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { + /* Both left and right not visible. */ + *i = ctx->ox; + *x = 0; + *rx = ctx->sx; + } else if (xoff < ctx->ox) { + /* Left not visible. */ + *i = ctx->ox - (ctx->xoff + px); + *x = 0; + *rx = nx - *i; + } else { + /* Right not visible. */ + *i = 0; + *x = (ctx->xoff + px) - ctx->ox; + *rx = ctx->sx - *x; + } + if (*rx > nx) + fatalx("%s: x too big, %u > %u", __func__, *rx, nx); + + return (1); +} + +/* Clear a line. */ static void tty_clear_line(struct tty *tty, const struct window_pane *wp, u_int py, u_int px, u_int nx, u_int bg) { - log_debug("%s: %u at %u,%u", __func__, nx, px, py); + struct client *c = tty->client; + + log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); /* Nothing to clear. */ if (nx == 0) @@ -801,14 +985,93 @@ tty_clear_line(struct tty *tty, const struct window_pane *wp, u_int py, tty_repeat_space(tty, nx); } +/* Clear a line, adjusting to visible part of pane. */ +static void +tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py, + u_int px, u_int nx, u_int bg) +{ + struct client *c = tty->client; + u_int i, x, rx, ry; + + log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); + + if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) + tty_clear_line(tty, ctx->wp, ry, x, rx, bg); +} + +/* Clamp area position to visible part of pane. */ +static int +tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, + u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, + u_int *ry) +{ + struct window_pane *wp = ctx->wp; + u_int xoff = wp->xoff + px, yoff = wp->yoff + py; + + if (!tty_is_visible(tty, ctx, px, py, nx, ny)) + return (0); + + if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { + /* All visible. */ + *i = 0; + *x = ctx->xoff + px - ctx->ox; + *rx = nx; + } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { + /* Both left and right not visible. */ + *i = ctx->ox; + *x = 0; + *rx = ctx->sx; + } else if (xoff < ctx->ox) { + /* Left not visible. */ + *i = ctx->ox - (ctx->xoff + px); + *x = 0; + *rx = nx - *i; + } else { + /* Right not visible. */ + *i = 0; + *x = (ctx->xoff + px) - ctx->ox; + *rx = ctx->sx - *x; + } + if (*rx > nx) + fatalx("%s: x too big, %u > %u", __func__, *rx, nx); + + if (yoff >= ctx->oy && yoff + ny <= ctx->oy + ctx->sy) { + /* All visible. */ + *j = 0; + *y = ctx->yoff + py - ctx->oy; + *ry = ny; + } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { + /* Both left and right not visible. */ + *j = ctx->oy; + *y = 0; + *ry = ctx->sy; + } else if (yoff < ctx->oy) { + /* Left not visible. */ + *j = ctx->oy - (ctx->yoff + py); + *y = 0; + *ry = ny - *j; + } else { + /* Right not visible. */ + *j = 0; + *y = (ctx->yoff + py) - ctx->oy; + *ry = ctx->sy - *y; + } + if (*ry > ny) + fatalx("%s: y too big, %u > %u", __func__, *ry, ny); + + return (1); +} + +/* Clear an area, adjusting to visible part of pane. */ static void tty_clear_area(struct tty *tty, const struct window_pane *wp, u_int py, u_int ny, u_int px, u_int nx, u_int bg) { - u_int yy; - char tmp[64]; + struct client *c = tty->client; + u_int yy; + char tmp[64]; - log_debug("%s: %u,%u at %u,%u", __func__, nx, ny, px, py); + log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py); /* Nothing to clear. */ if (nx == 0 || ny == 0) @@ -871,11 +1134,32 @@ tty_clear_area(struct tty *tty, const struct window_pane *wp, u_int py, tty_clear_line(tty, wp, yy, px, nx, bg); } -void -tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, - u_int oy) +/* Clear an area in a pane. */ +static void +tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py, + u_int ny, u_int px, u_int nx, u_int bg) { - tty_draw_line(tty, wp, wp->screen, py, ox, oy); + u_int i, j, x, y, rx, ry; + + if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry)) + tty_clear_area(tty, ctx->wp, y, ry, x, rx, bg); +} + +static void +tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int nx = screen_size_x(s), i, x, rx, ry; + + log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); + + if (!ctx->bigger) { + tty_draw_line(tty, wp, s, 0, py, nx, ctx->xoff, ctx->yoff + py); + return; + } + if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) + tty_draw_line(tty, wp, s, i, py, rx, x, ry); } static const struct grid_cell * @@ -904,17 +1188,27 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) void tty_draw_line(struct tty *tty, const struct window_pane *wp, - struct screen *s, u_int py, u_int ox, u_int oy) + struct screen *s, u_int px, u_int py, u_int nx, u_int atx, u_int aty) { struct grid *gd = s->grid; struct grid_cell gc, last; const struct grid_cell *gcp; - u_int i, j, ux, sx, nx, width; + struct grid_line *gl; + u_int i, j, ux, sx, width; int flags, cleared = 0; char buf[512]; size_t len, old_len; u_int cellsize; + log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, + px, py, nx, atx, aty); + + /* + * py is the line in the screen to draw. + * px is the start x and nx is the width to draw. + * atx,aty is the line on the terminal to draw it. + */ + flags = (tty->flags & TTY_NOCURSOR); tty->flags |= TTY_NOCURSOR; tty_update_mode(tty, tty->mode, s); @@ -927,41 +1221,48 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, * there may be empty background cells after it (from BCE). */ sx = screen_size_x(s); - + if (nx > sx) + nx = sx; cellsize = grid_get_line(gd, gd->hsize + py)->cellsize; if (sx > cellsize) sx = cellsize; if (sx > tty->sx) sx = tty->sx; + if (sx > nx) + sx = nx; ux = 0; + if (py == 0) + gl = NULL; + else + gl = grid_get_line(gd, gd->hsize + py - 1); if (wp == NULL || - py == 0 || - (~grid_get_line(gd, gd->hsize + py - 1)->flags & GRID_LINE_WRAPPED) || - ox != 0 || + gl == NULL || + (~gl->flags & GRID_LINE_WRAPPED) || + atx != 0 || tty->cx < tty->sx || - screen_size_x(s) < tty->sx) { - if (screen_size_x(s) < tty->sx && - ox == 0 && - sx != screen_size_x(s) && + nx < tty->sx) { + if (nx < tty->sx && + atx == 0 && + px + sx != nx && tty_term_has(tty->term, TTYC_EL1) && !tty_fake_bce(tty, wp, 8)) { tty_default_attributes(tty, wp, 8); - tty_cursor(tty, screen_size_x(s) - 1, oy + py); + tty_cursor(tty, nx - 1, aty); tty_putcode(tty, TTYC_EL1); cleared = 1; } - if (sx != 0) - tty_cursor(tty, ox, oy + py); + if (px + sx != 0) + tty_cursor(tty, atx, aty); } else - log_debug("%s: wrapped line %u", __func__, oy + py); + log_debug("%s: wrapped line %u", __func__, aty); memcpy(&last, &grid_default_cell, sizeof last); len = 0; width = 0; for (i = 0; i < sx; i++) { - grid_view_get_cell(gd, i, py, &gc); + grid_view_get_cell(gd, px + i, py, &gc); gcp = tty_check_codeset(tty, &gc); if (len != 0 && ((gcp->attr & GRID_ATTR_CHARSET) || @@ -969,7 +1270,7 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, gcp->attr != last.attr || gcp->fg != last.fg || gcp->bg != last.bg || - ux + width + gcp->data.width >= screen_size_x(s) || + ux + width + gcp->data.width >= nx || (sizeof buf) - len < gcp->data.size)) { tty_attributes(tty, &last, wp); tty_putn(tty, buf, len, width); @@ -983,10 +1284,10 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, screen_select_cell(s, &last, gcp); else memcpy(&last, gcp, sizeof last); - if (ux + gcp->data.width > screen_size_x(s)) { + if (ux + gcp->data.width > nx) { tty_attributes(tty, &last, wp); for (j = 0; j < gcp->data.width; j++) { - if (ux + j > screen_size_x(s)) + if (ux + j > nx) break; tty_putc(tty, ' '); ux++; @@ -1019,10 +1320,9 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, } } - if (!cleared && ux < screen_size_x(s)) { - nx = screen_size_x(s) - ux; + if (!cleared && ux < nx) { tty_default_attributes(tty, wp, 8); - tty_clear_line(tty, wp, oy + py, ox + ux, nx, 8); + tty_clear_line(tty, wp, aty, atx + ux, nx - ux, 8); } tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; @@ -1034,12 +1334,14 @@ tty_client_ready(struct client *c, struct window_pane *wp) { if (c->session == NULL || c->tty.term == NULL) return (0); - if (c->flags & (CLIENT_REDRAW|CLIENT_SUSPENDED)) + if (c->flags & (CLIENT_REDRAWWINDOW|CLIENT_SUSPENDED)) return (0); if (c->tty.flags & TTY_FREEZE) return (0); if (c->session->curw->window != wp->window) return (0); + if (wp->layout_cell == NULL) + return (0); return (1); } @@ -1050,21 +1352,23 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), struct window_pane *wp = ctx->wp; struct client *c; - /* wp can be NULL if updating the screen but not the terminal. */ if (wp == NULL) return; - - if ((wp->flags & (PANE_REDRAW|PANE_DROP)) || !window_pane_visible(wp)) + if (wp->flags & (PANE_REDRAW|PANE_DROP)) return; TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; + ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, + &ctx->sx, &ctx->sy); + ctx->xoff = wp->xoff; ctx->yoff = wp->yoff; + if (status_at_line(c) == 0) - ctx->yoff += status_line_size(c->session); + ctx->yoff += status_line_size(c); cmdfn(&c->tty, ctx); } @@ -1075,11 +1379,12 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - if (!tty_pane_full_width(tty, ctx) || + if (ctx->bigger || + !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && !tty_term_has(tty->term, TTYC_ICH1))) { - tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, ctx, ctx->ocy); return; } @@ -1095,11 +1400,12 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - if (!tty_pane_full_width(tty, ctx) || + if (ctx->bigger || + !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { - tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, ctx, ctx->ocy); return; } @@ -1113,6 +1419,11 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { + if (ctx->bigger) { + tty_draw_pane(tty, ctx, ctx->ocy); + return; + } + tty_default_attributes(tty, ctx->wp, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -1127,10 +1438,13 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (ctx->bigger || + !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || - !tty_term_has(tty->term, TTYC_IL1)) { + !tty_term_has(tty->term, TTYC_IL1) || + ctx->wp->sx == 1 || + ctx->wp->sy == 1) { tty_redraw_region(tty, ctx); return; } @@ -1148,10 +1462,13 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (ctx->bigger || + !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || - !tty_term_has(tty->term, TTYC_DL1)) { + !tty_term_has(tty->term, TTYC_DL1) || + ctx->wp->sx == 1 || + ctx->wp->sy == 1) { tty_redraw_region(tty, ctx); return; } @@ -1170,35 +1487,34 @@ void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int nx, py = ctx->yoff + ctx->ocy; + u_int nx; tty_default_attributes(tty, wp, ctx->bg); nx = screen_size_x(wp->screen); - tty_clear_line(tty, wp, py, ctx->xoff, nx, ctx->bg); + tty_clear_pane_line(tty, ctx, ctx->ocy, 0, nx, ctx->bg); } void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int nx, py = ctx->yoff + ctx->ocy; + u_int nx; tty_default_attributes(tty, wp, ctx->bg); nx = screen_size_x(wp->screen) - ctx->ocx; - tty_clear_line(tty, wp, py, ctx->xoff + ctx->ocx, nx, ctx->bg); + tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - u_int py = ctx->yoff + ctx->ocy; tty_default_attributes(tty, wp, ctx->bg); - tty_clear_line(tty, wp, py, ctx->xoff, ctx->ocx + 1, ctx->bg); + tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); } void @@ -1209,10 +1525,13 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orupper) return; - if (!tty_pane_full_width(tty, ctx) || + if (ctx->bigger || + !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || - !tty_term_has(tty->term, TTYC_RI)) { + !tty_term_has(tty->term, TTYC_RI) || + ctx->wp->sx == 1 || + ctx->wp->sy == 1) { tty_redraw_region(tty, ctx); return; } @@ -1234,9 +1553,12 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orlower) return; - if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + if (ctx->bigger || + (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || - !tty_term_has(tty->term, TTYC_CSR)) { + !tty_term_has(tty->term, TTYC_CSR) || + wp->sx == 1 || + wp->sy == 1) { tty_redraw_region(tty, ctx); return; } @@ -1270,9 +1592,12 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; u_int i; - if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || + if (ctx->bigger || + (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || - !tty_term_has(tty->term, TTYC_CSR)) { + !tty_term_has(tty->term, TTYC_CSR) || + wp->sx == 1 || + wp->sy == 1) { tty_redraw_region(tty, ctx); return; } @@ -1306,18 +1631,18 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); tty_margin_off(tty); - px = ctx->xoff; + px = 0; nx = screen_size_x(wp->screen); - py = ctx->yoff + ctx->ocy + 1; + py = ctx->ocy + 1; ny = screen_size_y(wp->screen) - ctx->ocy - 1; - tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg); + tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); - px = ctx->xoff + ctx->ocx; + px = ctx->ocx; nx = screen_size_x(wp->screen) - ctx->ocx; - py = ctx->yoff + ctx->ocy; + py = ctx->ocy; - tty_clear_line(tty, wp, py, px, nx, ctx->bg); + tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); } void @@ -1331,18 +1656,18 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); tty_margin_off(tty); - px = ctx->xoff; + px = 0; nx = screen_size_x(wp->screen); - py = ctx->yoff; + py = 0; ny = ctx->ocy - 1; - tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg); + tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); - px = ctx->xoff; + px = 0; nx = ctx->ocx + 1; - py = ctx->yoff + ctx->ocy; + py = ctx->ocy; - tty_clear_line(tty, wp, py, px, nx, ctx->bg); + tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); } void @@ -1356,12 +1681,12 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); tty_margin_off(tty); - px = ctx->xoff; + px = 0; nx = screen_size_x(wp->screen); - py = ctx->yoff; + py = 0; ny = screen_size_y(wp->screen); - tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg); + tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); } void @@ -1371,6 +1696,11 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; + if (ctx->bigger) { + wp->flags |= PANE_REDRAW; + return; + } + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); @@ -1386,13 +1716,15 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { - if (ctx->xoff + ctx->ocx > tty->sx - 1 && ctx->ocy == ctx->orlower) { - if (tty_pane_full_width(tty, ctx)) - tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); - else - tty_margin_off(tty); - } + if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1)) + return; + + if (ctx->xoff + ctx->ocx - ctx->ox > tty->sx - 1 && + ctx->ocy == ctx->orlower && + tty_pane_full_width(tty, ctx)) + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_cell(tty, ctx->cell, ctx->wp); @@ -1401,6 +1733,28 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { + struct window_pane *wp = ctx->wp; + + if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) + return; + + if (ctx->bigger && + (ctx->xoff + ctx->ocx < ctx->ox || + ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { + if (!ctx->wrapped || + !tty_pane_full_width(tty, ctx) || + (tty->term->flags & TERM_EARLYWRAP) || + ctx->xoff + ctx->ocx != 0 || + ctx->yoff + ctx->ocy != tty->cy + 1 || + tty->cx < tty->sx || + tty->cy == tty->rlower) + tty_draw_pane(tty, ctx, ctx->ocy); + else + wp->flags |= PANE_REDRAW; + return; + } + + tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_attributes(tty, ctx->cell, ctx->wp); @@ -1518,7 +1872,8 @@ static void tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) { - tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower); + tty_region(tty, ctx->yoff + rupper - ctx->oy, + ctx->yoff + rlower - ctx->oy); } /* Set region at absolute position. */ @@ -1557,7 +1912,8 @@ tty_margin_off(struct tty *tty) static void tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) { - tty_margin(tty, ctx->xoff, ctx->xoff + ctx->wp->sx - 1); + tty_margin(tty, ctx->xoff - ctx->ox, + ctx->xoff + ctx->wp->sx - 1 - ctx->ox); } /* Set margin at absolute position. */ @@ -1608,7 +1964,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, static void tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { - tty_cursor(tty, ctx->xoff + cx, ctx->yoff + cy); + tty_cursor(tty, ctx->xoff + cx - ctx->ox, ctx->yoff + cy - ctx->oy); } /* Move cursor to absolute position. */ @@ -1807,8 +2163,19 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_DIM); if (changed & GRID_ATTR_ITALICS) tty_set_italics(tty); - if (changed & GRID_ATTR_UNDERSCORE) - tty_putcode(tty, TTYC_SMUL); + if (changed & GRID_ATTR_ALL_UNDERSCORE) { + if ((changed & GRID_ATTR_UNDERSCORE) || + !tty_term_has(tty->term, TTYC_SMULX)) + tty_putcode(tty, TTYC_SMUL); + else if (changed & GRID_ATTR_UNDERSCORE_2) + tty_putcode1(tty, TTYC_SMULX, 2); + else if (changed & GRID_ATTR_UNDERSCORE_3) + tty_putcode1(tty, TTYC_SMULX, 3); + else if (changed & GRID_ATTR_UNDERSCORE_4) + tty_putcode1(tty, TTYC_SMULX, 4); + else if (changed & GRID_ATTR_UNDERSCORE_5) + tty_putcode1(tty, TTYC_SMULX, 5); + } if (changed & GRID_ATTR_BLINK) tty_putcode(tty, TTYC_BLINK); if (changed & GRID_ATTR_REVERSE) { diff --git a/window-copy.c b/window-copy.c index c2d9b9ad..e35e6070 100644 --- a/window-copy.c +++ b/window-copy.c @@ -353,9 +353,6 @@ window_copy_pageup(struct window_pane *wp, int half_page) oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wp, oy); - if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) - window_copy_other_end(wp); - if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; @@ -370,9 +367,13 @@ window_copy_pageup(struct window_pane *wp, int half_page) n = screen_size_y(s) - 2; } - if (data->oy + n > screen_hsize(data->backing)) + if (data->oy + n > screen_hsize(data->backing)) { data->oy = screen_hsize(data->backing); - else + if (data->cy < n) + data->cy = 0; + else + data->cy -= n; + } else data->oy += n; if (data->screen.sel == NULL || !data->rectflag) { @@ -397,9 +398,6 @@ window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit) oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wp, oy); - if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) - window_copy_other_end(wp); - if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; @@ -414,9 +412,13 @@ window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit) n = screen_size_y(s) - 2; } - if (data->oy < n) + if (data->oy < n) { data->oy = 0; - else + if (data->cy + (n - data->oy) >= screen_size_y(data->backing)) + data->cy = screen_size_y(data->backing) - 1; + else + data->cy += n - data->oy; + } else data->oy -= n; if (data->screen.sel == NULL || !data->rectflag) { @@ -527,7 +529,7 @@ window_copy_command(struct window_pane *wp, struct client *c, struct session *s, return; command = args->argv[0]; - if (m != NULL && m->valid) + if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) window_copy_move_mouse(m); if (args->argc == 1) { @@ -1170,7 +1172,6 @@ window_copy_search(struct window_pane *wp, int direction) window_copy_move_right(s, &fx, &fy); else window_copy_move_left(s, &fx, &fy); - window_copy_clear_selection(wp); wrapflag = options_get_number(wp->window->options, "wrap-search"); cis = window_copy_is_lowercase(data->searchstr); @@ -1271,11 +1272,13 @@ window_copy_goto_line(struct window_pane *wp, const char *linestr) { struct window_copy_mode_data *data = wp->modedata; const char *errstr; - u_int lineno; + int lineno; - lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr); + lineno = strtonum(linestr, -1, INT_MAX, &errstr); if (errstr != NULL) return; + if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) + lineno = screen_hsize(data->backing); data->oy = lineno; window_copy_update_selection(wp, 1); @@ -1296,7 +1299,7 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; - if (py == 0) { + if (py == 0 && s->rupper < s->rlower) { if (data->searchmark == NULL) { size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, screen_hsize(data->backing)); @@ -1685,7 +1688,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *s, expanded = format_single(NULL, arg, NULL, s, NULL, wp); job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); - bufferevent_write(job->event, buf, len); + bufferevent_write(job_get_event(job), buf, len); free(expanded); window_copy_copy_buffer(wp, bufname, buf, len); @@ -1808,6 +1811,7 @@ window_copy_clear_selection(struct window_pane *wp) screen_clear_selection(&data->screen); data->cursordrag = CURSORDRAG_NONE; + data->lineflag = LINE_SEL_NONE; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); @@ -2079,7 +2083,7 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only) } } - if (data->screen.sel != NULL || !data->rectflag) { + if (data->screen.sel == NULL || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); if ((data->cx >= data->lastsx && data->cx != px) || diff --git a/window-tree.c b/window-tree.c index 234f4126..8cdc9d80 100644 --- a/window-tree.c +++ b/window-tree.c @@ -461,7 +461,6 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, } } - static void window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, u_int sx, u_int sy, const struct grid_cell *gc, const char *label) @@ -378,6 +378,8 @@ window_destroy(struct window *w) if (event_initialized(&w->alerts_timer)) evtimer_del(&w->alerts_timer); + if (event_initialized(&w->offset_timer)) + event_del(&w->offset_timer); options_free(w->options); @@ -456,17 +458,9 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return (0); w->last = w->active; w->active = wp; - while (!window_pane_visible(w->active)) { - w->active = TAILQ_PREV(w->active, window_panes, entry); - if (w->active == NULL) - w->active = TAILQ_LAST(&w->panes, window_panes); - if (w->active == wp) { - notify_window("window-pane-changed", w); - return (1); - } - } w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; + tty_update_window_offset(w); notify_window("window-pane-changed", w); return (1); } @@ -507,8 +501,8 @@ window_get_active_at(struct window *w, u_int x, u_int y) struct window_pane *wp; TAILQ_FOREACH(wp, &w->panes, entry) { - if (!window_pane_visible(wp)) - continue; + if (!window_pane_visible(wp)) + continue; if (x < wp->xoff || x > wp->xoff + wp->sx) continue; if (y < wp->yoff || y > wp->yoff + wp->sy) @@ -561,9 +555,6 @@ window_zoom(struct window_pane *wp) if (w->flags & WINDOW_ZOOMED) return (-1); - if (!window_pane_visible(wp)) - return (-1); - if (window_count_panes(w) == 1) return (-1); @@ -600,7 +591,7 @@ window_unzoom(struct window *w) wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; } - layout_fix_panes(w, w->sx, w->sy); + layout_fix_panes(w); notify_window("window-layout-changed", w); return (0); @@ -898,7 +889,6 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, #ifdef HAVE_UTEMPTER char s[32]; #endif - int i; sigset_t set, oldset; if (wp->fd != -1) { @@ -1298,11 +1288,11 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, return; if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (wp2 == wp || wp2->mode != NULL) - continue; - if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF) - continue; - if (window_pane_visible(wp2)) + if (wp2 != wp && + wp2->mode == NULL && + wp2->fd != -1 && + (~wp2->flags & PANE_INPUTOFF) && + window_pane_visible(wp2)) input_key(wp2, key, NULL); } } @@ -1311,16 +1301,9 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, int window_pane_visible(struct window_pane *wp) { - struct window *w = wp->window; - - if (wp->layout_cell == NULL) - return (0); - - if (wp->xoff >= w->sx || wp->yoff >= w->sy) - return (0); - if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) - return (0); - return (1); + if (~wp->window->flags & WINDOW_ZOOMED) + return (1); + return (wp == wp->window->active); } u_int @@ -1377,7 +1360,7 @@ window_pane_find_up(struct window_pane *wp) u_int edge, left, right, end, size; int status, found; - if (wp == NULL || !window_pane_visible(wp)) + if (wp == NULL) return (NULL); status = options_get_number(wp->window->options, "pane-border-status"); @@ -1392,7 +1375,7 @@ window_pane_find_up(struct window_pane *wp) right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &wp->window->panes, entry) { - if (next == wp || !window_pane_visible(next)) + if (next == wp) continue; if (next->yoff + next->sy + 1 != edge) continue; @@ -1424,7 +1407,7 @@ window_pane_find_down(struct window_pane *wp) u_int edge, left, right, end, size; int status, found; - if (wp == NULL || !window_pane_visible(wp)) + if (wp == NULL) return (NULL); status = options_get_number(wp->window->options, "pane-border-status"); @@ -1439,7 +1422,7 @@ window_pane_find_down(struct window_pane *wp) right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &wp->window->panes, entry) { - if (next == wp || !window_pane_visible(next)) + if (next == wp) continue; if (next->yoff != edge) continue; @@ -1471,7 +1454,7 @@ window_pane_find_left(struct window_pane *wp) u_int edge, top, bottom, end, size; int found; - if (wp == NULL || !window_pane_visible(wp)) + if (wp == NULL) return (NULL); list = NULL; @@ -1485,7 +1468,7 @@ window_pane_find_left(struct window_pane *wp) bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &wp->window->panes, entry) { - if (next == wp || !window_pane_visible(next)) + if (next == wp) continue; if (next->xoff + next->sx + 1 != edge) continue; @@ -1517,7 +1500,7 @@ window_pane_find_right(struct window_pane *wp) u_int edge, top, bottom, end, size; int found; - if (wp == NULL || !window_pane_visible(wp)) + if (wp == NULL) return (NULL); list = NULL; @@ -1531,7 +1514,7 @@ window_pane_find_right(struct window_pane *wp) bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &wp->window->panes, entry) { - if (next == wp || !window_pane_visible(next)) + if (next == wp) continue; if (next->xoff != edge) continue; |