From patchwork Mon Mar 21 15:20:51 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 11425 Received: (qmail 92088 invoked by alias); 21 Mar 2016 15:21:37 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 91861 invoked by uid 89); 21 Mar 2016 15:21:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=578, 1818, 1776, streams X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Mon, 21 Mar 2016 15:21:19 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (Postfix) with ESMTPS id B8B3D112D42 for ; Mon, 21 Mar 2016 15:21:18 +0000 (UTC) Received: from cascais.lan (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u2LFLGPT019569 for ; Mon, 21 Mar 2016 11:21:17 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH v2 01/25] Introduce "struct ui" Date: Mon, 21 Mar 2016 15:20:51 +0000 Message-Id: <1458573675-15478-2-git-send-email-palves@redhat.com> In-Reply-To: <1458573675-15478-1-git-send-email-palves@redhat.com> References: <1458573675-15478-1-git-send-email-palves@redhat.com> I'm aiming towards supporting multiple consoles/MIs, each on its own stdio streams / terminal. See intro comment in top.h. (I've had trouble picking a name for this object. I've started out with "struct console" originally. But then this is about MI as well, and there's "interpreter-exec console", which is specifically about the CLI... So I changed to "struct terminal", but, then we have a terminal object that works when the input is not a terminal as well ... Then I sort of gave up and renamed it to "struct top_level". But it then gets horribly confusing when we talk about the "top level interpreter that's running on the current top level". In the end, I realized we're already sort of calling this "ui", in struct ui_out, struct ui_file, and a few coments here and there.) --- gdb/event-top.c | 75 ++++++++++++++++++++++-------------------------------- gdb/event-top.h | 2 -- gdb/mi/mi-interp.c | 5 ++-- gdb/top.c | 10 +++++--- gdb/top.h | 33 +++++++++++++++++++++++- 5 files changed, 71 insertions(+), 54 deletions(-) diff --git a/gdb/event-top.c b/gdb/event-top.c index eb4f0b9..a6d86fd 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -74,28 +74,10 @@ static void async_stop_sig (gdb_client_data); #endif static void async_sigterm_handler (gdb_client_data arg); -/* Readline offers an alternate interface, via callback - functions. These are all included in the file callback.c in the - readline distribution. This file provides (mainly) a function, which - the event loop uses as callback (i.e. event handler) whenever an event - is detected on the standard input file descriptor. - readline_callback_read_char is called (by the GDB event loop) whenever - there is a new character ready on the input stream. This function - incrementally builds a buffer internal to readline where it - accumulates the line read up to the point of invocation. In the - special case in which the character read is newline, the function - invokes a GDB supplied callback routine, which does the processing of - a full command line. This latter routine is the asynchronous analog - of the old command_line_input in gdb. Instead of invoking (and waiting - for) readline to read the command line and pass it back to - command_loop for processing, the new command_line_handler function has - the command line already available as its parameter. INPUT_HANDLER is - to be set to the function that readline will invoke when a complete - line of input is ready. CALL_READLINE is to be set to the function - that readline offers as callback to the event_loop. */ - -void (*input_handler) (char *); -void (*call_readline) (gdb_client_data); +/* Instead of invoking (and waiting for) readline to read the command + line and pass it back for processing, we use readline's alternate + interface, via callback functions, so that the event loop can react + to other event sources while we wait for input. */ /* Important variables for the event loop. */ @@ -177,6 +159,8 @@ cli_command_loop (void *data) static void change_line_handler (void) { + struct ui *ui = current_ui; + /* NOTE: this operates on input_fd, not instream. If we are reading commands from a file, instream will point to the file. However in async mode, we always read commands from a file with editing @@ -186,18 +170,18 @@ change_line_handler (void) if (async_command_editing_p) { /* Turn on editing by using readline. */ - call_readline = rl_callback_read_char_wrapper; - input_handler = command_line_handler; + ui->call_readline = rl_callback_read_char_wrapper; + ui->input_handler = command_line_handler; } else { /* Turn off editing by using gdb_readline_no_editing_callback. */ gdb_rl_callback_handler_remove (); - call_readline = gdb_readline_no_editing_callback; + ui->call_readline = gdb_readline_no_editing_callback; /* Set up the command handler as well, in case we are called as first thing from .gdbinit. */ - input_handler = command_line_handler; + ui->input_handler = command_line_handler; } } @@ -230,12 +214,14 @@ gdb_rl_callback_handler_remove (void) void gdb_rl_callback_handler_install (const char *prompt) { + struct ui *ui = current_ui; + /* Calling rl_callback_handler_install resets readline's input buffer. Calling this when we were already processing input therefore loses input. */ gdb_assert (!callback_handler_installed); - rl_callback_handler_install (prompt, input_handler); + rl_callback_handler_install (prompt, ui->input_handler); callback_handler_installed = 1; } @@ -368,22 +354,16 @@ top_level_prompt (void) return xstrdup (prompt); } -/* Get a pointer to the command line buffer. This is used to +static struct ui current_ui_; +struct ui *current_ui = ¤t_ui_; + +/* Get a pointer to the current UI's line buffer. This is used to construct a whole line of input from partial input. */ static struct buffer * get_command_line_buffer (void) { - static struct buffer line_buffer; - static int line_buffer_initialized; - - if (!line_buffer_initialized) - { - buffer_init (&line_buffer); - line_buffer_initialized = 1; - } - - return &line_buffer; + return ¤t_ui->line_buffer; } /* When there is an event ready on the stdin file descriptor, instead @@ -394,6 +374,8 @@ get_command_line_buffer (void) void stdin_event_handler (int error, gdb_client_data client_data) { + struct ui *ui = current_ui; + if (error) { printf_unfiltered (_("error detected on stdin\n")); @@ -406,7 +388,7 @@ stdin_event_handler (int error, gdb_client_data client_data) do { call_stdin_event_handler_again_p = 0; - (*call_readline) (client_data); + ui->call_readline (client_data); } while (call_stdin_event_handler_again_p != 0); } } @@ -664,6 +646,7 @@ gdb_readline_no_editing_callback (gdb_client_data client_data) char *result; struct buffer line_buffer; static int done_once = 0; + struct ui *ui = current_ui; buffer_init (&line_buffer); @@ -703,7 +686,7 @@ gdb_readline_no_editing_callback (gdb_client_data client_data) break; } xfree (buffer_finish (&line_buffer)); - (*input_handler) (0); + ui->input_handler (NULL); return; } @@ -720,7 +703,7 @@ gdb_readline_no_editing_callback (gdb_client_data client_data) buffer_grow_char (&line_buffer, '\0'); result = buffer_finish (&line_buffer); - (*input_handler) (result); + ui->input_handler (result); } @@ -980,6 +963,8 @@ set_async_editing_command (char *args, int from_tty, void gdb_setup_readline (void) { + struct ui *ui = current_ui; + /* This function is a noop for the sync case. The assumption is that the sync setup is ALL done in gdb_init, and we would only mess it up here. The sync stuff should really go away over @@ -1002,19 +987,19 @@ gdb_setup_readline (void) /* When a character is detected on instream by select or poll, readline will be invoked via this callback function. */ - call_readline = rl_callback_read_char_wrapper; + ui->call_readline = rl_callback_read_char_wrapper; } else { async_command_editing_p = 0; - call_readline = gdb_readline_no_editing_callback; + ui->call_readline = gdb_readline_no_editing_callback; } /* When readline has read an end-of-line character, it passes the complete line to gdb for processing; command_line_handler is the function that does this. */ - input_handler = command_line_handler; - + ui->input_handler = command_line_handler; + /* Tell readline to use the same input stream that gdb uses. */ rl_instream = instream; diff --git a/gdb/event-top.h b/gdb/event-top.h index bef2530..04956a5 100644 --- a/gdb/event-top.h +++ b/gdb/event-top.h @@ -57,8 +57,6 @@ extern void async_enable_stdin (void); extern int async_command_editing_p; extern int exec_done_display_p; extern struct prompts the_prompts; -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; diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index d02012c..7ec51d3 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -174,6 +174,7 @@ static int mi_interpreter_resume (void *data) { struct mi_interp *mi = (struct mi_interp *) data; + struct ui *ui = current_ui; /* As per hack note in mi_interpreter_init, swap in the output channels... */ @@ -181,8 +182,8 @@ mi_interpreter_resume (void *data) /* These overwrite some of the initialization done in _intialize_event_loop. */ - call_readline = gdb_readline_no_editing_callback; - input_handler = mi_execute_command_input_handler; + ui->call_readline = gdb_readline_no_editing_callback; + ui->input_handler = mi_execute_command_input_handler; async_command_editing_p = 0; /* FIXME: This is a total hack for now. PB's use of the MI implicitly relies on a bug in the async support which allows diff --git a/gdb/top.c b/gdb/top.c index 89fe832..913284a 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -778,13 +778,14 @@ struct gdb_readline_wrapper_cleanup static void gdb_readline_wrapper_cleanup (void *arg) { + struct ui *ui = current_ui; struct gdb_readline_wrapper_cleanup *cleanup = (struct gdb_readline_wrapper_cleanup *) arg; rl_already_prompted = cleanup->already_prompted_orig; - gdb_assert (input_handler == gdb_readline_wrapper_line); - input_handler = cleanup->handler_orig; + gdb_assert (ui->input_handler == gdb_readline_wrapper_line); + ui->input_handler = cleanup->handler_orig; /* Don't restore our input handler in readline yet. That would make readline prep the terminal (putting it in raw mode), while the @@ -810,13 +811,14 @@ gdb_readline_wrapper_cleanup (void *arg) char * gdb_readline_wrapper (const char *prompt) { + struct ui *ui = current_ui; struct cleanup *back_to; struct gdb_readline_wrapper_cleanup *cleanup; char *retval; cleanup = XNEW (struct gdb_readline_wrapper_cleanup); - cleanup->handler_orig = input_handler; - input_handler = gdb_readline_wrapper_line; + cleanup->handler_orig = ui->input_handler; + ui->input_handler = gdb_readline_wrapper_line; cleanup->already_prompted_orig = rl_already_prompted; diff --git a/gdb/top.h b/gdb/top.h index a498f39..fc4e90a 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -20,7 +20,38 @@ #ifndef TOP_H #define TOP_H -struct buffer; +#include "buffer.h" +#include "event-loop.h" + +/* All about a user interface instance. Each user interface has its + own I/O files/streams, readline state, its own top level + interpreter (for the main UI, this is the interpreter specified + with -i on the command line) and secondary interpreters (for + interpreter-exec ...), etc. There's always one UI associated with + stdin/stdout/stderr, but the user can create secondary UIs, for + example, to create a separate MI channel on its own stdio + streams. */ + +struct ui +{ + /* The UI's command line buffer. This is to used to accumulate + input until we have a whole command line. */ + struct buffer line_buffer; + + /* The callback used by the event loop whenever an event is detected + on the UI's input file descriptor. This function incrementally + builds a buffer where it accumulates the line read up to the + point of invocation. In the special case in which the character + read is newline, the function invokes the INPUT_HANDLER callback + (see below). */ + void (*call_readline) (gdb_client_data); + + /* The function to invoke when a complete line of input is ready for + processing. */ + void (*input_handler) (char *); +}; + +extern struct ui *current_ui; /* From top.c. */ extern char *saved_command_line;