I have a problem similar to the one in this post
C, junk appearing on screen using curses
However, I am using python. I wrote a wrapper Screen class that wraps the curses module in a more convenient way. I have a getch like this
def getKeyCode(self):
with self._curses_lock:
self._curses_screen.nodelay(False)
c = self._curses_screen.getch()
if c == 27:
with self._curses_lock:
self._curses_screen.nodelay(True)
next_c = self._curses_screen.getch()
with self._curses_lock:
self._curses_screen.nodelay(False)
return c
and a write which boils down to this
with self._curses_lock:
self._curses_screen.addstr(y, x, out_string, attr)
The getKeyCode is called by a separate python thread that waits in the getch for most of the time, and when a key is pressed, it gets added to a Queue. On the other (main) thread, I have an event loop that gets the events from the queue, performs repaints and refresh the screen.
As I know that ncurses holds shared state, I added a bunch of threading.Locks in order to prevent race conditions, yet if I keep pressed the arrow key, occasionally I get garbage. I guess that this is due to getch getting freed inside ncurses while a repainting is going on from the other thread. This messes up the state of the repaint, giving weird results. I obviously can't have my getKeyCode thread to hold the lock, because that would mean to prevent repainting at all until the getch returns, nor I want to have a getch non-blocking, since I would 1) not really solve the problem and 2) have a threading constantly running that would pump up the CPU usage to 100%. How can I solve this problem?
I solved the problem, like this. I set getch nonblocking with
self._curses_screen.nodelay(True)
then I do the wait not inside curses, but with a select on stdin. When the select returns, some stuff is available and I can lock and get exclusive access to the ncurses backend, guaranteed that I will return immediately with what's available and release the lock.
def getKeyCode(self):
select.select([sys.stdin], [], [])
with self._curses_lock:
c = self._curses_screen.getch()
if c == 27:
next_c = self._curses_screen.getch()
return c