From 103688949dff43f19496899e1ff231852e11f4f0 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Thu, 25 Sep 2025 20:22:38 +0200 Subject: [PATCH] Handle dtach forgetting our hiding of the cursor. --- src/ircplom/testing.py | 2 +- src/ircplom/tui_base.py | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/ircplom/testing.py b/src/ircplom/testing.py index 57de470..bc87e55 100644 --- a/src/ircplom/testing.py +++ b/src/ircplom/testing.py @@ -28,7 +28,7 @@ class TestTerminal(QueueMixin, TerminalInterface): def flush(self) -> None: pass - def calc_geometry(self) -> None: + def set_size_hide_cursor(self) -> None: self.size = TerminalInterface.__annotations__['size'](0, 0) def wrap(self, line: str) -> list[str]: diff --git a/src/ircplom/tui_base.py b/src/ircplom/tui_base.py index 73115a1..e9aba28 100644 --- a/src/ircplom/tui_base.py +++ b/src/ircplom/tui_base.py @@ -408,8 +408,8 @@ class TerminalInterface(ABC): 'Combine multiple contexts into one and run keypress loop.' @abstractmethod - def calc_geometry(self) -> None: - '(Re-)calculate .size..' + def set_size_hide_cursor(self) -> None: + '(Re-)calculate .size, ensure hiding of terminal cursor.' @abstractmethod def flush(self) -> None: @@ -482,8 +482,8 @@ class BaseTui(QueueMixin): self._term.flush() def _set_screen(self) -> None: - 'Calc screen geometry into windows, then call .redraw_affected.' - self._term.calc_geometry() + 'Hide cursor, calc screen geometry into wins, call .redraw_affected.' + self._term.set_size_hide_cursor() for window in self._windows: window.set_geometry() self._status_line.set_geometry(_YX(self._term.size.y - 2, @@ -642,11 +642,19 @@ class Terminal(QueueMixin, TerminalInterface): @contextmanager def setup(self) -> Generator: print(self._blessed.clear, end='') + # NB: Though it might seem the proper place, not using blessed's + # hidden_cursor() context manager here, instead opting for the + # .set_size_hide_cursor called on BaseTui.__init__ and on SIGWINCH, + # since terminal session detachers like dtach can forget an initial + # cursor hiding, so we do it each time the screen is configured a-new; + # we *do* however ensure a visible cursor in the end, as the + # hidden_cursor() context manager would, implicitly assuming that that + # is the "normal" terminal state the user wanted to return to with (self._blessed.raw(), self._blessed.fullscreen(), - self._blessed.hidden_cursor(), Loop(iterator=self._get_keypresses(), _q_out=self._q_out)): yield self + print(self._blessed.normal_cursor, end='', flush=True) @property def _cursor_yx(self) -> _YX: @@ -657,7 +665,9 @@ class Terminal(QueueMixin, TerminalInterface): print(self._blessed.move_yx(yx.y, yx.x), end='') self._cursor_yx_ = yx - def calc_geometry(self) -> None: + def set_size_hide_cursor(self) -> None: + # NB: see note on .setup why cursor hiding here rather than there + print(self._blessed.hide_cursor, end='', flush=True) self.size = _YX(self._blessed.height, self._blessed.width) def flush(self) -> None: -- 2.30.2