diff --git a/Makefile b/Makefile index e6d9e6b3..8947cd53 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ SRCS= alerts.c \ 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 \ 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-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-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,8 +190,36 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } } + /* Get default session size. */ + if (args_has(args, 'x')) { + tmp = args_get(args, 'x'); + if (strcmp(tmp, "-") == 0) { + if (c != NULL) + dsx = c->tty.sx; + } else { + dsx = strtonum(tmp, 1, USHRT_MAX, &errstr); + if (errstr != NULL) { + cmdq_error(item, "width %s", errstr); + goto error; + } + } + } + if (args_has(args, 'y')) { + tmp = args_get(args, 'y'); + if (strcmp(tmp, "-") == 0) { + if (c != NULL) + dsy = c->tty.sy; + } else { + dsy = strtonum(tmp, 1, USHRT_MAX, &errstr); + if (errstr != NULL) { + cmdq_error(item, "height %s", errstr); + goto error; + } + } + } + /* Find new session size. */ - if (!detached) { + if (!detached && !is_control) { sx = c->tty.sx; sy = c->tty.sy; if (!is_control && @@ -198,34 +227,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) options_get_number(global_s_options, "status")) sy--; } else { - sx = 80; - sy = 24; - } - if ((is_control || detached) && args_has(args, 'x')) { - tmp = args_get(args, 'x'); - if (strcmp(tmp, "-") == 0) { - if (c != NULL) - sx = c->tty.sx; - } else { - sx = 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')) { - tmp = args_get(args, 'y'); - if (strcmp(tmp, "-") == 0) { - if (c != NULL) - sy = c->tty.sy; - } else { - sy = strtonum(tmp, 1, USHRT_MAX, &errstr); - if (errstr != NULL) { - cmdq_error(item, "height %s", errstr); - goto error; - } + 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; @@ -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 0f45c2d5..e5ae099f 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -31,8 +33,8 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "C:lSt:", 0, 0 }, - .usage = "[-lS] [-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,11 +45,64 @@ 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, 'l')) { if (c->session != NULL) @@ -57,12 +112,13 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) 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); } @@ -70,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-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 + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "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-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 +#include #include #include @@ -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-split-window.c b/cmd-split-window.c index 7b58f81f..28821413 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -148,7 +148,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-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); diff --git a/cmd.c b/cmd.c index b90d5ea2..9226e6c2 100644 --- a/cmd.c +++ b/cmd.c @@ -81,6 +81,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; @@ -167,6 +168,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/format.c b/format.c index 126bbc14..21365457 100644 --- a/format.c +++ b/format.c @@ -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 { @@ -107,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; @@ -1347,8 +1347,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); @@ -1383,6 +1381,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); @@ -1451,8 +1450,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; @@ -1460,6 +1462,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)); @@ -1509,18 +1520,14 @@ 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 == w->sx); - format_add(ft, "pane_at_bottom", "%d", - wp->yoff + wp->sy == w->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) diff --git a/input-keys.c b/input-keys.c index 34710dac..39401a38 100644 --- a/input-keys.c +++ b/input-keys.c @@ -248,10 +248,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) && diff --git a/key-bindings.c b/key-bindings.c index c717f5ae..fbc54fb8 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -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 bb49b59f..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 }, 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); diff --git a/layout.c b/layout.c index 4ad645e0..89026b91 100644 --- a/layout.c +++ b/layout.c @@ -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); } } @@ -492,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 @@ -551,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. */ @@ -611,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); } @@ -718,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. */ @@ -1038,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); } @@ -1095,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 03ee530f..347c23de 100644 --- a/options-table.c +++ b/options-table.c @@ -60,6 +60,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[] = { @@ -194,6 +197,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, @@ -467,7 +477,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", @@ -589,22 +601,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, @@ -771,6 +767,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, diff --git a/resize.c b/resize.c index 1c9694da..fef8feeb 100644 --- a/resize.c +++ b/resize.c @@ -22,144 +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. - */ +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 +default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy, + int type) +{ + struct client *c; + u_int cx, cy; + const char *value; + + if (type == -1) + type = options_get_number(global_w_options, "window-size"); + if (type == WINDOW_SIZE_MANUAL) + goto manual; + + if (type == WINDOW_SIZE_LARGEST) { + *sx = *sy = 0; + 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; + } + 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; + } + 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; +} void recalculate_sizes(void) { - 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 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) { - lines = status_line_size(s); - s->attached = 0; - ssx = ssy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (c->flags & CLIENT_SUSPENDED) - continue; - if ((c->flags & (CLIENT_CONTROL|CLIENT_SIZECHANGED)) == - CLIENT_CONTROL) - 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 (ssx == UINT_MAX || ssy == UINT_MAX) - continue; - - if (lines != 0 && ssy == 0) - ssy = lines; - - if (s->sx == ssx && s->sy == ssy) - continue; - - log_debug("session $%u size %u,%u (was %u,%u)", s->id, ssx, ssy, - s->sx, s->sy); - - s->sx = ssx; - s->sy = ssy; - status_update_saved(s); } + /* + * Increment attached count and check the status line size for each + * client. + */ + TAILQ_FOREACH(c, &clients, entry) { + if ((s = c->session) == NULL) + continue; + + flags = c->flags; + if (flags & CLIENT_SUSPENDED) + continue; + if ((flags & CLIENT_CONTROL) && (~flags & CLIENT_SIZECHANGED)) + continue; + + if (c->tty.sy <= status_line_size(c)) + c->flags |= CLIENT_STATUSOFF; + else + c->flags &= ~CLIENT_STATUSOFF; + + 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->attached == 0) - 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; + type = options_get_number(w->options, "window-size"); + if (type == WINDOW_SIZE_MANUAL) + continue; + 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 (ssx == UINT_MAX || ssy == UINT_MAX) + if (w->sx == sx && w->sy == sy) + changed = 0; + + if (!changed) { + tty_update_window_offset(w); 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; - } - - if (w->sx == ssx && w->sy == ssy) - 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 95774633..167389b4 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -33,11 +33,15 @@ struct screen_redraw_ctx { u_int sx; u_int sy; + u_int ox; + u_int oy; }; 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 *); @@ -320,25 +324,56 @@ 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; + 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; - tty_draw_line(tty, NULL, &wp->status_screen, 0, wp->xoff + 2, - yoff); + if (xoff + size <= ctx->ox || + xoff >= ctx->ox + ctx->sx || + yoff < ctx->oy || + yoff >= ctx->oy + ctx->sy) + continue; + + 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); } @@ -385,13 +420,17 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) memset(ctx, 0, sizeof *ctx); ctx->c = c; - ctx->lines = tty_status_lines(c); + ctx->lines = status_line_size(c); + if (c->message_string != NULL || c->prompt_string != NULL) + 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"); - ctx->sx = c->tty.sx; - ctx->sy = c->tty.sy - ctx->lines; + tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); + + 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. */ @@ -420,33 +459,26 @@ screen_redraw_screen(struct client *c) 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); + screen_redraw_set_context(c, &ctx); - log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id, - wp->xoff, yoff); - - 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; @@ -455,14 +487,12 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int x, u_int y, struct window_pane *wp; struct window_pane *active = w->active; struct window_pane *marked = marked_pane.wp; - u_int type; + 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) && @@ -476,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]); } @@ -489,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; + u_int i, j; - 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; - } + 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"); @@ -535,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. */ @@ -557,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); } @@ -580,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. */ @@ -601,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; } @@ -653,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 afc390c7..aefe006e 100644 --- a/server-client.c +++ b/server-client.c @@ -194,6 +194,7 @@ server_client_create(int fd) c->session = NULL; c->last_session = NULL; + c->tty.sx = 80; c->tty.sy = 24; @@ -412,7 +413,7 @@ 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; @@ -422,8 +423,8 @@ server_client_check_mouse(struct client *c) 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 != ' ' && @@ -442,7 +443,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)) { @@ -518,20 +519,33 @@ have_event: /* 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; + 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 == x && - wp->yoff <= 1 + y && - wp->yoff + wp->sy >= y) || - (wp->yoff + wp->sy == y && - wp->xoff <= 1 + x && - wp->xoff + wp->sx >= x)) + 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) @@ -540,7 +554,7 @@ have_event: /* Otherwise try inside the pane. */ if (where == NOWHERE) { - wp = window_get_active_at(s->curw->window, x, y); + wp = window_get_active_at(s->curw->window, px, py); if (wp != NULL) where = PANE; } @@ -928,8 +942,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; } @@ -1304,28 +1316,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) { diff --git a/server-fn.c b/server-fn.c index c4429033..ae90f0c4 100644 --- a/server-fn.c +++ b/server-fn.c @@ -409,6 +409,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); diff --git a/session.c b/session.c index 1a109a83..061ff499 100644 --- a/session.c +++ b/session.c @@ -113,8 +113,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; @@ -133,7 +133,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); @@ -144,9 +144,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++; @@ -350,7 +347,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); @@ -362,10 +359,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); @@ -548,6 +546,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); } diff --git a/status.c b/status.c index 10f9f290..211da9fc 100644 --- a/status.c +++ b/status.c @@ -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); @@ -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; @@ -663,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); @@ -819,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); diff --git a/tmux.h b/tmux.h index 7b451ada..f1727d23 100644 --- a/tmux.h +++ b/tmux.h @@ -57,11 +57,17 @@ struct tmuxproc; #define PROTOCOL_VERSION 8 /* Default global configuration file. */ +#ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf" +#endif /* 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 @@ -789,6 +795,7 @@ struct window { struct timeval name_time; struct event alerts_timer; + struct event offset_timer; struct timeval activity_time; @@ -809,9 +816,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; @@ -852,6 +857,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, @@ -910,9 +920,6 @@ struct session { struct event lock_timer; - u_int sx; - u_int sy; - struct winlink *curw; struct winlink_stack lastw; struct winlinks windows; @@ -972,6 +979,9 @@ struct mouse_event { u_int ly; u_int lb; + u_int ox; + u_int oy; + int s; int w; int wp; @@ -1019,6 +1029,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; @@ -1099,11 +1115,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. */ @@ -1341,11 +1365,16 @@ struct client { #define CLIENT_SIZECHANGED 0x400000 #define CLIENT_STATUSOFF 0x800000 #define CLIENT_REDRAWSTATUSALWAYS 0x1000000 -#define CLIENT_ALLREDRAWFLAGS \ - (CLIENT_REDRAWWINDOW| \ - CLIENT_REDRAWSTATUS| \ - CLIENT_REDRAWSTATUSALWAYS| \ +#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; @@ -1383,6 +1412,10 @@ struct client { int references; + void *pan_window; + u_int pan_ox; + u_int pan_oy; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -1443,6 +1476,7 @@ struct options_table_entry { const char *separator; const char *style; + const char *pattern; }; /* Common command usages. */ @@ -1643,7 +1677,10 @@ struct environ *environ_for_session(struct session *, int); /* tty.c */ void tty_create_log(void); -u_int tty_status_lines(struct client *); +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 *); @@ -1668,10 +1705,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 *); @@ -1904,9 +1939,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 *, ...); @@ -1922,6 +1957,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 */ @@ -1989,10 +2027,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); @@ -2180,7 +2218,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 *); @@ -2296,7 +2334,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 *); diff --git a/tty.c b/tty.c index 049ea017..7cc129e5 100644 --- a/tty.c +++ b/tty.c @@ -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 *, @@ -698,19 +699,127 @@ tty_repeat_space(struct tty *tty, u_int n) tty_putn(tty, s, n, n); } -/* How many lines are taken up by the status line on this client? */ -u_int -tty_status_lines(struct client *c) +/* Is this window bigger than the terminal? */ +int +tty_window_bigger(struct tty *tty) { - u_int lines; + struct client *c = tty->client; + struct window *w = c->session->curw->window; - if (c->flags & CLIENT_STATUSOFF) - lines = 0; - else - lines = status_line_size(c->session); - if (c->message_string != NULL || c->prompt_string != NULL) - lines = (lines == 0) ? 1 : lines; - return (lines); + 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 (*sx >= w->sx) + c->pan_ox = 0; + else if (c->pan_ox + *sx > w->sx) + c->pan_ox = w->sx - *sx; + *ox = c->pan_ox; + if (*sy >= w->sy) + c->pan_oy = 0; + else 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); } /* @@ -770,18 +879,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) @@ -816,14 +989,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) @@ -886,11 +1138,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 * @@ -919,17 +1192,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); @@ -942,41 +1225,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) || @@ -984,7 +1274,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); @@ -998,10 +1288,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++; @@ -1034,10 +1324,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; @@ -1055,6 +1344,8 @@ tty_client_ready(struct client *c, struct window_pane *wp) return (0); if (c->session->curw->window != wp->window) return (0); + if (wp->layout_cell == NULL) + return (0); return (1); } @@ -1065,21 +1356,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); } @@ -1090,11 +1383,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; } @@ -1110,11 +1404,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; } @@ -1128,6 +1423,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); @@ -1142,7 +1442,8 @@ 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) || @@ -1165,7 +1466,8 @@ 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) || @@ -1189,35 +1491,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 @@ -1228,7 +1529,8 @@ 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) || @@ -1255,7 +1557,8 @@ 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) || wp->sx == 1 || @@ -1293,7 +1596,8 @@ 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) || wp->sx == 1 || @@ -1331,18 +1635,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 @@ -1356,18 +1660,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 @@ -1381,12 +1685,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 @@ -1396,6 +1700,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); @@ -1411,7 +1720,10 @@ 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 && + 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); @@ -1425,6 +1737,27 @@ 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); @@ -1543,7 +1876,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. */ @@ -1582,7 +1916,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. */ @@ -1633,7 +1968,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. */ diff --git a/window.c b/window.c index 019718f2..6e76b480 100644 --- a/window.c +++ b/window.c @@ -380,6 +380,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); @@ -458,17 +460,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); } @@ -509,8 +503,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) @@ -563,9 +557,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); @@ -602,7 +593,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); @@ -1284,11 +1275,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); } } @@ -1297,16 +1288,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 @@ -1363,7 +1347,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"); @@ -1378,7 +1362,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; @@ -1410,7 +1394,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"); @@ -1425,7 +1409,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; @@ -1457,7 +1441,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; @@ -1471,7 +1455,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; @@ -1503,7 +1487,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; @@ -1517,7 +1501,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;