TUI: rewrite tui_query_hook()

Message ID 1420689048-23538-1-git-send-email-patrick@parcs.ath.cx
State New, archived
Headers

Commit Message

Patrick Palka Jan. 8, 2015, 3:50 a.m. UTC
  This patch rewrites tui_query_hook() to print things via tui_puts() and
to read in a line of input via wgetnstr().  The main motivation for this
rewrite is to get the backspace key to work correctly during a quit
prompt so that the user can revise their answer before pressing enter.
The backspace key now works correctly because we now use getstr()
instead of successive calls to getch().

gdb/ChangeLog:

	* tui/tui-hooks.c (tui_query_hook): Rewrite to use tui_puts and
	wgetnstr.
---
 gdb/tui/tui-hooks.c | 31 +++++++++++--------------------
 1 file changed, 11 insertions(+), 20 deletions(-)
  

Comments

Pedro Alves Jan. 8, 2015, 11:03 a.m. UTC | #1
On 01/08/2015 03:50 AM, Patrick Palka wrote:
> This patch rewrites tui_query_hook() to print things via tui_puts() and
> to read in a line of input via wgetnstr().  The main motivation for this
> rewrite is to get the backspace key to work correctly during a quit
> prompt so that the user can revise their answer before pressing enter.
> The backspace key now works correctly because we now use getstr()
> instead of successive calls to getch().

Pagination, done in prompt_for_continue, gets away without this hook, as
it's written in terms of readline.  If we did the same to defaulted_query,
I think the default code would just work for the TUI too.  Did you
consider that?

Thanks,
Pedro Alves
  
Patrick Palka Jan. 8, 2015, 12:40 p.m. UTC | #2
On Thu, Jan 8, 2015 at 6:03 AM, Pedro Alves <palves@redhat.com> wrote:
> On 01/08/2015 03:50 AM, Patrick Palka wrote:
>> This patch rewrites tui_query_hook() to print things via tui_puts() and
>> to read in a line of input via wgetnstr().  The main motivation for this
>> rewrite is to get the backspace key to work correctly during a quit
>> prompt so that the user can revise their answer before pressing enter.
>> The backspace key now works correctly because we now use getstr()
>> instead of successive calls to getch().
>
> Pagination, done in prompt_for_continue, gets away without this hook, as
> it's written in terms of readline.  If we did the same to defaulted_query,
> I think the default code would just work for the TUI too.  Did you
> consider that?

This is complicated by the fact that the default query code inserts
annotations before and after the query.  If we use
gdb_readline_wrapper to print the query and wait for input then the
2nd annotation will not be printed in a timely manner because
gdb_readline_wrapper blocks until a response is given by the user.

>
> Thanks,
> Pedro Alves
>
  
Eli Zaretskii Jan. 8, 2015, 1:31 p.m. UTC | #3
> From: Patrick Palka <patrick@parcs.ath.cx>
> Cc: Patrick Palka <patrick@parcs.ath.cx>
> Date: Wed,  7 Jan 2015 22:50:48 -0500
> 
> @@ -80,30 +80,18 @@ tui_query_hook (const char *msg, va_list argp)
>    echo ();
>    while (1)
>      {
> -      wrap_here ("");		/* Flush any buffered output.  */
> -      gdb_flush (gdb_stdout);
> +      char response[2], answer;
>  
> -      fputs_filtered (question, gdb_stdout);
> -      printf_filtered (_("(y or n) "));
> +      tui_puts (question);
> +      tui_puts (_("(y or n) "));
>  
> -      wrap_here ("");
> -      gdb_flush (gdb_stdout);
> -
> -      answer = tui_getc (stdin);
> -      clearerr (stdin);		/* in case of C-d */
> -      if (answer == EOF)	/* C-d */
> +      if (wgetnstr (win, response, 1) == ERR)

Given the latest discussions about buffering, don't you need to call
wrefresh after the second tui_puts?

Thanks.
  
Patrick Palka Jan. 8, 2015, 1:42 p.m. UTC | #4
On Thu, Jan 8, 2015 at 8:31 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Patrick Palka <patrick@parcs.ath.cx>
>> Cc: Patrick Palka <patrick@parcs.ath.cx>
>> Date: Wed,  7 Jan 2015 22:50:48 -0500
>>
>> @@ -80,30 +80,18 @@ tui_query_hook (const char *msg, va_list argp)
>>    echo ();
>>    while (1)
>>      {
>> -      wrap_here ("");                /* Flush any buffered output.  */
>> -      gdb_flush (gdb_stdout);
>> +      char response[2], answer;
>>
>> -      fputs_filtered (question, gdb_stdout);
>> -      printf_filtered (_("(y or n) "));
>> +      tui_puts (question);
>> +      tui_puts (_("(y or n) "));
>>
>> -      wrap_here ("");
>> -      gdb_flush (gdb_stdout);
>> -
>> -      answer = tui_getc (stdin);
>> -      clearerr (stdin);              /* in case of C-d */
>> -      if (answer == EOF)     /* C-d */
>> +      if (wgetnstr (win, response, 1) == ERR)
>
> Given the latest discussions about buffering, don't you need to call
> wrefresh after the second tui_puts?

I have been using this patch for a few months locally and have not
seen any buffering issues.  The recently-mentioned issues are mostly
Windows-related, aren't they?  I can add a wrefresh() if that is
what's needed to prevent potential buffering issues.

>
> Thanks.
  
Pedro Alves Jan. 8, 2015, 1:53 p.m. UTC | #5
On 01/08/2015 12:40 PM, Patrick Palka wrote:

> If we use
> gdb_readline_wrapper to print the query and wait for input then the
> 2nd annotation will not be printed in a timely manner because
> gdb_readline_wrapper blocks until a response is given by the user.

Can't we just cat the annotations bits into the query string
itself?  IOW, make them part of the secondary prompt.

Thanks,
Pedro Alves
  
Pedro Alves Jan. 8, 2015, 1:57 p.m. UTC | #6
On 01/08/2015 01:42 PM, Patrick Palka wrote:
> On Thu, Jan 8, 2015 at 8:31 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>>> From: Patrick Palka <patrick@parcs.ath.cx>
>>> Cc: Patrick Palka <patrick@parcs.ath.cx>
>>> Date: Wed,  7 Jan 2015 22:50:48 -0500
>>>
>>> @@ -80,30 +80,18 @@ tui_query_hook (const char *msg, va_list argp)
>>>    echo ();
>>>    while (1)
>>>      {
>>> -      wrap_here ("");                /* Flush any buffered output.  */
>>> -      gdb_flush (gdb_stdout);
>>> +      char response[2], answer;
>>>
>>> -      fputs_filtered (question, gdb_stdout);
>>> -      printf_filtered (_("(y or n) "));
>>> +      tui_puts (question);
>>> +      tui_puts (_("(y or n) "));
>>>
>>> -      wrap_here ("");
>>> -      gdb_flush (gdb_stdout);
>>> -
>>> -      answer = tui_getc (stdin);
>>> -      clearerr (stdin);              /* in case of C-d */
>>> -      if (answer == EOF)     /* C-d */
>>> +      if (wgetnstr (win, response, 1) == ERR)
>>
>> Given the latest discussions about buffering, don't you need to call
>> wrefresh after the second tui_puts?
> 
> I have been using this patch for a few months locally and have not
> seen any buffering issues.  

That's because presently tui_puts always flushes.  You'll need it
since the query string doesn't end in a new line.  This is exactly
like  we have a gdb_flush in defaulted_query after printing the
query question.

> The recently-mentioned issues are mostly
> Windows-related, aren't they?  I can add a wrefresh() if that is
> what's needed to prevent potential buffering issues.

Let's leave that either for the buffering patch, or if that patch
goes in before this one, add the flushing call here then.  That patch
(or patches..) adds a function to handle the flushing, rather than
calling wrefresh, and you'd want to call that one.

Thanks,
Pedro Alves
  
Patrick Palka Jan. 8, 2015, 2:10 p.m. UTC | #7
On Thu, Jan 8, 2015 at 8:53 AM, Pedro Alves <palves@redhat.com> wrote:
> On 01/08/2015 12:40 PM, Patrick Palka wrote:
>
>> If we use
>> gdb_readline_wrapper to print the query and wait for input then the
>> 2nd annotation will not be printed in a timely manner because
>> gdb_readline_wrapper blocks until a response is given by the user.
>
> Can't we just cat the annotations bits into the query string
> itself?  IOW, make them part of the secondary prompt.

I'm not sure because the annotations contain newlines, so (with
annotations enabled) the prompt passed to readline would have
newlines.  I do not know if readline supports multi-line prompts.

But IMO a consolidation of the custom TUI query hook and the default
query hook is a quite separate endeavor.  I think it should be left as
future work.

>
> Thanks,
> Pedro Alves
>
  
Patrick Palka Jan. 8, 2015, 2:13 p.m. UTC | #8
On Thu, Jan 8, 2015 at 9:10 AM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> On Thu, Jan 8, 2015 at 8:53 AM, Pedro Alves <palves@redhat.com> wrote:
>> On 01/08/2015 12:40 PM, Patrick Palka wrote:
>>
>>> If we use
>>> gdb_readline_wrapper to print the query and wait for input then the
>>> 2nd annotation will not be printed in a timely manner because
>>> gdb_readline_wrapper blocks until a response is given by the user.
>>
>> Can't we just cat the annotations bits into the query string
>> itself?  IOW, make them part of the secondary prompt.
>
> I'm not sure because the annotations contain newlines, so (with
> annotations enabled) the prompt passed to readline would have
> newlines.  I do not know if readline supports multi-line prompts.
>
> But IMO a consolidation of the custom TUI query hook and the default
> query hook is a quite separate endeavor.  I think it should be left as
> future work.

And in the meantime this patch takes advantage of the existence of a
custom TUI query hook to at least make queries within TUI to behave
nicely.

>
>>
>> Thanks,
>> Pedro Alves
>>
  
Eli Zaretskii Jan. 8, 2015, 2:14 p.m. UTC | #9
> From: Patrick Palka <patrick@parcs.ath.cx>
> Date: Thu, 8 Jan 2015 08:42:38 -0500
> Cc: gdb-patches@sourceware.org
> 
> > Given the latest discussions about buffering, don't you need to call
> > wrefresh after the second tui_puts?
> 
> I have been using this patch for a few months locally and have not
> seen any buffering issues.  The recently-mentioned issues are mostly
> Windows-related, aren't they?  I can add a wrefresh() if that is
> what's needed to prevent potential buffering issues.

I think we shouldn't assume that stdout gets flushed when a program
reads from stdin.
  
Patrick Palka Jan. 8, 2015, 2:25 p.m. UTC | #10
On Thu, Jan 8, 2015 at 9:10 AM, Patrick Palka <patrick@parcs.ath.cx> wrote:
> On Thu, Jan 8, 2015 at 8:53 AM, Pedro Alves <palves@redhat.com> wrote:
>> On 01/08/2015 12:40 PM, Patrick Palka wrote:
>>
>>> If we use
>>> gdb_readline_wrapper to print the query and wait for input then the
>>> 2nd annotation will not be printed in a timely manner because
>>> gdb_readline_wrapper blocks until a response is given by the user.
>>
>> Can't we just cat the annotations bits into the query string
>> itself?  IOW, make them part of the secondary prompt.
>
> I'm not sure because the annotations contain newlines, so (with
> annotations enabled) the prompt passed to readline would have
> newlines.  I do not know if readline supports multi-line prompts.

Actually readline does support multi-line prompts because what you
mentioned is exactly what prompt_for_continue() does.  It concatenates
the annotations to the prompt string and passes the multi-line prompt
to gdb_readline_wrapper().  So it will be easy to consolidate these
two query hooks.  I will try to do so.

>
> But IMO a consolidation of the custom TUI query hook and the default
> query hook is a quite separate endeavor.  I think it should be left as
> future work.
>
>>
>> Thanks,
>> Pedro Alves
>>
  
Patrick Palka Jan. 8, 2015, 2:26 p.m. UTC | #11
On Thu, Jan 8, 2015 at 9:14 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Patrick Palka <patrick@parcs.ath.cx>
>> Date: Thu, 8 Jan 2015 08:42:38 -0500
>> Cc: gdb-patches@sourceware.org
>>
>> > Given the latest discussions about buffering, don't you need to call
>> > wrefresh after the second tui_puts?
>>
>> I have been using this patch for a few months locally and have not
>> seen any buffering issues.  The recently-mentioned issues are mostly
>> Windows-related, aren't they?  I can add a wrefresh() if that is
>> what's needed to prevent potential buffering issues.
>
> I think we shouldn't assume that stdout gets flushed when a program
> reads from stdin.

That sounds like a good rule of thumb.
  

Patch

diff --git a/gdb/tui/tui-hooks.c b/gdb/tui/tui-hooks.c
index 6ba6285..9dee840 100644
--- a/gdb/tui/tui-hooks.c
+++ b/gdb/tui/tui-hooks.c
@@ -68,9 +68,9 @@  tui_query_hook (const char *msg, va_list argp)
 {
   int retval;
   int ans2;
-  int answer;
   char *question;
   struct cleanup *old_chain;
+  WINDOW *win = TUI_CMD_WIN->generic.handle;
 
   /* Format the question outside of the loop, to avoid reusing
      ARGP.  */
@@ -80,30 +80,18 @@  tui_query_hook (const char *msg, va_list argp)
   echo ();
   while (1)
     {
-      wrap_here ("");		/* Flush any buffered output.  */
-      gdb_flush (gdb_stdout);
+      char response[2], answer;
 
-      fputs_filtered (question, gdb_stdout);
-      printf_filtered (_("(y or n) "));
+      tui_puts (question);
+      tui_puts (_("(y or n) "));
 
-      wrap_here ("");
-      gdb_flush (gdb_stdout);
-
-      answer = tui_getc (stdin);
-      clearerr (stdin);		/* in case of C-d */
-      if (answer == EOF)	/* C-d */
+      if (wgetnstr (win, response, 1) == ERR)
 	{
 	  retval = 1;
 	  break;
 	}
-      /* Eat rest of input line, to EOF or newline.  */
-      if (answer != '\n')
-	do
-	  {
-            ans2 = tui_getc (stdin);
-	    clearerr (stdin);
-	  }
-	while (ans2 != EOF && ans2 != '\n' && ans2 != '\r');
+
+      answer = response[0];
 
       if (answer >= 'a')
 	answer -= 040;
@@ -117,10 +105,13 @@  tui_query_hook (const char *msg, va_list argp)
 	  retval = 0;
 	  break;
 	}
-      printf_filtered (_("Please answer y or n.\n"));
+      tui_puts (_("Please answer y or n.\n"));
     }
   noecho ();
 
+  /* Update our knowledge of the cursor position.  */
+  tui_puts ("");
+
   do_cleanups (old_chain);
   return retval;
 }