Fix the processing of Meta-key commands in TUI
authorPatrick Palka <patrick@parcs.ath.cx>
Sun, 23 Nov 2014 10:03:39 +0000 (14:03 +0400)
committerJoel Brobecker <brobecker@adacore.com>
Sun, 23 Nov 2014 10:04:09 +0000 (14:04 +0400)
This patch fixes the annoying bug where key sequences such as Alt_F or
Alt_B (go forward or backwards by a word) do not behave promptly in TUI.
You have to press a third key in order for the key sequence to register.

This is mostly ncurses' fault.  Calling wgetch() normally causes ncurses
to read only a single key from stdin.  However if the key read is the
start-sequence key (^[ a.k.a. ESC) then wgetch() reads TWO keys from
stdin, storing the 2nd key into an internal FIFO buffer and returning
the start-sequence key.  The extraneous read of the 2nd key makes us
miss its corresponding stdin event, so the event loop blocks until a
third key is pressed.  This explains why such key sequences do not
behave promptly in TUI.

To fix this issue, we must somehow compensate for the missed stdin event
corresponding to the 2nd byte of a key sequence.  This patch achieves
this by hacking  up the stdin event handler to conditionally execute the
readline callback multiple times in a row.  This is done via a new
global variable, call_stdin_event_handler_again_p, which is set from
tui_getc() when we receive a start-sequence key and notice extra pending
input in the ncurses buffer.

Tested on x86_64-unknown-linux-gnu.

gdb/ChangeLog:

* event-top.h (call_stdin_event_handler_again_p): Declare.
* event-top.c (call_stdin_event_handler_again_p): Define.
(stdin_event_handler): Use it.
* tui/tui-io.c (tui_getc): Prepare to call the stdin event
handler again if there is pending input following a
start sequence.

gdb/ChangeLog
gdb/event-top.c
gdb/event-top.h
gdb/tui/tui-io.c

index e16f28ff60b78ba853631c09fd4d9e42ab7e6903..326a0d67e998b7807db99dc4c7ca071dd89b6b7f 100644 (file)
@@ -1,3 +1,12 @@
+2014-11-23  Patrick Palka  <patrick@parcs.ath.cx>
+
+       * event-top.h (call_stdin_event_handler_again_p): Declare.
+       * event-top.c (call_stdin_event_handler_again_p): Define.
+       (stdin_event_handler): Use it.
+       * tui/tui-io.c (tui_getc): Prepare to call the stdin event
+       handler again if there is pending input following a
+       start sequence.
+
 2014-11-23  Patrick Palka  <patrick@parcs.ath.cx>
 
        Pushed by Joel Brobecker  <brobecker@adacore.com>
index 282c0fe03d86f8b9ade785824b7142a5f8c94683..cb438acfcbebb6bdfb227a2377dc36a511e19e39 100644 (file)
@@ -119,6 +119,11 @@ int exec_done_display_p = 0;
    read commands from.  */
 int input_fd;
 
+/* Used by the stdin event handler to compensate for missed stdin events.
+   Setting this to a non-zero value inside an stdin callback makes the callback
+   run again.  */
+int call_stdin_event_handler_again_p;
+
 /* Signal handling variables.  */
 /* Each of these is a pointer to a function that the event loop will
    invoke if the corresponding signal has received.  The real signal
@@ -420,7 +425,13 @@ stdin_event_handler (int error, gdb_client_data client_data)
       quit_command ((char *) 0, stdin == instream);
     }
   else
-    (*call_readline) (client_data);
+    {
+      do
+       {
+         call_stdin_event_handler_again_p = 0;
+         (*call_readline) (client_data);
+       } while (call_stdin_event_handler_again_p != 0);
+    }
 }
 
 /* Re-enable stdin after the end of an execution command in
index ac0d47bdeb090f997dc11b8f545f323999567df7..919287e6f34e31dc18b89a84c9475144de03636c 100644 (file)
@@ -61,6 +61,7 @@ extern void (*call_readline) (void *);
 extern void (*input_handler) (char *);
 extern int input_fd;
 extern void (*after_char_processing_hook) (void);
+extern int call_stdin_event_handler_again_p;
 
 /* Wrappers for rl_callback_handler_remove and
    rl_callback_handler_install that keep track of whether the callback
index 601d278c2df1371b8f564f699ce4c95e738ef448..aa1d1c70863e0ddcf99207c3fbf479c6a720bebe 100644 (file)
@@ -691,7 +691,33 @@ tui_getc (FILE *fp)
     TUI_CMD_WIN->detail.command_info.curch = 0;
   if (ch == KEY_BACKSPACE)
     return '\b';
-  
+
+  if (async_command_editing_p && key_is_start_sequence (ch))
+    {
+      int ch_pending;
+
+      nodelay (w, TRUE);
+      ch_pending = wgetch (w);
+      nodelay (w, FALSE);
+
+      /* If we have pending input following a start sequence, call the stdin
+        event handler again because ncurses may have already read and stored
+        the input into its internal buffer, meaning that we won't get an stdin
+        event for it.  If we don't compensate for this missed stdin event, key
+        sequences as Alt_F (^[f) will not behave promptly.
+
+        (We only compensates for the missed 2nd byte of a key sequence because
+        2-byte sequences are by far the most commonly used. ncurses may have
+        buffered a larger, 3+-byte key sequence though it remains to be seen
+        whether it is useful to compensate for all the bytes of such
+        sequences.)  */
+      if (ch_pending != ERR)
+       {
+         ungetch (ch_pending);
+         call_stdin_event_handler_again_p = 1;
+       }
+    }
+
   return ch;
 }