From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id OWQ1Lm639GnsKgkAWB0awg (envelope-from ) for ; Fri, 01 May 2026 10:23:42 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=EcIWXjfl; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id B878A1E0BA; Fri, 01 May 2026 10:23:42 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.3 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,GAPPY_SUBJECT, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 37B171E067 for ; Fri, 01 May 2026 10:23:41 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id C5EF94374231 for ; Fri, 1 May 2026 14:23:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C5EF94374231 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=EcIWXjfl Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 07BFB43B5511 for ; Fri, 1 May 2026 14:23:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 07BFB43B5511 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 07BFB43B5511 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777645391; cv=none; b=KRIcK97Qt90DvCNv6CTnLSpdvT6mM7LcYgXnNtQO31LegoNlNuVHPzBkC53fBV0Jjacb+4AvcSQ8mcnLfW3TUROVlkCVEOKmyrvOfsk49ywXKIXANWzdQMdxasjAUx/OeSreevxAnqdM/rwhxCnh+IOfHQbaI7ymwmRNJNv4kRg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777645391; c=relaxed/simple; bh=I+ri8dUneAslrmtQabf+8rceB7ISEPwuDoHjMz99y50=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=IINc/upOcPS+J8444tcGAmr2wM8pnIuCWinRJ7mwPXg+TrvjqJMdjKGhpjqW8QK4atq0CBxBU9Jvpei4EHT28noSIeLK4WZZAbGSBrl2HChVy6+EsvRbSeX5pafYDGOSb6X/IgMpXQdPNS2/6rUDwjEWHRyqNz4W2cnBp2N27y8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 07BFB43B5511 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777645390; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FHprJEo8K82rEb1JsuKhnGV3G0QUphzZq/VnY6AW/DM=; b=EcIWXjflruxphpP18E4KDe1uLQz2hMuskHBAJ+dzMuFuQYksvDSli/cKkVdHWp8f5fB1bL 3vG4wjfrFBCHtwyfOGTFhi76HIG+sQ+b9BC/7S1DOfzH0Y52g07WI9QFZt6p8yLgl9zrVZ 1+Zbcj/6dWiE7ZuQG2TXmKmvoAqzxIs= Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-685-4kzTG6UCNjG-YjRxcxpDSw-1; Fri, 01 May 2026 10:23:08 -0400 X-MC-Unique: 4kzTG6UCNjG-YjRxcxpDSw-1 X-Mimecast-MFC-AGG-ID: 4kzTG6UCNjG-YjRxcxpDSw_1777645387 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-43fe791a398so1725648f8f.0 for ; Fri, 01 May 2026 07:23:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777645387; x=1778250187; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=FHprJEo8K82rEb1JsuKhnGV3G0QUphzZq/VnY6AW/DM=; b=aB+KT76Gzn8RBf7WBtPGsk7HQfc7pqa7ZGe5IO3oc4pjEM9uys37kT88kdQ4MtlXOW l42eVzNH49u8+/YSpsrJEtfnJUI6sEj20o3s5Ch5dDQ+zoIcEzNXlubtL7TwG8JgIlJI 6yZ5G2ochjc3NIzRhnSBPa+w4rxp4+BHGImYmLRv2DeIoFaooSoLuikFUKGztOWThPZy ypuPgW/oLqNKJ8y2itOm1R7rePEtjmqmFko+Y9pSpVqDoV9GTbEHt2+8Ue383onOvzqD 7u6K1qkrFBHOY6YivGWZbLQSB8TBtdKHQJiUFDgmBJjOmOJf1KLuocG/hUoK/ZQzR2ds DW0g== X-Gm-Message-State: AOJu0Yz3+zi7mG4I8yi1N/7Zs5tGiP/BEvYyZqUuVsdcoM4w3RD/DohH GeCNr3zGmqMNGlLZ4f/1wP2Vae5D2Ju0xzrot8QTRoQpCnwzNK3gHliufalMBCLuvxL+T1xb2pi jCPmQC9n7nymxtIsZVhJ6CBRvi463ttGaGdOixkCWBC5fd0inVSWoajJoSi2vswBjUhfuUclT7h 9ZUU97lPEQv0jMZVva6CkAufkIt5bDtgHbM8fqQuWTtNsG7IU= X-Gm-Gg: AeBDieshOabcEna0bYrjAwbDG0UIfOXB8OVALej4LgYqJlMUmjmpCN0vFJNgMBbbtlB fPypCWciv6gLH3NOXEizwROLKeH4BxZOsWtNkxlSM80qZyJvsijCTkP7K5xK0Q+iV0Bt5Ayh6E9 uTDxvqMqLvnvLK0TCHNAudJO8iVqWW8IdXIFS/2PSWDJokb+gPPbWVBefpdonY0+3fIJLpY8NJz iN8XHjmy9HvgaH1igYu/31dNuVaUBpkkBF3JxP9cVfGSzImyZY2DzQ18Yq2udxAGf3um7eMiW2t 6JhI8sVSl+gL5S8vqahZF8T4SZ529zv0tLKxr3W+SqsheddTek6ptaPfyW/Tpd37/yESds0kkpQ yzlnGsu5wZ7Hb3vAvEit/18MkTQw= X-Received: by 2002:a05:6000:24c5:b0:43f:e452:f391 with SMTP id ffacd0b85a97d-4493cb42f83mr12497272f8f.6.1777645386633; Fri, 01 May 2026 07:23:06 -0700 (PDT) X-Received: by 2002:a05:6000:24c5:b0:43f:e452:f391 with SMTP id ffacd0b85a97d-4493cb42f83mr12497172f8f.6.1777645385757; Fri, 01 May 2026 07:23:05 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-44a98b76fd0sm5511086f8f.35.2026.05.01.07.23.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 May 2026 07:23:05 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 6/6] gdb/tui: fix debuginfod prompt using 'C-x C-a' to enter TUI Date: Fri, 1 May 2026 15:22:56 +0100 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ZJcV9Hiu6BajqsK_zZdCBtsjiknYsz3zEIXmtvlERgQ_1777645387 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org This commit ties closely into the previous commit. The previous commit looks at issues that can arise when using 'tui enable' to enter TUI mode if a debuginfod prompt is triggered. This commit looks at the problems that can arise when a multi-key combination is used to enter TUI mode, e.g. 'C-x C-a'. Bug PR gdb/33794 discusses this issue. There has been a previous attempt to address this issue here: https://inbox.sourceware.org/gdb-patches/20260417075719.852558-5-tdevries@suse.de The approach taken in that patch was to prevent switching to TUI mode if debuginfod is still in ASK mode, this means the switch could potentially trigger a secondary prompt. While the previous commit is relatively simple, the complexity in this case arises from how multi-key combinations are handled by readline. Currently global readline state is used to track the multi-key press situation, and when the multi-key is dispatched back to application (GDB) code, the globals are still live. If GDB then triggers reentry into readline, e.g. by triggering a secondary prompt, the call into readline for this prompt will cause the global state to be released. When the secondary prompt is finished and we return back to readline the global state will be accessed, and undefined behaviour occurs, including crashes. The core idea of my proposed solution to this is to move handling of the multi-key actions out of the readline callback, and into GDB's normal event loop. When the user presses a combination like 'C-x C-a' this will call a templated tui_rl_keybinding function as it currently does, but instead of immediately forwarding to another function to carry out the TUI changes, we instead schedule a callback with the event loop and then return. As far as readline is concerned the multi-key action has now been dealt with, however, no interface changes have yet occurred. As readline has now finished handling this key press, readline returns to the event loop to get the next user input. At the event loop the pending callback is seen and dispatched. This callback triggers the actual UI changes, e.g. entering TUI mode. As we are not inside readline at this point we are free to create secondary prompts if needed. In tui_rl_keybinding we use run_on_main_thread to schedule a callback with the event loop, but there are some additional changes needed: 1. If we changed the tui_active state then we need to call reinitialize_more_filter. Previously tui_rl_switch_mode would call rl_newline which would make readline think that a command had been fully entered, this would trigger a call to GDB's command_line_handler, which calls command_handler, which then calls reinitialize_more_filter. For reasons explained below tui_rl_switch_mode can no longer call rl_newline, so the reinitialize_more_filter is never reached. This means that especially when switching CLI to TUI, when the `cmd` window is smaller than the CLI terminal, GDB might enter TUI mode thinking that the TUI is already full. This leads to incorrect pager prompts appearing. Resolve this by explicitly resetting the pager. 2. After changing the tui_active state (i.e. entering or leaving TUI mode), there will not be a GDB prompt displayed. Under the old scheme, the rl_newline call in tui_rl_switch_mode would trick GDB into thinking an empty command had just been completed, this would then trigger a prompt redisplay. Under the new scheme we need to explicitly call display_gdb_prompt or tui_redisplay_readline to redraw the prompt. However, as we were at a GDB prompt already when the user pressed a key like 'C-x C-a', the current_ui will not think that a prompt is needed, if we plan to call display_gdb_prompt then we'll need to change the prompt_state to PROMPT_NEEDED before calling display_gdb_prompt. When possible we prefer calling tui_redisplay_readline, as this preserves the current readline input line buffer contents, so if the user types something at the prompt and then does 'C-x o' to change window focus, the partially typed text is preserved. Both of these additional actions need to be performed for both the normal exit path, and the exception path in order that the prompt be correctly displayed, so this code is done in a SCOPE_EXIT block. The other set of changes are in tui_rl_switch_mode: 1. The calls to rl_prep_terminal are no longer needed as display_gdb_prompt will take care of calling this for us if appropriate (e.g. we are not in TUI mode). 2. The gdb_exception_forced_quit handling can now just propagate the exception, We are no longer within a readline callback, and so can throw this exception further up the stack. 3. Likewise with gdb_exception, we can re-throw this. As the run_on_main_thread mechanism silently swallows all gdb_exceptions except the gdb_exception_forced_quit sub-class, we do need to print the exception ourselves first though. This is why we had to separate out the gdb_exception_forced_quit handling. 4. The rl_kill_text call is no longer needed as the following rl_newline call is going to be removed. 5. The rl_newline call was a neat trick to force a prompt redisplay, but this only works when we are within a readline callback, it injects a newline so that when we return from this callback readline will see the pending newline character, process the now empty line (thanks to the rl_kill_text call), and the print the prompt. This is replaced by the display_gdb_prompt call that was added to tui_rl_keybinding. 6. The dont_repeat call was needed because the rl_kill_text and rl_newline calls were tricking readline into thinking the user had pressed Enter on an empty line, this was done to force a prompt redisplay. However, pressing Enter on an empty line repeats the previous command unless dont_repeat has been called. Now we don't use the rl_newline trick, the dont_repeat call is not needed. The gdb.tui/debuginfod-query.exp test is updated to include tests that switch using multi-key combinations. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33794 --- gdb/testsuite/gdb.tui/debuginfod-query.exp | 5 +- .../gdb.tui/tui-enable-failure-lib.c | 31 ++++ gdb/testsuite/gdb.tui/tui-enable-failure.c | 22 +++ gdb/testsuite/gdb.tui/tui-enable-failure.exp | 136 ++++++++++++++++++ gdb/tui/tui.c | 120 +++++++++++----- 5 files changed, 278 insertions(+), 36 deletions(-) create mode 100644 gdb/testsuite/gdb.tui/tui-enable-failure-lib.c create mode 100644 gdb/testsuite/gdb.tui/tui-enable-failure.c create mode 100644 gdb/testsuite/gdb.tui/tui-enable-failure.exp diff --git a/gdb/testsuite/gdb.tui/debuginfod-query.exp b/gdb/testsuite/gdb.tui/debuginfod-query.exp index 5baf3e24e1f..b7727090c3e 100644 --- a/gdb/testsuite/gdb.tui/debuginfod-query.exp +++ b/gdb/testsuite/gdb.tui/debuginfod-query.exp @@ -213,10 +213,7 @@ file copy -force $binfile $debugdir/ # Create CACHE and DB directories ready for debuginfod to use. prepare_for_debuginfod cache db -# Can add 'keys' to MODES list, but this doesn't currently -# work due to PR gdb/33794. This will be fixed in the next -# commit, and this comment removed. -set modes {command} +set modes {command keys} with_debuginfod_env $cache { save_vars { env(DEBUGINFOD_URLS) } { diff --git a/gdb/testsuite/gdb.tui/tui-enable-failure-lib.c b/gdb/testsuite/gdb.tui/tui-enable-failure-lib.c new file mode 100644 index 00000000000..c267743fd2e --- /dev/null +++ b/gdb/testsuite/gdb.tui/tui-enable-failure-lib.c @@ -0,0 +1,31 @@ +/* Copyright 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Preload library that overrides ncurses newterm to always return NULL, + causing tui_enable to fail. */ + +#include + +/* Define this type so we can override newterm. We only plan to + return NULL, so the details of this type are not important. */ +typedef struct screen_dummy SCREEN; + +SCREEN * +newterm (const char *type, void *outfd, void *infd) +{ + return NULL; +} diff --git a/gdb/testsuite/gdb.tui/tui-enable-failure.c b/gdb/testsuite/gdb.tui/tui-enable-failure.c new file mode 100644 index 00000000000..1dc4b7ca539 --- /dev/null +++ b/gdb/testsuite/gdb.tui/tui-enable-failure.c @@ -0,0 +1,22 @@ +/* Copyright 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.tui/tui-enable-failure.exp b/gdb/testsuite/gdb.tui/tui-enable-failure.exp new file mode 100644 index 00000000000..5ad2eca3d03 --- /dev/null +++ b/gdb/testsuite/gdb.tui/tui-enable-failure.exp @@ -0,0 +1,136 @@ +# Copyright 2026 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test that GDB handles tui_enable failure gracefully. +# +# Use an LD_PRELOAD library to override ncurses' newterm function, +# making it always return NULL. This causes tui_enable to fail with +# an error. +# +# Test both the "tui enable" command and the multi-key combination +# (C-x C-a) paths. The multi-key path is particularly important as it +# exercises the tui_rl_keybinding SCOPE_EXIT error handling. + +require allow_tui_tests +require {!is_remote host} +require {istarget *-linux*} + +tuiterm_env + +standard_testfile .c -lib.c + +set libfile ${testfile}-lib +set libobj [standard_output_file ${libfile}.so] + +# Compile the preload library. +if { [build_executable "build preload lib" $libobj $srcfile2 \ + {debug shlib}] == -1 } { + return +} + +# Compile the test executable. +if { [build_executable "build executable" $testfile $srcfile] == -1 } { + return +} + +# Send 'print NUM' to GDB and check the output appear on LINE. +proc check_responsiveness { num line } { + gdb_assert { [Term::command "print $num"] } \ + "send print command" + + gdb_assert { [Term::check_region_contents_p 0 $line 80 1 " = $num"] } \ + "check print output is in expected location" +} + +# Try to enable TUI mode using HOW, which is either 'command' or +# 'keys'. The preload library ensures that newterm returns NULL, so +# tui_enable will fail. Check that an error is produced and that GDB +# remains responsive. +# +# The first failure sets tui_finish_init to TRIBOOL_UNKNOWN, so a +# second attempt produces a different (generic) error message. Try +# the second attempt too, and check that GDB is still responsive. +# +# The 'keys' path (C-x C-a) is particularly important as it exercises +# the tui_rl_keybinding SCOPE_EXIT error handling. +proc_with_prefix run_test { how } { + Term::clean_restart 24 80 $::testfile + + if {![Term::prepare_for_tui]} { + return + } + + Term::gen_prompt + + # First attempt to enable TUI. + if { $how eq "command" } { + send_gdb "tui enable\n" + set line_offset 1 + } elseif { $how eq "keys" } { + send_gdb "\030\001" + set line_offset 0 + } else { + perror "unknown test mode: $how" + } + + gdb_assert { [Term::wait_for "Cannot enable the TUI: error opening terminal"] } \ + "error from first tui enable attempt" + + # Check that GDB is still responsive. + with_test_prefix "responsive after first failure" { + check_responsiveness 1 [expr {2 + $line_offset}] + } + + # Second attempt using 'tui enable' command should also fail. + send_gdb "tui enable\n" + gdb_assert { [Term::wait_for "Cannot enable the TUI"] } \ + "error from second tui enable attempt" + + # Check that GDB is still responsive. + with_test_prefix "responsive after second failure" { + check_responsiveness 2 [expr {6 + $line_offset}] + } + + # Third attempt using multi-key combo should also fail. + send_gdb "\030\001" + gdb_assert { [Term::wait_for "Cannot enable the TUI"] } \ + "error from third tui enable attempt" + + # Check that GDB is still responsive. + with_test_prefix "responsive after third failure" { + check_responsiveness 3 [expr {9 + $line_offset}] + } + + verbose -log "\n\n\nAPB: Dump:\n\n" + Term::dump_screen +} + +set modes {command keys} + +save_vars { env(LD_PRELOAD) env(ASAN_OPTIONS) } { + if { ![info exists env(LD_PRELOAD)] + || $env(LD_PRELOAD) == "" } { + set env(LD_PRELOAD) "$libobj" + } else { + append env(LD_PRELOAD) ":$libobj" + } + + # Prevent address sanitizer error about library ordering. + append_environment_default ASAN_OPTIONS verify_asan_link_order 0 + + foreach_with_prefix how $modes { + run_test $how + } +} diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c index 5da5a58a6fb..bf83e9c3fdf 100644 --- a/gdb/tui/tui.c +++ b/gdb/tui/tui.c @@ -42,6 +42,7 @@ #include "top.h" #include "ui.h" #include "observable.h" +#include "run-on-main-thread.h" #include @@ -119,15 +120,20 @@ tui_rl_switch_mode (int notused1 = 0, int notused2 = 0) { gdb_assert (!gdb_in_secondary_prompt_p (current_ui)); - /* Don't let exceptions escape. We're in the middle of a readline - callback that isn't prepared for that. */ + /* This function is called through the run_on_main_thread event loop + callback mechanism. That mechanism propagates + gdb_exception_forced_quit exceptions, but silently discards + gdb_exception exceptions. We catch and print gdb_exception + exceptions before propagating them, the run_on_main_thread + mechanism will then discard these and return to the event loop. + Any RAII cleanup between here and there will have been done, + which is important. For gdb_exception_forced_quit exceptions we + just propagate these up the stack without printing, these will be + handled when they are caught by the event loop. */ try { if (tui_active) - { - tui_disable (); - rl_prep_terminal (0); - } + tui_disable (); else { /* If we type "foo", entering it into the readline buffer @@ -143,43 +149,24 @@ tui_rl_switch_mode (int notused1 = 0, int notused2 = 0) TUI. */ rl_clear_visible_line (); - /* If tui_enable throws, we'll re-prep below. */ + /* Disable readline state ahead of enabling TUI mode. If + tui_enable fails then the next display_gdb_prompt will + re-prep the terminal for us. */ rl_deprep_terminal (); + tui_enable (); } } catch (const gdb_exception_forced_quit &ex) { - /* Ideally, we'd do a 'throw' here, but as noted above, we can't - do that, so, instead, we'll set the necessary flags so that - a later QUIT check will restart the forced quit. */ - set_force_quit_flag (); + throw; } catch (const gdb_exception &ex) { exception_print (gdb_stderr, ex); - - if (!tui_active) - rl_prep_terminal (0); + throw; } - /* Clear the readline in case switching occurred in middle of - something. */ - if (rl_end) - rl_kill_text (0, rl_end); - - /* Since we left the curses mode, the terminal mode is restored to - some previous state. That state may not be suitable for readline - to work correctly (it may be restored in line mode). We force an - exit of the current readline so that readline is re-entered and - it will be able to setup the terminal for its needs. By - re-entering in readline, we also redisplay its prompt in the - non-curses mode. */ - rl_newline (1, '\n'); - - /* Make sure the \n we are returning does not repeat the last - command. */ - dont_repeat (); return 0; } @@ -334,7 +321,76 @@ tui_rl_keybinding (int count, int key) if (gdb_in_secondary_prompt_p (current_ui)) return 0; - return FPTR (count, key); + run_on_main_thread ([=] () { + bool was_active = tui_active; + + /* Cleanup required even on the exception path. */ + SCOPE_EXIT { + /* If we switched from CLI to TUI (or back) then we should + reinitialize the pager in order to avoid spurious pagination + prompts. This is especially important going from CLI to TUI + where the command window is usually smaller than the full + terminal, so the pager might already think that we have more + lines printed than will fit in the window, despite the window + starting empty after a mode switch. */ + if (was_active != tui_active) + reinitialize_more_filter (); + + /* The user was at a prompt and pressed a multi-key combination + (e.g. C-x C-a). As a result this callback was invoked from + the event loop. We're now exiting this callback and want to + ensure that the prompt is drawn correctly. + + We have two approaches, full display_gdb_prompt, or a light + weight tui_redisplay_readline. The former will clear the + readline input buffer and redisplay the prompt, while the + second will redraw the prompt along with anything in the + input buffer. We want the light weight option where + possible, but there are times when this isn't an option: + + 1. A successful switch between CLI and TUI, in either + direction, changes how we update the prompt. We need to + call display_gdb_prompt the first time to ensure + everything is done correctly. We also need to consider + the case where tui_enable fails, leaving us in CLI mode, + this also requires a call to display_gdb_prompt as the + light weight tui_redisplay_readline is not appropriate for + CLI use. + + 2. Usually the TUI will have the RL_STATE_CALLBACK state flag + set because of when this event callback is called. If a + secondary prompt has been displayed, then once the + secondary prompt completed, the RL_STATE_CALLBACK flag + will have been cleared. This is good for us, because + after a secondary prompt rl_prompt will still hold the + secondary prompt string, so we need display_gdb_prompt to + set the correct top-level prompt. */ + if (!was_active || !tui_active || !RL_ISSTATE (RL_STATE_CALLBACK)) + { + current_ui->prompt_state = PROMPT_NEEDED; + display_gdb_prompt (nullptr); + } + else + { + /* If we switched TUI to CLI then we took the above block. + If we're in CLI mode then these callbacks will switch us + back to TUI mode. Either way, if we get here then the + TUI must be active. */ + gdb_assert (tui_active); + + /* The only time rl_prompt will be NULL is when we switch + from CLI to TUI, but that will be handled by the block + above. */ + gdb_assert (rl_prompt != nullptr); + + tui_redisplay_readline (); + } + }; + + (void) FPTR (count, key); + }); + + return 0; } /* Initialize readline and configure the keymap for the switching -- 2.25.4