ui/curses: wait for keys instead of polling

The main loop slept 100 ms whenever no key was pending, so keys took
up to a tick to register and every screen woke ten times a second.
Each view now declares how often it needs idle(): the game view keeps
its 100 ms tick, while menus wait for a key. getch() blocks for that
long, so keys register at once, and over 15 s in the roster menu the
process goes from about 300 wake-ups to 6.
This commit is contained in:
Dash committed 2026-10-17 19:57:28 +02:00
1 parent c9bce751ac
commit 9ceda9b873
3 files changed
+14 -4

No files matched your search

+9 -4
View File
@@ -2,7 +2,6 @@ import argparse
import curses
import os
import signal
import time
from pathlib import Path
from types import FrameType
@@ -54,7 +53,6 @@ class CursesUserInterface(BaseUserInterface):
super().__init__(roster, player, args, data)
os.environ.setdefault("ESCDELAY", "25")
self._screen = curses.initscr()
self._screen.nodelay(True)
self._screen.keypad(True)
curses.noecho()
curses.curs_set(0)
@@ -87,6 +85,7 @@ class CursesUserInterface(BaseUserInterface):
self._roster_view.on_quit += self._quit
self._view: BaseView = self._roster_view
self._view.start()
self._wait_for_keys()
def run(self) -> None:
try:
@@ -98,8 +97,7 @@ class CursesUserInterface(BaseUserInterface):
while True:
key = self._screen.getch()
if key == curses.ERR:
time.sleep(0.1)
if key == curses.ERR: # the view's idle interval passed
self._view.idle()
continue
@@ -250,6 +248,13 @@ class CursesUserInterface(BaseUserInterface):
self._view.stop()
self._view = view
self._view.start()
self._wait_for_keys()
def _wait_for_keys(self) -> None:
# getch() blocks until a key comes or the view's idle interval
# passes, so menus sit still and keys never wait for a tick.
interval = self._view.idle_interval
self._screen.timeout(-1 if interval is None else interval)
def _create_character(
self,