forked from Imagelibrary/binutils-gdb
Recent work in the TUI has improved GDB's use of the curses wnoutrefresh and doupdate mechanism, which improves performance by batching together updates and then doing a single set of writes to the screen when doupdate is finally called. The tui_batch_rendering type is a RAII class which, in its destructor, calls doupdate to send the batched updates to the screen. However, if there is no tui_batch_rendering active on the call stack then any wnoutrefresh calls will remain batched but undisplayed until the next time doupdate happens to be called. This problem can be seen in PR gdb/32623. When an inferior is started the 'Starting program' message is not immediately displayed to the user. The 'Starting program' message originates from run_command_1 in infcmd.c, the message is sent to the current_uiout, which will be the TUI ui_out. After the message is sent, ui_out::flush() is called, here's the backtrace when that happens: #0 tui_file::flush (this=0x36e4ab0) at ../../src/gdb/tui/tui-file.c:42 #1 0x0000000001004f4b in pager_file::flush (this=0x36d35f0) at ../../src/gdb/utils.c:1531 #2 0x0000000001004f71 in gdb_flush (stream=0x36d35f0) at ../../src/gdb/utils.c:1539 #3 0x00000000006975ab in cli_ui_out::do_flush (this=0x35a50b0) at ../../src/gdb/cli-out.c:250 #4 0x00000000009fd1f9 in ui_out::flush (this=0x35a50b0) at ../../src/gdb/ui-out.h:263 #5 0x00000000009f56ad in run_command_1 (args=0x0, from_tty=1, run_how=RUN_NORMAL) at ../../src/gdb/infcmd.c:449 #6 0x00000000009f599a in run_command (args=0x0, from_tty=1) at ../../src/gdb/infcmd.c:511 And if we check out tui_file::flush (tui-file.c) we can see that this just calls tui_win_info::refresh_window(), which in turn, just uses wnoutrefresh to batch any pending output. The problem is that, in the above backtrace, there is no tui_batch_rendering active, and so there will be no doupdate call to flush the output to the screen. We could add a tui_batch_rendering into tui_file::flush. And tui_file::write. And tui_file::puts ..... ... but that all seems a bit unnecessary. Instead, I propose that tui_win_info::refresh_window() should be changed. If suppress_output is true (i.e. a tui_batch_rendering is active) then we should continue to call wnoutrefresh(). But if suppress_output is false, meaning that no tui_batch_rendering is in place, then we should call wrefresh(), which immediately writes the output to the screen. Testing but PR gdb/32623 was a little involved. We need to 'run' the inferior and check for the 'Starting program' message. But DejaGNUU can only check for the message once it knows the message should have appeared. But, as the bug is that output is not displayed, we don't have any output hints that the inferior is started yet... In the end, I have the inferior create a file in the test's output directory. Now DejaGNU can send the 'run' command, and wait for the file to appear. Once that happens, we know that the 'Starting program' message should have appeared. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32623 Approved-By: Tom Tromey <tom@tromey.com>
178 lines
4.1 KiB
C
178 lines
4.1 KiB
C
/* General window behavior.
|
|
|
|
Copyright (C) 1998-2024 Free Software Foundation, Inc.
|
|
|
|
Contributed by Hewlett-Packard Company.
|
|
|
|
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 <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "tui/tui-data.h"
|
|
#include "tui/tui-io.h"
|
|
#include "tui/tui-wingeneral.h"
|
|
#include "tui/tui-win.h"
|
|
#include "cli/cli-style.h"
|
|
|
|
#include "gdb_curses.h"
|
|
|
|
/* This is true when there is a live instance of tui_batch_rendering.
|
|
The outermost tui_batch_rendering will cause a flush to the
|
|
screen. */
|
|
|
|
static bool suppress_output;
|
|
|
|
/* See tui-data.h. */
|
|
|
|
tui_batch_rendering::tui_batch_rendering ()
|
|
: m_saved_suppress (suppress_output)
|
|
{
|
|
suppress_output = true;
|
|
}
|
|
|
|
/* See tui-data.h. */
|
|
|
|
tui_batch_rendering::~tui_batch_rendering ()
|
|
{
|
|
suppress_output = m_saved_suppress;
|
|
if (!suppress_output)
|
|
doupdate ();
|
|
}
|
|
|
|
/* See tui-data.h. */
|
|
|
|
void
|
|
tui_win_info::refresh_window ()
|
|
{
|
|
if (handle != NULL)
|
|
{
|
|
if (suppress_output)
|
|
wnoutrefresh (handle.get ());
|
|
else
|
|
wrefresh (handle.get ());
|
|
}
|
|
}
|
|
|
|
/* Draw a border around the window. */
|
|
static void
|
|
box_win (struct tui_win_info *win_info,
|
|
bool highlight_flag)
|
|
{
|
|
WINDOW *win;
|
|
int attrs;
|
|
|
|
win = win_info->handle.get ();
|
|
if (highlight_flag)
|
|
attrs = tui_active_border_attrs;
|
|
else
|
|
attrs = tui_border_attrs;
|
|
|
|
/* tui_apply_style resets the style entirely, so be sure to call it
|
|
before applying ATTRS. */
|
|
if (cli_styling)
|
|
tui_apply_style (win, (highlight_flag
|
|
? tui_active_border_style.style ()
|
|
: tui_border_style.style ()));
|
|
wattron (win, attrs);
|
|
wborder (win, tui_border_vline, tui_border_vline,
|
|
tui_border_hline, tui_border_hline,
|
|
tui_border_ulcorner, tui_border_urcorner,
|
|
tui_border_llcorner, tui_border_lrcorner);
|
|
if (!win_info->title ().empty ())
|
|
{
|
|
/* Emit "+-TITLE-+" -- so 2 characters on the right and 2 on
|
|
the left. */
|
|
int max_len = win_info->width - win_info->box_size () - 2;
|
|
|
|
if (win_info->title ().size () <= max_len)
|
|
mvwaddstr (win, 0, 2, win_info->title ().c_str ());
|
|
else
|
|
{
|
|
std::string truncated
|
|
= "..." + win_info->title ().substr (win_info->title ().size ()
|
|
- max_len + 3);
|
|
mvwaddstr (win, 0, 2, truncated.c_str ());
|
|
}
|
|
}
|
|
wattroff (win, attrs);
|
|
tui_apply_style (win, ui_file_style ());
|
|
}
|
|
|
|
|
|
void
|
|
tui_unhighlight_win (struct tui_win_info *win_info)
|
|
{
|
|
if (win_info != NULL
|
|
&& win_info->can_box ()
|
|
&& win_info->handle != NULL)
|
|
{
|
|
box_win (win_info, false);
|
|
win_info->refresh_window ();
|
|
win_info->set_highlight (false);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
tui_highlight_win (struct tui_win_info *win_info)
|
|
{
|
|
if (win_info != NULL
|
|
&& win_info->can_box ()
|
|
&& win_info->handle != NULL)
|
|
{
|
|
box_win (win_info, true);
|
|
win_info->refresh_window ();
|
|
win_info->set_highlight (true);
|
|
}
|
|
}
|
|
|
|
void
|
|
tui_win_info::check_and_display_highlight_if_needed ()
|
|
{
|
|
if (can_box ())
|
|
{
|
|
if (is_highlighted)
|
|
tui_highlight_win (this);
|
|
else
|
|
tui_unhighlight_win (this);
|
|
}
|
|
}
|
|
|
|
void
|
|
tui_win_info::make_window ()
|
|
{
|
|
handle.reset (newwin (height, width, y, x));
|
|
if (handle != NULL)
|
|
{
|
|
scrollok (handle.get (), TRUE);
|
|
if (can_box ())
|
|
box_win (this, false);
|
|
}
|
|
}
|
|
|
|
/* We can't really make windows visible, or invisible. So we have to
|
|
delete the entire window when making it invisible, and create it
|
|
again when making it visible. */
|
|
void
|
|
tui_win_info::make_visible (bool visible)
|
|
{
|
|
if (is_visible () == visible)
|
|
return;
|
|
|
|
if (visible)
|
|
make_window ();
|
|
else
|
|
handle.reset (nullptr);
|
|
}
|