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.
This commit is contained in:
nicm 2021-08-13 06:52:51 +00:00
parent a2b8506917
commit 2bb0b9d6c5
10 changed files with 115 additions and 122 deletions

View File

@ -124,17 +124,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (!Eflag) if (!Eflag)
environ_update(s->options, c->environ, s->environ); 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) if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(c, NULL); 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 { } else {
if (server_client_open(c, &cause) != 0) { if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause); 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) if (!Eflag)
environ_update(s->options, c->environ, s->environ); environ_update(s->options, c->environ, s->environ);
c->session = s; server_client_set_session(c, s);
server_client_set_key_table(c, NULL); 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) if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0); proc_send(c->peer, MSG_READY, -1, NULL, 0);
notify_client("client-attached", c); notify_client("client-attached", c);
c->flags |= CLIENT_ATTACHED; c->flags |= CLIENT_ATTACHED;
} }
recalculate_sizes();
alerts_check_session(s);
server_update_socket();
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@ -329,18 +329,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
proc_send(c->peer, MSG_READY, -1, NULL, 0); proc_send(c->peer, MSG_READY, -1, NULL, 0);
} else if (c->session != NULL) } else if (c->session != NULL)
c->last_session = c->session; c->last_session = c->session;
c->session = s; server_client_set_session(c, s);
if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(c, NULL); 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 * If there are still configuration file errors to display, put the new

View File

@ -134,23 +134,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(args, 'E')) if (!args_has(args, 'E'))
environ_update(s->options, tc->environ, s->environ); environ_update(s->options, tc->environ, s->environ);
if (tc->session != NULL && tc->session != s) server_client_set_session(tc, s);
tc->last_session = tc->session;
tc->session = s;
if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(tc, NULL); 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); return (CMD_RETURN_NORMAL);
} }

View File

@ -1792,8 +1792,12 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
if (sctx->s->mode & MODE_FOCUSON) if (sctx->s->mode & MODE_FOCUSON)
break; break;
screen_write_mode_set(sctx, MODE_FOCUSON); screen_write_mode_set(sctx, MODE_FOCUSON);
if (wp != NULL) if (wp == NULL)
wp->flags |= PANE_FOCUSPUSH; /* force update */ break;
if (wp->flags & PANE_FOCUSED)
bufferevent_write(wp->event, "\033[I", 3);
else
bufferevent_write(wp->event, "\033[O", 3);
break; break;
case 1005: case 1005:
screen_write_mode_set(sctx, MODE_MOUSE_UTF8); screen_write_mode_set(sctx, MODE_MOUSE_UTF8);

View File

@ -33,7 +33,6 @@
#include "tmux.h" #include "tmux.h"
static void server_client_free(int, short, void *); 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_resize(struct window_pane *);
static void server_client_check_pane_buffer(struct window_pane *); static void server_client_check_pane_buffer(struct window_pane *);
static void server_client_check_window_resize(struct window *); static void server_client_check_window_resize(struct window *);
@ -301,9 +300,8 @@ server_client_attached_lost(struct client *c)
s = loop->session; s = loop->session;
if (loop == c || s == NULL || s->curw->window != w) if (loop == c || s == NULL || s->curw->window != w)
continue; continue;
if (found == NULL || if (found == NULL || timercmp(&loop->activity_time,
timercmp(&loop->activity_time, &found->activity_time, &found->activity_time, >))
>))
found = loop; found = loop;
} }
if (found != NULL) 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. */ /* Lost a client. */
void void
server_client_lost(struct client *c) server_client_lost(struct client *c)
@ -1389,7 +1421,6 @@ server_client_loop(void)
struct client *c; struct client *c;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
int focus;
/* Check for window resize. This is done before redrawing. */ /* Check for window resize. This is done before redrawing. */
RB_FOREACH(w, windows, &windows) 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 * 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) { RB_FOREACH(w, windows, &windows) {
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->fd != -1) { if (wp->fd != -1) {
if (focus)
server_client_check_pane_focus(wp);
server_client_check_pane_resize(wp); server_client_check_pane_resize(wp);
server_client_check_pane_buffer(wp); server_client_check_pane_buffer(wp);
} }
@ -1615,54 +1643,6 @@ out:
bufferevent_enable(wp->event, EV_READ); 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 * 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 * 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: case MSG_EXITING:
if (datalen != 0) if (datalen != 0)
fatalx("bad MSG_EXITING size"); fatalx("bad MSG_EXITING size");
c->session = NULL; server_client_set_session(c, NULL);
tty_close(&c->tty); tty_close(&c->tty);
proc_send(c->peer, MSG_EXITED, -1, NULL, 0); proc_send(c->peer, MSG_EXITED, -1, NULL, 0);
break; break;

View File

@ -445,21 +445,9 @@ server_destroy_session(struct session *s)
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (c->session != s) if (c->session != s)
continue; continue;
if (s_new == NULL) { server_client_set_session(c, NULL);
c->session = NULL; if (s_new == NULL)
c->flags |= CLIENT_EXIT; 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(); recalculate_sizes();
} }

View File

@ -489,6 +489,8 @@ session_last(struct session *s)
int int
session_set_current(struct session *s, struct winlink *wl) session_set_current(struct session *s, struct winlink *wl)
{ {
struct winlink *old = s->curw;
if (wl == NULL) if (wl == NULL)
return (-1); return (-1);
if (wl == s->curw) 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_remove(&s->lastw, wl);
winlink_stack_push(&s->lastw, s->curw); winlink_stack_push(&s->lastw, s->curw);
s->curw = wl; 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); winlink_clear_flags(wl);
window_update_activity(wl->window); window_update_activity(wl->window);
tty_update_window_offset(wl->window); tty_update_window_offset(wl->window);

5
tmux.h
View File

@ -1004,7 +1004,7 @@ struct window_pane {
#define PANE_FOCUSED 0x4 #define PANE_FOCUSED 0x4
/* 0x8 unused */ /* 0x8 unused */
/* 0x10 unused */ /* 0x10 unused */
#define PANE_FOCUSPUSH 0x20 /* 0x20 unused */
#define PANE_INPUTOFF 0x40 #define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80 #define PANE_CHANGED 0x80
#define PANE_EXITED 0x100 #define PANE_EXITED 0x100
@ -2506,6 +2506,7 @@ int server_client_handle_key(struct client *, struct key_event *);
struct client *server_client_create(int); struct client *server_client_create(int);
int server_client_open(struct client *, char **); int server_client_open(struct client *, char **);
void server_client_unref(struct client *); void server_client_unref(struct client *);
void server_client_set_session(struct client *, struct session *);
void server_client_lost(struct client *); void server_client_lost(struct client *);
void server_client_suspend(struct client *); void server_client_suspend(struct client *);
void server_client_detach(struct client *, enum msgtype); 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_has_pane(struct window *, struct window_pane *);
int window_set_active_pane(struct window *, struct window_pane *, int window_set_active_pane(struct window *, struct window_pane *,
int); int);
void window_update_focus(struct window *);
void window_pane_update_focus(struct window_pane *);
void window_redraw_active_switch(struct window *, void window_redraw_active_switch(struct window *,
struct window_pane *); struct window_pane *);
struct window_pane *window_add_pane(struct window *, struct window_pane *, struct window_pane *window_add_pane(struct window *, struct window_pane *,

View File

@ -821,11 +821,13 @@ complete_key:
/* Check for focus events. */ /* Check for focus events. */
if (key == KEYC_FOCUS_OUT) { 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); notify_client("client-focus-out", c);
} else if (key == KEYC_FOCUS_IN) { } else if (key == KEYC_FOCUS_IN) {
tty->client->flags |= CLIENT_FOCUSED; c->flags |= CLIENT_FOCUSED;
notify_client("client-focus-in", c); notify_client("client-focus-in", c);
window_update_focus(c->session->curw->window);
} }
/* Fire the key. */ /* Fire the key. */

View File

@ -458,6 +458,52 @@ window_has_pane(struct window *w, struct window_pane *wp)
return (0); 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 int
window_set_active_pane(struct window *w, struct window_pane *wp, int notify) 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->active_point = next_active_point++;
w->active->flags |= PANE_CHANGED; 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); tty_update_window_offset(w);
if (notify) if (notify)