mirror of
https://github.com/rr-/pq-cli.git
synced 2026-11-12 22:40:36 -07:00
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:
3 files changed
+14
-4
No files matched your search
@@ -2,7 +2,6 @@ import argparse
|
|||||||
import curses
|
import curses
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
|
|
||||||
@@ -54,7 +53,6 @@ class CursesUserInterface(BaseUserInterface):
|
|||||||
super().__init__(roster, player, args, data)
|
super().__init__(roster, player, args, data)
|
||||||
os.environ.setdefault("ESCDELAY", "25")
|
os.environ.setdefault("ESCDELAY", "25")
|
||||||
self._screen = curses.initscr()
|
self._screen = curses.initscr()
|
||||||
self._screen.nodelay(True)
|
|
||||||
self._screen.keypad(True)
|
self._screen.keypad(True)
|
||||||
curses.noecho()
|
curses.noecho()
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
@@ -87,6 +85,7 @@ class CursesUserInterface(BaseUserInterface):
|
|||||||
self._roster_view.on_quit += self._quit
|
self._roster_view.on_quit += self._quit
|
||||||
self._view: BaseView = self._roster_view
|
self._view: BaseView = self._roster_view
|
||||||
self._view.start()
|
self._view.start()
|
||||||
|
self._wait_for_keys()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
try:
|
try:
|
||||||
@@ -98,8 +97,7 @@ class CursesUserInterface(BaseUserInterface):
|
|||||||
while True:
|
while True:
|
||||||
key = self._screen.getch()
|
key = self._screen.getch()
|
||||||
|
|
||||||
if key == curses.ERR:
|
if key == curses.ERR: # the view's idle interval passed
|
||||||
time.sleep(0.1)
|
|
||||||
self._view.idle()
|
self._view.idle()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -250,6 +248,13 @@ class CursesUserInterface(BaseUserInterface):
|
|||||||
self._view.stop()
|
self._view.stop()
|
||||||
self._view = view
|
self._view = view
|
||||||
self._view.start()
|
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(
|
def _create_character(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import curses
|
|||||||
|
|
||||||
|
|
||||||
class BaseView:
|
class BaseView:
|
||||||
|
# How often idle() runs, in milliseconds; None waits for a key.
|
||||||
|
idle_interval: int | None = None
|
||||||
|
|
||||||
def __init__(self, screen: curses.window) -> None:
|
def __init__(self, screen: curses.window) -> None:
|
||||||
self.screen = screen
|
self.screen = screen
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class PageScrollable(Protocol):
|
|||||||
|
|
||||||
|
|
||||||
class GameView(BaseView):
|
class GameView(BaseView):
|
||||||
|
idle_interval = 100
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
screen: curses.window,
|
screen: curses.window,
|
||||||
|
|||||||
Reference in new issue
Block a user