From e8bbc987e08376976d9e6f66a647461744c4bd4d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 18 Sep 2024 11:43:15 -0700 Subject: [PATCH] termio: stop the termios poller when not focused --- src/Surface.zig | 9 ++------ src/termio/Exec.zig | 47 +++++++++++++++++++++++++++++++++++------- src/termio/Termio.zig | 14 +++++++++++-- src/termio/backend.zig | 10 +++++++++ 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 982d74118..baa8dc6b0 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1826,17 +1826,12 @@ pub fn focusCallback(self: *Surface, focused: bool) !void { // Schedule render which also drains our mailbox try self.queueRender(); - // Update the focus state and notify the terminal about the focus event if - // it is requesting it + // Update the focus state and notify the terminal { self.renderer_state.mutex.lock(); self.io.terminal.flags.focused = focused; - const focus_event = self.io.terminal.modes.get(.focus_event); self.renderer_state.mutex.unlock(); - - if (focus_event) { - self.io.queueMessage(.{ .focused = focused }, .unlocked); - } + self.io.queueMessage(.{ .focused = focused }, .unlocked); } } diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 175dc98bf..6efee0bde 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -196,6 +196,32 @@ pub fn threadExit(self: *Exec, td: *termio.Termio.ThreadData) void { exec.read_thread.join(); } +pub fn focusGained( + self: *Exec, + td: *termio.Termio.ThreadData, + focused: bool, +) !void { + _ = self; + + assert(td.backend == .exec); + const execdata = &td.backend.exec; + + if (!focused) { + // Flag the timer to end on the next iteration. This is + // a lot cheaper than doing full timer cancellation. + execdata.termios_timer_running = false; + } else { + // If we're focused, we want to start our termios timer. We + // only do this if it isn't already running. We use the termios + // callback because that'll trigger an immediate state check AND + // start the timer. + if (execdata.termios_timer_c.state() != .active) { + execdata.termios_timer_running = true; + _ = termiosTimer(td, undefined, undefined, {}); + } + } +} + pub fn resize( self: *Exec, grid_size: renderer.GridSize, @@ -391,6 +417,8 @@ fn termiosTimer( _: *xev.Completion, r: xev.Timer.RunError!void, ) xev.CallbackAction { + // log.debug("termios timer fired", .{}); + // This should never happen because we guard starting our // timer on windows but we want this assertion to fire if // we ever do start the timer on windows. @@ -448,14 +476,16 @@ fn termiosTimer( } // Repeat the timer - exec.termios_timer.run( - td.loop, - &exec.termios_timer_c, - TERMIOS_POLL_MS, - termio.Termio.ThreadData, - td, - termiosTimer, - ); + if (exec.termios_timer_running) { + exec.termios_timer.run( + td.loop, + &exec.termios_timer_c, + TERMIOS_POLL_MS, + termio.Termio.ThreadData, + td, + termiosTimer, + ); + } return .disarm; } @@ -604,6 +634,7 @@ pub const ThreadData = struct { /// The timer to detect termios state changes. termios_timer: xev.Timer, termios_timer_c: xev.Completion = .{}, + termios_timer_running: bool = true, /// The last known termios mode. Used for change detection /// to prevent unnecessary locking of expensive mutexes. diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index f209748df..865ca8d90 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -523,8 +523,18 @@ pub fn childExitedAbnormally(self: *Termio, exit_code: u32, runtime_ms: u64) !vo /// Called when focus is gained or lost (when focus events are enabled) pub fn focusGained(self: *Termio, td: *ThreadData, focused: bool) !void { - const seq = if (focused) "\x1b[I" else "\x1b[O"; - try self.queueWrite(td, seq, false); + self.renderer_state.mutex.lock(); + const focus_event = self.renderer_state.terminal.modes.get(.focus_event); + self.renderer_state.mutex.unlock(); + + // If we have focus events enabled, we send the focus event. + if (focus_event) { + const seq = if (focused) "\x1b[I" else "\x1b[O"; + try self.queueWrite(td, seq, false); + } + + // We always notify our backend of focus changes. + try self.backend.focusGained(td, focused); } /// Process output from the pty. This is the manual API that users can diff --git a/src/termio/backend.zig b/src/termio/backend.zig index b29df89c6..0080e7628 100644 --- a/src/termio/backend.zig +++ b/src/termio/backend.zig @@ -62,6 +62,16 @@ pub const Backend = union(Kind) { } } + pub fn focusGained( + self: *Backend, + td: *termio.Termio.ThreadData, + focused: bool, + ) !void { + switch (self.*) { + .exec => |*exec| try exec.focusGained(td, focused), + } + } + pub fn resize( self: *Backend, grid_size: renderer.GridSize,