wake the renderer on pty output instead of polling at 33 fps
This commit is contained in:
@@ -264,6 +264,7 @@ class VtermClearScreenCommand(sublime_plugin.TextCommand):
|
|||||||
return
|
return
|
||||||
# the renderer thread owns the screen; it applies this on its next tick
|
# the renderer thread owns the screen; it applies this on its next tick
|
||||||
terminal.pending_clear_screen = True
|
terminal.pending_clear_screen = True
|
||||||
|
terminal.wake()
|
||||||
self.view.run_command("vterm_clear_undo_stack")
|
self.view.run_command("vterm_clear_undo_stack")
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class VtermCoreEventListener(sublime_plugin.EventListener):
|
|||||||
# background — via the renderer tick: a render here would race the
|
# background — via the renderer tick: a render here would race the
|
||||||
# renderer thread, which owns the emulator
|
# renderer thread, which owns the emulator
|
||||||
terminal.pending_render = True
|
terminal.pending_render = True
|
||||||
|
terminal.wake()
|
||||||
view.run_command("vterm_show_cursor", {"scroll": False})
|
view.run_command("vterm_show_cursor", {"scroll": False})
|
||||||
elif view.settings().get("vterm_view") and not view.settings().get("vterm_view.finished"):
|
elif view.settings().get("vterm_view") and not view.settings().get("vterm_view.finished"):
|
||||||
# the plugin host restarted; this tab's terminal is unreachable
|
# the plugin host restarted; this tab's terminal is unreachable
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ FEED_LIMIT = 256 * 1024
|
|||||||
# SUPPRESS_HISTORY_AFTER_CLEAR in emulator.py must outlast this
|
# SUPPRESS_HISTORY_AFTER_CLEAR in emulator.py must outlast this
|
||||||
REPAINT_NUDGE_DELAY = 0.25
|
REPAINT_NUDGE_DELAY = 0.25
|
||||||
|
|
||||||
|
# extra wait once a tick finds nothing pending, on top of RENDER_PERIOD.
|
||||||
|
IDLE_TICK = 0.2
|
||||||
|
|
||||||
|
|
||||||
def view_size(view, default=None):
|
def view_size(view, default=None):
|
||||||
"""Terminal (rows, columns) that fit the view's viewport."""
|
"""Terminal (rows, columns) that fit the view's viewport."""
|
||||||
@@ -116,6 +119,12 @@ class Terminal:
|
|||||||
self.pending_render = False
|
self.pending_render = False
|
||||||
# a UTF-8 sequence split across read chunks must not be dropped
|
# a UTF-8 sequence split across read chunks must not be dropped
|
||||||
self._decoder = codecs.getincrementaldecoder("utf-8")("replace")
|
self._decoder = codecs.getincrementaldecoder("utf-8")("replace")
|
||||||
|
# set by the reader (and the UI thread) to end the renderer's idle park
|
||||||
|
self._wake = threading.Event()
|
||||||
|
|
||||||
|
def wake(self):
|
||||||
|
"""Cut short the renderer's idle wait, for a flag set from another thread"""
|
||||||
|
self._wake.set()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_id(cls, vid):
|
def from_id(cls, vid):
|
||||||
@@ -203,6 +212,7 @@ class Terminal:
|
|||||||
"""Terminate the child and drop this terminal from the registry."""
|
"""Terminate the child and drop this terminal from the registry."""
|
||||||
LOG.debug("[kill] terminating process")
|
LOG.debug("[kill] terminating process")
|
||||||
self.alive = False
|
self.alive = False
|
||||||
|
self._wake.set()
|
||||||
scheme_manager.remove_listener(self.apply_scheme)
|
scheme_manager.remove_listener(self.apply_scheme)
|
||||||
try:
|
try:
|
||||||
self.process.terminate()
|
self.process.terminate()
|
||||||
@@ -254,11 +264,13 @@ class Terminal:
|
|||||||
except EOFError:
|
except EOFError:
|
||||||
break
|
break
|
||||||
chunks.append(self._decoder.decode(raw))
|
chunks.append(self._decoder.decode(raw))
|
||||||
|
self._wake.set()
|
||||||
if done[0] or not self.is_hosted():
|
if done[0] or not self.is_hosted():
|
||||||
LOG.debug("[reader] stops")
|
LOG.debug("[reader] stops")
|
||||||
break
|
break
|
||||||
self.alive = False
|
self.alive = False
|
||||||
done[0] = True
|
done[0] = True
|
||||||
|
self._wake.set()
|
||||||
|
|
||||||
def feed_data():
|
def feed_data():
|
||||||
"""Parse up to FEED_LIMIT of pending output into the screen."""
|
"""Parse up to FEED_LIMIT of pending output into the screen."""
|
||||||
@@ -280,9 +292,10 @@ class Terminal:
|
|||||||
"""Own the emulator: parse, then ask sublime to redraw, once per tick."""
|
"""Own the emulator: parse, then ask sublime to redraw, once per tick."""
|
||||||
while True:
|
while True:
|
||||||
# a hidden tab is not drawn at all: the screen keeps the state
|
# a hidden tab is not drawn at all: the screen keeps the state
|
||||||
# and one render on unhide catches the view up. The tick stays at
|
# and one render on unhide catches the view up.
|
||||||
# full rate either way, or the parser would fall behind the pty
|
|
||||||
visible = self.is_visible()
|
visible = self.is_visible()
|
||||||
|
|
||||||
|
self._wake.clear()
|
||||||
with intermission(period=RENDER_PERIOD):
|
with intermission(period=RENDER_PERIOD):
|
||||||
feed_data()
|
feed_data()
|
||||||
if self.screen.in_sync_frame():
|
if self.screen.in_sync_frame():
|
||||||
@@ -300,6 +313,16 @@ class Terminal:
|
|||||||
LOG.debug("[renderer] stops")
|
LOG.debug("[renderer] stops")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# nothing left to do: park until woken, instead of spinning at
|
||||||
|
# RENDER_PERIOD. A sync frame or a teardown skipped this by
|
||||||
|
# leaving the block above, and keeps its own cadence.
|
||||||
|
# dirty rows only count while visible: nothing clears them for a
|
||||||
|
# hidden tab, and on_activated wakes us with pending_render
|
||||||
|
if not (
|
||||||
|
chunks or self.pending_render or self.pending_clear or self.pending_clear_screen or (visible and self.screen.dirty)
|
||||||
|
):
|
||||||
|
self._wake.wait(IDLE_TICK)
|
||||||
|
|
||||||
while chunks:
|
while chunks:
|
||||||
feed_data()
|
feed_data()
|
||||||
done[0] = True
|
done[0] = True
|
||||||
|
|||||||
Reference in New Issue
Block a user