]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/tui/tui-regs.c
Fix TUI text centering
[thirdparty/binutils-gdb.git] / gdb / tui / tui-regs.c
CommitLineData
f377b406 1/* TUI display registers in window.
f33c6cbf 2
1d506c26 3 Copyright (C) 1998-2024 Free Software Foundation, Inc.
f33c6cbf 4
f377b406 5 Contributed by Hewlett-Packard Company.
c906108c 6
f377b406 7 This file is part of GDB.
c906108c 8
f377b406
SC
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
a9762ec7 11 the Free Software Foundation; either version 3 of the License, or
f377b406
SC
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
a9762ec7 20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
c906108c
SS
21
22#include "defs.h"
e17c207e 23#include "arch-utils.h"
d7b2e967
AC
24#include "tui/tui.h"
25#include "tui/tui-data.h"
c906108c
SS
26#include "symtab.h"
27#include "gdbtypes.h"
28#include "gdbcmd.h"
29#include "frame.h"
bc77de56 30#include "regcache.h"
c906108c
SS
31#include "inferior.h"
32#include "target.h"
d7b2e967
AC
33#include "tui/tui-layout.h"
34#include "tui/tui-win.h"
d7b2e967
AC
35#include "tui/tui-wingeneral.h"
36#include "tui/tui-file.h"
2c0b251b 37#include "tui/tui-regs.h"
312809f8 38#include "tui/tui-io.h"
10f59415 39#include "reggroups.h"
79a45b7d 40#include "valprint.h"
51f0e40d 41#include "completer.h"
c906108c 42
6a83354a 43#include "gdb_curses.h"
96ec9981 44
7a02bab7
TT
45/* A subclass of string_file that expands tab characters. */
46class tab_expansion_file : public string_file
47{
48public:
49
50 tab_expansion_file () = default;
51
52 void write (const char *buf, long length_buf) override;
53
54private:
55
56 int m_column = 0;
57};
58
59void
60tab_expansion_file::write (const char *buf, long length_buf)
61{
62 for (long i = 0; i < length_buf; ++i)
63 {
64 if (buf[i] == '\t')
65 {
66 do
67 {
68 string_file::write (" ", 1);
69 ++m_column;
70 }
71 while ((m_column % 8) != 0);
72 }
73 else
74 {
75 string_file::write (&buf[i], 1);
76 if (buf[i] == '\n')
77 m_column = 0;
78 else
79 ++m_column;
80 }
81 }
82}
83
1a4f81dd
TT
84/* Get the register from the frame and return a printable
85 representation of it. */
86
7a02bab7 87static std::string
8480a37e 88tui_register_format (const frame_info_ptr &frame, int regnum)
1a4f81dd
TT
89{
90 struct gdbarch *gdbarch = get_frame_arch (frame);
5eccfcc2 91
7a02bab7
TT
92 /* Expand tabs into spaces, since ncurses on MS-Windows doesn't. */
93 tab_expansion_file stream;
1a4f81dd
TT
94 gdbarch_print_registers_info (gdbarch, &stream, frame, regnum, 1);
95
96 /* Remove the possible \n. */
5d10a204 97 std::string str = stream.release ();
1a4f81dd 98 if (!str.empty () && str.back () == '\n')
32ed168c 99 str.pop_back ();
1a4f81dd 100
7a02bab7 101 return str;
1a4f81dd
TT
102}
103
b1adacb6
TT
104/* Compute the register value from the given frame and format it for
105 the display. update 'content' and set 'highlight' if the contents
106 changed. */
107void
6b323813 108tui_register_info::update (const frame_info_ptr &frame)
1a4f81dd 109{
66ab1a14
TT
110 std::string new_content = tui_register_format (frame, m_regno);
111 highlight = content != new_content;
112 content = std::move (new_content);
1a4f81dd 113}
96bd6233
TT
114
115/* See tui-regs.h. */
116
c906108c 117int
0b5ec218 118tui_data_window::last_regs_line_no () const
c906108c 119{
80df3337
TT
120 int num_lines = m_regs_content.size () / m_regs_column_count;
121 if (m_regs_content.size () % m_regs_column_count)
0670413d 122 num_lines++;
6ba8e26f 123 return num_lines;
55fb0713 124}
c906108c 125
18ab23af 126/* See tui-regs.h. */
c906108c 127
c906108c 128int
3b23c5f2 129tui_data_window::line_from_reg_element_no (int element_no) const
c906108c 130{
80df3337 131 if (element_no < m_regs_content.size ())
c906108c
SS
132 {
133 int i, line = (-1);
134
135 i = 1;
136 while (line == (-1))
137 {
80df3337 138 if (element_no < m_regs_column_count * i)
c906108c
SS
139 line = i - 1;
140 else
141 i++;
142 }
143
144 return line;
145 }
146 else
147 return (-1);
55fb0713 148}
c906108c 149
18ab23af 150/* See tui-regs.h. */
c906108c 151
c906108c 152int
baff0c28 153tui_data_window::first_reg_element_no_inline (int line_no) const
c906108c 154{
80df3337
TT
155 if (line_no * m_regs_column_count <= m_regs_content.size ())
156 return ((line_no + 1) * m_regs_column_count) - m_regs_column_count;
c906108c
SS
157 else
158 return (-1);
55fb0713 159}
c906108c 160
10f59415
SC
161/* Show the registers of the given group in the data window
162 and refresh the window. */
c906108c 163void
09df3a10 164tui_data_window::set_register_group (const reggroup *group)
1ad87082
TT
165{
166 update_register_data (group);
167 rerender ();
168}
169
170/* Set the data window to display the registers of the register group
171 using the given frame. */
172
173void
174tui_data_window::update_register_data (const reggroup *group)
c906108c 175{
1a259672 176 if (group == nullptr)
10f59415 177 group = general_reggroup;
c906108c 178
1ad87082
TT
179 if (!target_has_registers ()
180 || !target_has_stack ()
181 || !target_has_memory ())
368c1354 182 {
45a57c1e 183 set_title (_("Registers"));
1a259672 184 m_current_group = nullptr;
66ab1a14 185 m_gdbarch = nullptr;
a31bff9d 186 m_regs_content.clear ();
1ad87082 187 return;
368c1354 188 }
a31bff9d 189
1ad87082 190 frame_info_ptr frame = get_selected_frame (nullptr);
66ab1a14
TT
191 struct gdbarch *gdbarch = get_frame_arch (frame);
192
193 if (m_current_group == group && m_gdbarch == gdbarch)
194 {
195 /* Nothing to do here. */
196 return;
197 }
c906108c 198
45a57c1e 199 m_current_group = group;
66ab1a14 200 m_gdbarch = gdbarch;
10f59415 201
45a57c1e
TT
202 /* Make a new title showing which group we display. */
203 this->set_title (string_printf ("Register group: %s", group->name ()));
10f59415 204
45a57c1e
TT
205 /* Create the registers. */
206 m_regs_content.clear ();
10f59415 207
45a57c1e
TT
208 for (int regnum = 0;
209 regnum < gdbarch_num_cooked_regs (gdbarch);
210 regnum++)
211 {
212 /* Must be in the group. */
213 if (!gdbarch_register_reggroup_p (gdbarch, regnum, group))
214 continue;
10f59415 215
45a57c1e
TT
216 /* If the register name is empty, it is undefined for this
217 processor, so don't display anything. */
218 const char *name = gdbarch_register_name (gdbarch, regnum);
219 if (*name == '\0')
220 continue;
10f59415 221
45a57c1e 222 m_regs_content.emplace_back (regnum, frame);
21e1c91e 223 }
10f59415
SC
224}
225
18ab23af 226/* See tui-regs.h. */
517e9505 227
c906108c 228void
517e9505 229tui_data_window::display_registers_from (int start_element_no)
c906108c 230{
1ad87082
TT
231 werase (handle.get ());
232 check_and_display_highlight_if_needed ();
233
074b58b4
TT
234 /* In case the regs window is not boxed, we'll write the last char in the
235 last line here, causing a scroll, so prevent that. */
236 scrollok (handle.get (), FALSE);
237
0670413d 238 int max_len = 0;
80df3337 239 for (auto &&data_item_win : m_regs_content)
c906108c 240 {
7a02bab7 241 int len = data_item_win.content.size ();
0670413d
TT
242
243 if (len > max_len)
244 max_len = len;
245 }
7134f2eb 246 m_item_width = max_len + 1;
4cf28e91
HD
247
248 int i;
249 /* Mark register windows above the visible area. */
250 for (i = 0; i < start_element_no; i++)
251 m_regs_content[i].y = 0;
0670413d 252
ff3c86a8 253 m_regs_column_count = (width - box_size ()) / m_item_width;
80df3337
TT
254 if (m_regs_column_count == 0)
255 m_regs_column_count = 1;
ff3c86a8 256 m_item_width = (width - box_size ()) / m_regs_column_count;
0670413d
TT
257
258 /* Now create each data "sub" window, and write the display into
259 it. */
bebb0dd4 260 int cur_y = box_width ();
ff3c86a8 261 while (i < m_regs_content.size () && cur_y <= height - box_size ())
0670413d 262 {
7134f2eb 263 for (int j = 0;
80df3337 264 j < m_regs_column_count && i < m_regs_content.size ();
0670413d 265 j++)
c906108c 266 {
0670413d 267 /* Create the window if necessary. */
bebb0dd4 268 m_regs_content[i].x = box_width () + (m_item_width * j);
7134f2eb 269 m_regs_content[i].y = cur_y;
7134f2eb 270 m_regs_content[i].rerender (handle.get (), m_item_width);
0670413d 271 i++; /* Next register. */
c906108c 272 }
0670413d 273 cur_y++; /* Next row. */
c906108c 274 }
5fc2d6aa 275
4cf28e91
HD
276 /* Mark register windows below the visible area. */
277 for (; i < m_regs_content.size (); i++)
278 m_regs_content[i].y = 0;
279
5fc2d6aa 280 refresh_window ();
55fb0713 281}
c906108c 282
18ab23af 283/* See tui-regs.h. */
c906108c 284
aca2dd16
TT
285void
286tui_data_window::display_reg_element_at_line (int start_element_no,
287 int start_line_no)
c906108c 288{
0670413d 289 int element_no = start_element_no;
c906108c 290
0670413d
TT
291 if (start_element_no != 0 && start_line_no != 0)
292 {
293 int last_line_no, first_line_on_last_page;
294
295 last_line_no = last_regs_line_no ();
ff3c86a8 296 first_line_on_last_page = last_line_no - (height - box_size ());
0670413d
TT
297 if (first_line_on_last_page < 0)
298 first_line_on_last_page = 0;
299
300 /* If the element_no causes us to scroll past the end of the
301 registers, adjust what element to really start the
302 display at. */
303 if (start_line_no > first_line_on_last_page)
304 element_no = first_reg_element_no_inline (first_line_on_last_page);
c906108c 305 }
0670413d 306 display_registers_from (element_no);
6ba8e26f 307}
c906108c 308
18ab23af 309/* See tui-regs.h. */
c906108c 310
c906108c 311int
517e9505 312tui_data_window::display_registers_from_line (int line_no)
c906108c 313{
0670413d
TT
314 int element_no;
315
316 if (line_no < 0)
317 line_no = 0;
318 else
319 {
320 /* Make sure that we don't display off the end of the
321 registers. */
322 if (line_no >= last_regs_line_no ())
80cb6c27 323 {
80df3337 324 line_no = line_from_reg_element_no (m_regs_content.size () - 1);
0670413d
TT
325 if (line_no < 0)
326 line_no = 0;
c906108c 327 }
c906108c
SS
328 }
329
0670413d 330 element_no = first_reg_element_no_inline (line_no);
80df3337 331 if (element_no < m_regs_content.size ())
0670413d
TT
332 display_reg_element_at_line (element_no, line_no);
333 else
334 line_no = (-1);
335
336 return line_no;
55fb0713 337}
c906108c
SS
338
339
18ab23af
TT
340/* Answer the index first element displayed. If none are displayed,
341 then return (-1). */
342int
343tui_data_window::first_data_item_displayed ()
344{
80df3337 345 for (int i = 0; i < m_regs_content.size (); i++)
18ab23af 346 {
76139810 347 if (m_regs_content[i].visible ())
18ab23af
TT
348 return i;
349 }
350
351 return -1;
352}
353
18ab23af 354void
764593ab 355tui_data_window::erase_data_content ()
18ab23af 356{
a6a3b67f 357 center_string (_("[ Register Values Unavailable ]"));
18ab23af
TT
358}
359
360/* See tui-regs.h. */
361
362void
1ad87082 363tui_data_window::rerender ()
18ab23af 364{
80df3337 365 if (m_regs_content.empty ())
764593ab 366 erase_data_content ();
18ab23af 367 else
1ad87082
TT
368 display_registers_from (0);
369 tui_wrefresh (handle.get ());
18ab23af
TT
370}
371
372
18ab23af
TT
373/* Scroll the data window vertically forward or backward. */
374void
375tui_data_window::do_scroll_vertical (int num_to_scroll)
376{
377 int first_element_no;
378 int first_line = (-1);
379
380 first_element_no = first_data_item_displayed ();
80df3337 381 if (first_element_no < m_regs_content.size ())
18ab23af
TT
382 first_line = line_from_reg_element_no (first_element_no);
383 else
384 { /* Calculate the first line from the element number which is in
dda83cd7 385 the general data content. */
18ab23af
TT
386 }
387
388 if (first_line >= 0)
389 {
390 first_line += num_to_scroll;
18ab23af
TT
391 display_registers_from_line (first_line);
392 }
393}
394
55fb0713
AC
395/* This function check all displayed registers for changes in values,
396 given a particular frame. If the values have changed, they are
397 updated with the new value and highlighted. */
c906108c 398void
8480a37e 399tui_data_window::check_register_values (const frame_info_ptr &frame)
c906108c 400{
66ab1a14
TT
401 if (frame == nullptr)
402 set_register_group (nullptr);
63356bfd 403 else
c906108c 404 {
66ab1a14
TT
405 /* If the frame architecture changed, we need to reset the
406 register group. */
407 struct gdbarch *gdbarch = get_frame_arch (frame);
408 if (gdbarch != m_gdbarch)
409 set_register_group (nullptr);
410 else
c906108c 411 {
66ab1a14
TT
412 for (tui_register_info &data_item_win : m_regs_content)
413 {
414 bool was_hilighted = data_item_win.highlight;
10f59415 415
66ab1a14 416 data_item_win.update (frame);
10f59415 417
66ab1a14
TT
418 /* Register windows whose y == 0 are outside the visible area. */
419 if ((data_item_win.highlight || was_hilighted)
420 && data_item_win.visible ())
421 data_item_win.rerender (handle.get (), m_item_width);
422 }
c906108c 423 }
66ab1a14 424 tui_wrefresh (handle.get ());
c906108c 425 }
55fb0713 426}
c906108c 427
1cc6d956
MS
428/* Display a register in a window. If hilite is TRUE, then the value
429 will be displayed in reverse video. */
cdaa6eb4 430void
6b323813 431tui_register_info::rerender (WINDOW *handle, int field_width)
10f59415 432{
cdaa6eb4
TT
433 if (highlight)
434 /* We ignore the return value, casting it to void in order to avoid
435 a compiler warning. The warning itself was introduced by a patch
436 to ncurses 5.7 dated 2009-08-29, changing this macro to expand
437 to code that causes the compiler to generate an unused-value
438 warning. */
7134f2eb 439 (void) wstandout (handle);
10f59415 440
7134f2eb 441 mvwaddnstr (handle, y, x, content.c_str (), field_width - 1);
3537bc23
HD
442 if (content.size () < field_width)
443 waddstr (handle, n_spaces (field_width - content.size ()));
cdaa6eb4
TT
444
445 if (highlight)
446 /* We ignore the return value, casting it to void in order to avoid
447 a compiler warning. The warning itself was introduced by a patch
448 to ncurses 5.7 dated 2009-08-29, changing this macro to expand
449 to code that causes the compiler to generate an unused-value
450 warning. */
7134f2eb 451 (void) wstandend (handle);
10f59415
SC
452}
453
1bca9b1e
AB
454/* Helper for "tui reg next", returns the next register group after
455 CURRENT_GROUP in the register group list for GDBARCH, with wrap around
456 behaviour.
457
458 If CURRENT_GROUP is nullptr (e.g. if the tui register window has only
459 just been displayed and has no current group selected) or the currently
460 selected register group can't be found (e.g. if the architecture has
461 changed since the register window was last updated), then the first
462 register group will be returned. */
51f0e40d 463
711898e1
AB
464static const reggroup *
465tui_reg_next (const reggroup *current_group, struct gdbarch *gdbarch)
c906108c 466{
1bca9b1e
AB
467 const std::vector<const reggroup *> &groups = gdbarch_reggroups (gdbarch);
468 auto it = std::find (groups.begin (), groups.end (), current_group);
469 if (it != groups.end ())
470 it++;
471 if (it == groups.end ())
472 return groups.front ();
473 return *it;
10f59415
SC
474}
475
1bca9b1e
AB
476/* Helper for "tui reg prev", returns the register group previous to
477 CURRENT_GROUP in the register group list for GDBARCH, with wrap around
478 behaviour.
479
480 If CURRENT_GROUP is nullptr (e.g. if the tui register window has only
481 just been displayed and has no current group selected) or the currently
482 selected register group can't be found (e.g. if the architecture has
483 changed since the register window was last updated), then the last
484 register group will be returned. */
55b40027 485
711898e1
AB
486static const reggroup *
487tui_reg_prev (const reggroup *current_group, struct gdbarch *gdbarch)
55b40027 488{
1bca9b1e
AB
489 const std::vector<const reggroup *> &groups = gdbarch_reggroups (gdbarch);
490 auto it = std::find (groups.rbegin (), groups.rend (), current_group);
491 if (it != groups.rend ())
492 it++;
493 if (it == groups.rend ())
494 return groups.back ();
495 return *it;
55b40027
AB
496}
497
51f0e40d
AB
498/* Implement the 'tui reg' command. Changes the register group displayed
499 in the tui register window. Displays the tui register window if it is
500 not already on display. */
c906108c 501
10f59415 502static void
e2d8ae16 503tui_reg_command (const char *args, int from_tty)
10f59415 504{
51f0e40d 505 struct gdbarch *gdbarch = get_current_arch ();
c906108c 506
51f0e40d
AB
507 if (args != NULL)
508 {
51f0e40d
AB
509 size_t len = strlen (args);
510
511 /* Make sure the curses mode is enabled. */
512 tui_enable ();
513
45bbae5c
TT
514 tui_suppress_output suppress;
515
51f0e40d
AB
516 /* Make sure the register window is visible. If not, select an
517 appropriate layout. We need to do this before trying to run the
518 'next' or 'prev' commands. */
2d83e710 519 if (TUI_DATA_WIN == NULL || !TUI_DATA_WIN->is_visible ())
0dbc2fc7 520 tui_regs_layout ();
51f0e40d 521
1bca9b1e 522 const reggroup *match = nullptr;
711898e1 523 const reggroup *current_group = TUI_DATA_WIN->get_current_group ();
51f0e40d 524 if (strncmp (args, "next", len) == 0)
fe3eaf1c 525 match = tui_reg_next (current_group, gdbarch);
51f0e40d 526 else if (strncmp (args, "prev", len) == 0)
fe3eaf1c 527 match = tui_reg_prev (current_group, gdbarch);
5783701b 528 else
51f0e40d 529 {
5783701b
AB
530 /* This loop matches on the initial part of a register group
531 name. If this initial part in ARGS matches only one register
532 group then the switch is made. */
1bca9b1e 533 for (const struct reggroup *group : gdbarch_reggroups (gdbarch))
51f0e40d 534 {
af7ce09b 535 if (strncmp (group->name (), args, len) == 0)
5783701b
AB
536 {
537 if (match != NULL)
538 error (_("ambiguous register group name '%s'"), args);
539 match = group;
540 }
51f0e40d
AB
541 }
542 }
543
544 if (match == NULL)
545 error (_("unknown register group '%s'"), args);
546
09df3a10 547 TUI_DATA_WIN->set_register_group (match);
51f0e40d
AB
548 }
549 else
550 {
6cb06a8c
TT
551 gdb_printf (_("\"tui reg\" must be followed by the name of "
552 "either a register group,\nor one of 'next' "
553 "or 'prev'. Known register groups are:\n"));
51f0e40d 554
1bca9b1e
AB
555 bool first = true;
556 for (const struct reggroup *group : gdbarch_reggroups (gdbarch))
51f0e40d
AB
557 {
558 if (!first)
6cb06a8c 559 gdb_printf (", ");
1bca9b1e 560 first = false;
af7ce09b 561 gdb_printf ("%s", group->name ());
51f0e40d
AB
562 }
563
6cb06a8c 564 gdb_printf ("\n");
51f0e40d 565 }
10f59415
SC
566}
567
51f0e40d
AB
568/* Complete names of register groups, and add the special "prev" and "next"
569 names. */
c906108c 570
eb3ff9a5 571static void
51f0e40d 572tui_reggroup_completer (struct cmd_list_element *ignore,
eb3ff9a5 573 completion_tracker &tracker,
51f0e40d 574 const char *text, const char *word)
10f59415 575{
ea68593b 576 static const char * const extra[] = { "next", "prev", NULL };
51f0e40d 577
eb3ff9a5 578 reggroup_completer (ignore, tracker, text, word);
51f0e40d 579
ea68593b 580 complete_on_enum (tracker, extra, text, word);
10f59415 581}
c906108c 582
6c265988 583void _initialize_tui_regs ();
18ab23af 584void
6c265988 585_initialize_tui_regs ()
18ab23af
TT
586{
587 struct cmd_list_element **tuicmd, *cmd;
588
589 tuicmd = tui_get_cmd_list ();
590
591 cmd = add_cmd ("reg", class_tui, tui_reg_command, _("\
283be8bf
TT
592TUI command to control the register window.\n\
593Usage: tui reg NAME\n\
594NAME is the name of the register group to display"), tuicmd);
18ab23af
TT
595 set_cmd_completer (cmd, tui_reggroup_completer);
596}