[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is a "silent" ncurses initialization possible?

From: Robin Haberkorn
Subject: Re: Is a "silent" ncurses initialization possible?
Date: Thu, 25 Jun 2015 10:51:52 +0200

On Mon, 8 Jun 2015 21:50:12 -0400
Thomas Dickey <address@hidden> wrote:

> On Mon, Jun 08, 2015 at 07:12:08PM +0200, Robin Haberkorn wrote:
> > Of course it is not a tty-device. But later on, I freopen()
> > `screen_tty` with the tty-device.
> > Why does cbreak() still fail then?
> cbreak tries to update the tty mode, and fails.  It sounds as if
> ncurses is still holding a descriptor to the non-tty device, which is
> not necessarily the one that you get from freopen.  (Something to
> check on, anyway). 

Never mind, I've now implemented the deferred Curses initialization I
was talking about and I'm using cbreak() with signal handlers and it is
working fine...

Well uhm, just that you cannot get it to work properly across all Curses
"platforms". For instance, PDCurses on Windows console (win32 port of
PDCurses) does not properly set up the Windows console mode to deliver
control interruptions on ^C. Even though that's technically possible.
I'm working around it by calling the appropriate Windows API functions

Actually the ncurses win32 console driver does it right. But it's buggy.
I actually need to call noraw() before cbreak(), since only noraw()
will set the correct console mode (ENABLE_PROCESSED_INPUT). But I think
that should be easy to fix.

On the other PDCurses variants (tested XCurses and PDCurses/win32a),
there is currently no way to handle ^C asynchronously.
Perhaps I will bother to ask the PDCurses/win32a author to implement
this. I think when separating the event loop via threads it should be
possible to invoke a Windows console control handler even from a
windowed application.

> > 
> > Btw. I do know it's possible to open /dev/tty with newterm() in the
> > first place. I used to do it in SciTECO as well. This will leave
> > stdin/stdout
> I do that in dialog...

Even though I no longer need to do it in SciTECO to avoid stdio
interaction in its non-interactive mode, I'm doing it again.

It also allows you to keep writing to stdio even in prog mode, allowing
you to redirect the stdout and/or stderr output into files or pipes.
But this only works if stdout/stderr is not attached to a terminal
(since else, it will interfere with curses), so I do isatty(1) to check
that, dup(1) to "save" stdout and freopen("/dev/null", stdout) to
"mute" stdout if it would go to the terminal. The same for stderr of
course. After resetting shell mode, I then dup2() my saved copy of the
file description back to 1 (or 2). This keeps the redirection magic
completely hidden from the rest of the code which can still happily
do stdio, or even write to fd 1 or 2 directly.
But I guess, that's exactly what you are doing in Dialog...

> > 
> > But what's worse: Even then, I don't think it will work.
> > How can the "worker" (non-UI) thread get the UI thread that is
> > blocking on getch() to wake up and perform additional ncurses
> > operations??? AFAIK and according to getch(3NCURSES) there is no
> > way to interrupt getch() via signals and no thread-safe function
> > for inserting messages into the input FIFO that could be abused for
> > turning getch() into an event loop. As long as the UI thread is
> > blocking, it appears there is no way to safely call other curses
> > functions.
> agreed.

As Stian Skjelstad has pointed out, on UNIX (!) you can still select()
or poll() on stdin (or whatever you used in newterm) before calling
getch(). In a worker thread you can than signal the curses-thread if
you need to do UI updates.

I can even think of three similar but more elegant ways if restricting
yourself to UNIX is OK:
 * use a proper event handling library instead of select()/poll() like
   libev and rewrite your threaded code using asynchronous callbacks.
   No locking or nasty inter-thread communication necessary.
 * just use two threads and 1 global curses mutex. Lock it around
   getch() after select() in the event-loop-thread and in the worker
   thread whenever you call a curses function. This ensures that at no
   time, more than one curses function is called at the same time. You
   don't actually have to perform all curses calls in one and the same
   thread. GTK+ (can) handle concurrency this way.
 * Do the above with a thread-aware build of ncurses and save the
   explicit locking.

All of these solutions however may have to cope with this:
getch() should be non-blocking since select()/poll() can wake up
spuriously, otherwise blocking your event loop or mutex until someone
really presses a key. If it is non-blocking however, can it handle
escape sequences correctly, properly queuing up the characters and
returning ERR before it is ready? And will curses than handle the
escape delay correctly? I doubt it.

And sorry for the rant: Why did no one in the last 30 or so years of
curses ever address the problem of UI-worker-thread-separation properly
and made it a defined and documented part of the standard or library?

How does any other curses app solve this issue, e.g. when showing a
progress bar that can be interrupted with a "Cancel" button or
something similar?

reply via email to

[Prev in Thread] Current Thread [Next in Thread]