mirror of
https://github.com/tmux/tmux.git
synced 2026-08-27 17:11:35 +00:00
server-client: consume window damage during the normal redraw pass
redraw_client_damage() (added previously, unused until now) is called from server_client_check_redraw()'s normal redraw pass, and server_client_any_pane_redraw() now also checks for pending window damage so a client with only damage (no PANE_REDRAW/PANE_REDRAWSCROLLBAR flags) still gets its redraw pass run. server_client_check_redraw() now returns whether the redraw was deferred (waiting for outstanding tty output to drain) rather than performed. A deferred redraw no longer escalates to a full CLIENT_REDRAWWINDOW to avoid losing what was pending - server_client_loop() now only clears PANE_REDRAW, PANE_REDRAWSCROLLBAR and window damage once every client viewing a window actually drew this pass (tracked via a new per-window redraw_deferred flag), otherwise they're left in place and retried in their normal, narrowly-scoped form. server_client_set_session() now redraws only if the client's session or current window actually changed, not on every call (e.g. switch-client -t= from clicking a pane name in the status line resolves here even when nothing besides the active pane changed). A drag callback's mouse_drag_update() now opens a sync region itself (tty_sync_start()) before its first write, so a fast-path write it makes directly and a later correction arriving via redraw_client_damage() end up in the same atomic terminal update instead of two visible frames.
This commit is contained in:
@@ -38,7 +38,7 @@ static void server_client_repeat_timer(int, short, void *);
|
||||
static void server_client_click_timer(int, short, void *);
|
||||
static void server_client_check_exit(struct client *, int);
|
||||
static void server_client_exit_timer(int, short, void *);
|
||||
static void server_client_check_redraw(struct client *);
|
||||
static int server_client_check_redraw(struct client *);
|
||||
static void server_client_check_modes(struct client *);
|
||||
static void server_client_set_title(struct client *);
|
||||
static void server_client_set_path(struct client *);
|
||||
@@ -472,7 +472,24 @@ server_client_set_session(struct client *c, struct session *s)
|
||||
tty_update_client_offset(c);
|
||||
status_timer_start(c);
|
||||
server_client_fire_session_changed(c, old);
|
||||
server_redraw_client(c);
|
||||
|
||||
/*
|
||||
* A full redraw is only needed if the client's session or
|
||||
* current window actually changed - not if this merely
|
||||
* confirmed the client is still looking at the same window
|
||||
* (as happens when switch-client -t targets a pane in the
|
||||
* already-current window, e.g. clicking a pane name in a
|
||||
* second #{P:} status line: the default MouseDown1Status
|
||||
* binding resolves that click to switch-client -t=, which
|
||||
* reaches here regardless of whether anything besides the
|
||||
* active pane changed). Redrawing unconditionally here
|
||||
* forced a full window redraw for what should have been
|
||||
* just an active-pane change, already handled narrowly by
|
||||
* window_set_active_pane() and window_redraw_active_switch()
|
||||
* before this is reached.
|
||||
*/
|
||||
if (old == NULL || old != s || old->curw != s->curw)
|
||||
server_redraw_client(c);
|
||||
}
|
||||
|
||||
server_check_unattached();
|
||||
@@ -1444,8 +1461,19 @@ server_client_key_callback(struct cmdq_item *item, void *data)
|
||||
/*
|
||||
* Mouse drag is in progress, so fire the callback (now that
|
||||
* the mouse event is valid).
|
||||
*
|
||||
* Start a synchronized-output region here rather than
|
||||
* leaving it to whatever redraw eventually follows: a drag
|
||||
* callback may write directly via the pane's fast path
|
||||
* immediately, with any correction only arriving later via
|
||||
* redraw_client_damage(), which opens its own sync region.
|
||||
* Since tty_sync_end() is only called once, at the very end
|
||||
* of this client's pass in server_client_reset_state(),
|
||||
* starting it here merges both into one atomic terminal
|
||||
* update instead of two visible frames.
|
||||
*/
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_DRAGGING) {
|
||||
tty_sync_start(&c->tty);
|
||||
c->tty.mouse_drag_update(c, m);
|
||||
goto out;
|
||||
}
|
||||
@@ -1855,14 +1883,25 @@ server_client_loop(void)
|
||||
server_client_check_exit(c, 0);
|
||||
if (c->session != NULL && c->session->curw != NULL) {
|
||||
server_client_check_modes(c);
|
||||
server_client_check_redraw(c);
|
||||
if (server_client_check_redraw(c))
|
||||
c->session->curw->window->redraw_deferred = 1;
|
||||
server_client_reset_state(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Any windows will have been redrawn as part of clients, so clear
|
||||
* their flags now.
|
||||
* their flags now. Neither window damage nor PANE_REDRAW/
|
||||
* PANE_REDRAWSCROLLBAR are lossy to leave for a later pass, so only
|
||||
* clear them once every client actually viewing the window has
|
||||
* drawn (none deferred this pass, per the loop above), otherwise
|
||||
* leave them for the deferred redraw timer to retry. Checking
|
||||
* EVBUFFER_LENGTH(tty->out) here instead would be wrong: a redraw
|
||||
* that was NOT deferred still just wrote fresh bytes into that same
|
||||
* buffer, which libevent has not flushed to the fd yet, so it would
|
||||
* look identical to a deferred one and nothing would ever clear.
|
||||
* PANE_ACTIVITY is unrelated bookkeeping (activity monitoring, not
|
||||
* redraw) and keeps its original unconditional-clear behaviour.
|
||||
*/
|
||||
RB_FOREACH(w, windows, &windows) {
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
@@ -1870,9 +1909,16 @@ server_client_loop(void)
|
||||
server_client_check_pane_resize(wp);
|
||||
server_client_check_pane_buffer(wp);
|
||||
}
|
||||
wp->flags &= ~(PANE_REDRAW|PANE_REDRAWSCROLLBAR|
|
||||
PANE_ACTIVITY);
|
||||
wp->flags &= ~PANE_ACTIVITY;
|
||||
if (!w->redraw_deferred)
|
||||
wp->flags &= ~(PANE_REDRAW|
|
||||
PANE_REDRAWSCROLLBAR);
|
||||
}
|
||||
|
||||
if (!w->redraw_deferred)
|
||||
redraw_free_damage(w);
|
||||
w->redraw_deferred = 0;
|
||||
|
||||
check_window_name(w);
|
||||
}
|
||||
|
||||
@@ -2419,6 +2465,8 @@ server_client_any_pane_redraw(struct client *c)
|
||||
|
||||
if (c->flags & CLIENT_REDRAWWINDOW)
|
||||
return (1);
|
||||
if (!TAILQ_EMPTY(&w->damage))
|
||||
return (1);
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp->flags & (PANE_REDRAW|PANE_REDRAWSCROLLBAR))
|
||||
return (1);
|
||||
@@ -2426,8 +2474,11 @@ server_client_any_pane_redraw(struct client *c)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Check for client redraws. */
|
||||
static void
|
||||
/*
|
||||
* Check for client redraws. Returns 1 if the redraw was deferred (waiting
|
||||
* for outstanding tty output to drain) rather than actually performed.
|
||||
*/
|
||||
static int
|
||||
server_client_check_redraw(struct client *c)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
@@ -2440,7 +2491,7 @@ server_client_check_redraw(struct client *c)
|
||||
size_t n;
|
||||
|
||||
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
||||
return;
|
||||
return (0);
|
||||
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
|
||||
log_debug("%s: redraw%s%s%s%s%s", c->name,
|
||||
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
|
||||
@@ -2458,13 +2509,19 @@ server_client_check_redraw(struct client *c)
|
||||
needed = 1;
|
||||
if (!needed) {
|
||||
c->flags &= ~CLIENT_STATUSFORCE;
|
||||
return;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is outstanding data, defer the redraw until it has been
|
||||
* consumed. We can just add a timer to get out of the event loop and
|
||||
* end up back here.
|
||||
* end up back here. Unlike before, this does not need to escalate to
|
||||
* a full CLIENT_REDRAWWINDOW redraw to avoid losing whatever is
|
||||
* pending: server_client_loop() only clears PANE_REDRAW,
|
||||
* PANE_REDRAWSCROLLBAR and window damage once a pass actually drew
|
||||
* them (see the redraw_deferred handling there), so they are simply
|
||||
* left in place and retried in their normal, narrowly-scoped form
|
||||
* next time.
|
||||
*/
|
||||
n = EVBUFFER_LENGTH(tty->out);
|
||||
if (n != 0 || (tty->flags & TTY_BLOCK)) {
|
||||
@@ -2478,9 +2535,7 @@ server_client_check_redraw(struct client *c)
|
||||
log_debug("redraw timer started");
|
||||
evtimer_add(&ev, &tv);
|
||||
}
|
||||
if (server_client_any_pane_redraw(c))
|
||||
c->flags |= CLIENT_REDRAWWINDOW;
|
||||
return;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Unfreeze the tty and turn off the cursor. */
|
||||
@@ -2517,6 +2572,7 @@ server_client_check_redraw(struct client *c)
|
||||
}
|
||||
server_client_set_progress_bar(c);
|
||||
redraw_screen(c);
|
||||
redraw_client_damage(c);
|
||||
}
|
||||
|
||||
/* Put the tty back how it was. */
|
||||
@@ -2531,6 +2587,7 @@ server_client_check_redraw(struct client *c)
|
||||
c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE);
|
||||
c->redraw = EVBUFFER_LENGTH(tty->out);
|
||||
log_debug("%s: redraw added %zu bytes", c->name, c->redraw);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Set client title. */
|
||||
|
||||
Reference in New Issue
Block a user