From b3cca3938332011ef9b2454ba5a30f15863930ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Bri=C3=A8re?= Date: Fri, 28 Jun 2019 23:38:58 -0400 Subject: Make Readline non-blocking: Use Readline's callback interface When Twinkle is running in CLI mode and is sent a "quit" command to its local socket, it will currently not respond immediately, but rather wait until the next line has been read from its standard input (issue #143). This is due to the blocking nature of readline(), which only returns once a complete line has been read. Switching to Readline's "alternate" callback interface is the first step in addressing this issue. (As a bonus, this also fixes a bug where the line pointer returned by readline() was not freed correctly.) --- src/userintf.cpp | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/userintf.cpp b/src/userintf.cpp index f75a140..772184d 100644 --- a/src/userintf.cpp +++ b/src/userintf.cpp @@ -86,22 +86,29 @@ char * tw_command_generator (const char *text, int state) return ((char *)NULL); } -char *tw_readline(const char *prompt) +// Ugly hack to allow invoking methods on our object from within a C-style +// callback function. This relies on the object being a singleton. +static t_userintf *cb_user_intf; +// Callback method (a.k.a. "line handler") that will be invoked by Readline +// once a complete line has been read. +static void tw_readline_cb(char *line) { - static char *line = NULL; - if (!line) { + // EOF + cout << endl; + // Calling this from the line handler prevents one extra + // prompt from being displayed. (The duplicate call later on + // will not be an issue.) + rl_callback_handler_remove(); + + cb_user_intf->cmd_quit(); + } else { + if (*line) { + add_history(line); + cb_user_intf->exec_command(line); + } free(line); - line = NULL; - } - - line = readline(prompt); - - if (line && *line) { - add_history(line); } - - return line; } ///////////////////////////// @@ -2206,16 +2213,15 @@ void t_userintf::run(void) { read_history(sys_config->get_history_file().c_str()); stifle_history(CLI_MAX_HISTORY_LENGTH); + // Additional stuff for using the Readline callback interface + cb_user_intf = this; + rl_callback_handler_install(CLI_PROMPT, tw_readline_cb); while (!end_interface) { - char *command_line = tw_readline(CLI_PROMPT); - if (!command_line){ - cout << endl; - break; - } - - exec_command(command_line); + rl_callback_read_char(); } + + rl_callback_handler_remove(); // Terminate phone functions write_history(sys_config->get_history_file().c_str()); -- cgit v1.2.3