From 2bb0b9d6c5edd7c4127c971f5ccebed969f86c1c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Aug 2021 06:52:51 +0000 Subject: [PATCH] Change focus to be driven by events rather than walking all panes at end of event loop, this way the ordering of in and out can be enforced. GitHub issue 2808. --- cmd-attach-session.c | 23 +---------- cmd-new-session.c | 10 +---- cmd-switch-client.c | 16 +------- input.c | 8 +++- server-client.c | 96 ++++++++++++++++++-------------------------- server-fn.c | 16 +------- session.c | 6 +++ tmux.h | 5 ++- tty-keys.c | 6 ++- window.c | 51 +++++++++++++++++++++++ 10 files changed, 115 insertions(+), 122 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 6a7ebba7..c2074f4f 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -124,17 +124,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (!Eflag) environ_update(s->options, c->environ, s->environ); - c->session = s; + server_client_set_session(c, s); if (~cmdq_get_flags(item) & CMDQ_STATE_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); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); @@ -156,25 +148,14 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (!Eflag) environ_update(s->options, c->environ, s->environ); - c->session = s; + server_client_set_session(c, 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); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); notify_client("client-attached", c); c->flags |= CLIENT_ATTACHED; } - recalculate_sizes(); - alerts_check_session(s); - server_update_socket(); return (CMD_RETURN_NORMAL); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 033c707f..f3a5de26 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -329,18 +329,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) proc_send(c->peer, MSG_READY, -1, NULL, 0); } else if (c->session != NULL) c->last_session = c->session; - c->session = s; + server_client_set_session(c, s); if (~cmdq_get_flags(item) & CMDQ_STATE_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); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); } - recalculate_sizes(); - server_update_socket(); /* * If there are still configuration file errors to display, put the new diff --git a/cmd-switch-client.c b/cmd-switch-client.c index b10496e3..bc6baa6a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -134,23 +134,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'E')) environ_update(s->options, tc->environ, s->environ); - if (tc->session != NULL && tc->session != s) - tc->last_session = tc->session; - tc->session = s; + server_client_set_session(tc, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(tc, NULL); - tty_update_client_offset(tc); - status_timer_start(tc); - notify_client("client-session-changed", tc); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - - server_check_unattached(); - server_redraw_client(tc); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = tc; - recalculate_sizes(); - alerts_check_session(s); return (CMD_RETURN_NORMAL); } diff --git a/input.c b/input.c index 49ed68b7..155144b7 100644 --- a/input.c +++ b/input.c @@ -1792,8 +1792,12 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (sctx->s->mode & MODE_FOCUSON) break; screen_write_mode_set(sctx, MODE_FOCUSON); - if (wp != NULL) - wp->flags |= PANE_FOCUSPUSH; /* force update */ + if (wp == NULL) + break; + if (wp->flags & PANE_FOCUSED) + bufferevent_write(wp->event, "\033[I", 3); + else + bufferevent_write(wp->event, "\033[O", 3); break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); diff --git a/server-client.c b/server-client.c index 3b102beb..f4cc7865 100644 --- a/server-client.c +++ b/server-client.c @@ -33,7 +33,6 @@ #include "tmux.h" static void server_client_free(int, short, void *); -static void server_client_check_pane_focus(struct window_pane *); static void server_client_check_pane_resize(struct window_pane *); static void server_client_check_pane_buffer(struct window_pane *); static void server_client_check_window_resize(struct window *); @@ -301,9 +300,8 @@ server_client_attached_lost(struct client *c) s = loop->session; if (loop == c || s == NULL || s->curw->window != w) continue; - if (found == NULL || - timercmp(&loop->activity_time, &found->activity_time, - >)) + if (found == NULL || timercmp(&loop->activity_time, + &found->activity_time, >)) found = loop; } if (found != NULL) @@ -311,6 +309,40 @@ server_client_attached_lost(struct client *c) } } + +/* Set client session. */ +void +server_client_set_session(struct client *c, struct session *s) +{ + struct session *old = c->session; + + if (s != NULL && c->session != NULL && c->session != s) + c->last_session = c->session; + else if (s == NULL) + c->last_session = NULL; + c->session = s; + c->flags |= CLIENT_FOCUSED; + recalculate_sizes(); + + if (old != NULL && old->curw != NULL) + window_update_focus(old->curw->window); + if (s != NULL) { + window_update_focus(s->curw->window); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); + s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; + alerts_check_session(s); + tty_update_client_offset(c); + status_timer_start(c); + notify_client("client-session-changed", c); + server_redraw_client(c); + } + + server_check_unattached(); + server_update_socket(); +} + /* Lost a client. */ void server_client_lost(struct client *c) @@ -1389,7 +1421,6 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - int focus; /* Check for window resize. This is done before redrawing. */ RB_FOREACH(w, windows, &windows) @@ -1407,14 +1438,11 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear - * their flags now. Also check pane focus and resize. + * their flags now. */ - focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { - if (focus) - server_client_check_pane_focus(wp); server_client_check_pane_resize(wp); server_client_check_pane_buffer(wp); } @@ -1615,54 +1643,6 @@ out: bufferevent_enable(wp->event, EV_READ); } -/* Check whether pane should be focused. */ -static void -server_client_check_pane_focus(struct window_pane *wp) -{ - struct client *c; - int push; - - /* Do we need to push the focus state? */ - push = wp->flags & PANE_FOCUSPUSH; - wp->flags &= ~PANE_FOCUSPUSH; - - /* If we're not the active pane in our window, we're not focused. */ - if (wp->window->active != wp) - goto not_focused; - - /* - * If our window is the current window in any focused clients with an - * attached session, we're focused. - */ - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) - continue; - if (c->session->attached == 0) - continue; - - if (c->session->curw->window == wp->window) - goto focused; - } - -not_focused: - if (push || (wp->flags & PANE_FOCUSED)) { - if (wp->base.mode & MODE_FOCUSON) - bufferevent_write(wp->event, "\033[O", 3); - notify_pane("pane-focus-out", wp); - } - wp->flags &= ~PANE_FOCUSED; - return; - -focused: - if (push || !(wp->flags & PANE_FOCUSED)) { - if (wp->base.mode & MODE_FOCUSON) - bufferevent_write(wp->event, "\033[I", 3); - notify_pane("pane-focus-in", wp); - session_update_activity(c->session, NULL); - } - wp->flags |= PANE_FOCUSED; -} - /* * Update cursor position and mode settings. The scroll region and attributes * are cleared when idle (waiting for an event) as this is the most likely time @@ -2078,7 +2058,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); - c->session = NULL; + server_client_set_session(c, NULL); tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, NULL, 0); break; diff --git a/server-fn.c b/server-fn.c index 4358fa5c..9b2f073b 100644 --- a/server-fn.c +++ b/server-fn.c @@ -445,21 +445,9 @@ server_destroy_session(struct session *s) TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; - if (s_new == NULL) { - c->session = NULL; + server_client_set_session(c, NULL); + if (s_new == NULL) c->flags |= CLIENT_EXIT; - } else { - 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); - gettimeofday(&s_new->last_attached_time, NULL); - server_redraw_client(c); - alerts_check_session(s_new); - } } recalculate_sizes(); } diff --git a/session.c b/session.c index cb0093a2..73f44e18 100644 --- a/session.c +++ b/session.c @@ -489,6 +489,8 @@ session_last(struct session *s) int session_set_current(struct session *s, struct winlink *wl) { + struct winlink *old = s->curw; + if (wl == NULL) return (-1); if (wl == s->curw) @@ -497,6 +499,10 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_remove(&s->lastw, wl); winlink_stack_push(&s->lastw, s->curw); s->curw = wl; + if (options_get_number(global_options, "focus-events")) { + window_update_focus(old->window); + window_update_focus(wl->window); + } winlink_clear_flags(wl); window_update_activity(wl->window); tty_update_window_offset(wl->window); diff --git a/tmux.h b/tmux.h index 3ac272f8..55df8005 100644 --- a/tmux.h +++ b/tmux.h @@ -1004,7 +1004,7 @@ struct window_pane { #define PANE_FOCUSED 0x4 /* 0x8 unused */ /* 0x10 unused */ -#define PANE_FOCUSPUSH 0x20 +/* 0x20 unused */ #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 #define PANE_EXITED 0x100 @@ -2506,6 +2506,7 @@ int server_client_handle_key(struct client *, struct key_event *); struct client *server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); +void server_client_set_session(struct client *, struct session *); void server_client_lost(struct client *); void server_client_suspend(struct client *); void server_client_detach(struct client *, enum msgtype); @@ -2826,6 +2827,8 @@ struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *, int); +void window_update_focus(struct window *); +void window_pane_update_focus(struct window_pane *); void window_redraw_active_switch(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, struct window_pane *, diff --git a/tty-keys.c b/tty-keys.c index 02fba0e5..156fceba 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -821,11 +821,13 @@ complete_key: /* Check for focus events. */ if (key == KEYC_FOCUS_OUT) { - tty->client->flags &= ~CLIENT_FOCUSED; + c->flags &= ~CLIENT_FOCUSED; + window_update_focus(c->session->curw->window); notify_client("client-focus-out", c); } else if (key == KEYC_FOCUS_IN) { - tty->client->flags |= CLIENT_FOCUSED; + c->flags |= CLIENT_FOCUSED; notify_client("client-focus-in", c); + window_update_focus(c->session->curw->window); } /* Fire the key. */ diff --git a/window.c b/window.c index f94acfcb..446898cb 100644 --- a/window.c +++ b/window.c @@ -458,6 +458,52 @@ window_has_pane(struct window *w, struct window_pane *wp) return (0); } +void +window_update_focus(struct window *w) +{ + if (w != NULL) { + log_debug("%s: @%u", __func__, w->id); + window_pane_update_focus(w->active); + } +} + +void +window_pane_update_focus(struct window_pane *wp) +{ + struct client *c; + int focused = 0; + + if (wp != NULL) { + if (wp != wp->window->active) + focused = 0; + else { + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && + c->session->attached != 0 && + (c->flags & CLIENT_FOCUSED) && + c->session->curw->window == wp->window) { + focused = 1; + break; + } + } + } + if (!focused && (wp->flags & PANE_FOCUSED)) { + log_debug("%s: %%%u focus out", __func__, wp->id); + if (wp->base.mode & MODE_FOCUSON) + bufferevent_write(wp->event, "\033[O", 3); + notify_pane("pane-focus-out", wp); + wp->flags &= ~PANE_FOCUSED; + } else if (focused && (~wp->flags & PANE_FOCUSED)) { + log_debug("%s: %%%u focus in", __func__, wp->id); + if (wp->base.mode & MODE_FOCUSON) + bufferevent_write(wp->event, "\033[I", 3); + notify_pane("pane-focus-in", wp); + wp->flags |= PANE_FOCUSED; + } else + log_debug("%s: %%%u focus unchanged", __func__, wp->id); + } +} + int window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { @@ -471,6 +517,11 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; + if (options_get_number(global_options, "focus-events")) { + window_pane_update_focus(w->last); + window_pane_update_focus(w->active); + } + tty_update_window_offset(w); if (notify)