1 /* TUI display registers in window.
3 Copyright (C) 1998-2019 Free Software Foundation, Inc.
5 Contributed by Hewlett-Packard Company.
7 This file is part of GDB.
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
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
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.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "arch-utils.h"
25 #include "tui/tui-data.h"
33 #include "tui/tui-layout.h"
34 #include "tui/tui-win.h"
35 #include "tui/tui-wingeneral.h"
36 #include "tui/tui-file.h"
37 #include "tui/tui-regs.h"
38 #include "tui/tui-io.h"
39 #include "reggroups.h"
41 #include "completer.h"
43 #include "gdb_curses.h"
45 static void tui_display_register (struct tui_data_item_window
*data
);
47 /* Get the register from the frame and return a printable
48 representation of it. */
50 static gdb::unique_xmalloc_ptr
<char>
51 tui_register_format (struct frame_info
*frame
, int regnum
)
53 struct gdbarch
*gdbarch
= get_frame_arch (frame
);
57 scoped_restore save_pagination
58 = make_scoped_restore (&pagination_enabled
, 0);
59 scoped_restore save_stdout
60 = make_scoped_restore (&gdb_stdout
, &stream
);
62 gdbarch_print_registers_info (gdbarch
, &stream
, frame
, regnum
, 1);
64 /* Remove the possible \n. */
65 std::string
&str
= stream
.string ();
66 if (!str
.empty () && str
.back () == '\n')
67 str
.resize (str
.size () - 1);
69 /* Expand tabs into spaces, since ncurses on MS-Windows doesn't. */
70 return tui_expand_tabs (str
.c_str ());
73 /* Get the register value from the given frame and format it for the
74 display. When changep is set, check if the new register value has
75 changed with respect to the previous call. */
77 tui_get_register (struct frame_info
*frame
,
78 struct tui_data_item_window
*data
,
79 int regnum
, bool *changedp
)
83 if (target_has_registers
)
85 gdb::unique_xmalloc_ptr
<char> new_content
86 = tui_register_format (frame
, regnum
);
89 && strcmp (data
->content
.get (), new_content
.get ()) != 0)
92 data
->content
= std::move (new_content
);
99 tui_data_window::last_regs_line_no () const
101 int num_lines
= (-1);
103 if (!regs_content
.empty ())
105 num_lines
= regs_content
.size () / regs_column_count
;
106 if (regs_content
.size () % regs_column_count
)
112 /* See tui-regs.h. */
115 tui_data_window::line_from_reg_element_no (int element_no
) const
117 if (element_no
< regs_content
.size ())
124 if (element_no
< regs_column_count
* i
)
136 /* See tui-regs.h. */
139 tui_data_window::first_reg_element_no_inline (int line_no
) const
141 if (line_no
* regs_column_count
<= regs_content
.size ())
142 return ((line_no
+ 1) * regs_column_count
) - regs_column_count
;
147 /* Show the registers of the given group in the data window
148 and refresh the window. */
150 tui_data_window::show_registers (struct reggroup
*group
)
153 group
= general_reggroup
;
155 if (target_has_registers
&& target_has_stack
&& target_has_memory
)
157 show_register_group (group
, get_selected_frame (NULL
),
158 group
== current_group
);
160 /* Clear all notation of changed values. */
161 for (auto &&data_item_win
: regs_content
)
162 data_item_win
.highlight
= false;
163 current_group
= group
;
169 erase_data_content (_("[ Register Values Unavailable ]"));
174 /* Set the data window to display the registers of the register group
175 using the given frame. Values are refreshed only when
176 refresh_values_only is TRUE. */
179 tui_data_window::show_register_group (struct reggroup
*group
,
180 struct frame_info
*frame
,
181 int refresh_values_only
)
183 struct gdbarch
*gdbarch
= get_frame_arch (frame
);
187 /* Make a new title showing which group we display. */
189 title
= xstrprintf ("Register group: %s", reggroup_name (group
));
191 /* See how many registers must be displayed. */
193 for (regnum
= 0; regnum
< gdbarch_num_cooked_regs (gdbarch
); regnum
++)
197 /* Must be in the group. */
198 if (!gdbarch_register_reggroup_p (gdbarch
, regnum
, group
))
201 /* If the register name is empty, it is undefined for this
202 processor, so don't display anything. */
203 name
= gdbarch_register_name (gdbarch
, regnum
);
204 if (name
== 0 || *name
== '\0')
210 regs_content
.resize (nr_regs
);
212 /* Now set the register names and values. */
214 for (regnum
= 0; regnum
< gdbarch_num_cooked_regs (gdbarch
); regnum
++)
216 struct tui_data_item_window
*data_item_win
;
219 /* Must be in the group. */
220 if (!gdbarch_register_reggroup_p (gdbarch
, regnum
, group
))
223 /* If the register name is empty, it is undefined for this
224 processor, so don't display anything. */
225 name
= gdbarch_register_name (gdbarch
, regnum
);
226 if (name
== 0 || *name
== '\0')
229 data_item_win
= ®s_content
[pos
];
232 if (!refresh_values_only
)
234 data_item_win
->item_no
= regnum
;
235 data_item_win
->name
= name
;
236 data_item_win
->highlight
= false;
238 tui_get_register (frame
, data_item_win
, regnum
, 0);
244 /* See tui-regs.h. */
247 tui_data_window::display_registers_from (int start_element_no
)
249 if (!regs_content
.empty ())
251 int j
, item_win_width
, cur_y
;
254 for (auto &&data_item_win
: regs_content
)
260 p
= data_item_win
.content
.get ();
267 item_win_width
= max_len
+ 1;
268 int i
= start_element_no
;
270 regs_column_count
= (width
- 2) / item_win_width
;
271 if (regs_column_count
== 0)
272 regs_column_count
= 1;
273 item_win_width
= (width
- 2) / regs_column_count
;
275 /* Now create each data "sub" window, and write the display into
278 while (i
< regs_content
.size ()
279 && cur_y
<= viewport_height
)
282 j
< regs_column_count
&& i
< regs_content
.size ();
285 struct tui_data_item_window
*data_item_win
;
287 /* Create the window if necessary. */
288 data_item_win
= ®s_content
[i
];
289 if (data_item_win
->handle
!= NULL
290 && (data_item_win
->height
!= 1
291 || data_item_win
->width
!= item_win_width
292 || data_item_win
->origin
.x
!= (item_win_width
* j
) + 1
293 || data_item_win
->origin
.y
!= cur_y
))
295 tui_delete_win (data_item_win
->handle
);
296 data_item_win
->handle
= 0;
299 if (data_item_win
->handle
== NULL
)
301 data_item_win
->height
= 1;
302 data_item_win
->width
= item_win_width
;
303 data_item_win
->origin
.x
= (item_win_width
* j
) + 1;
304 data_item_win
->origin
.y
= cur_y
;
305 tui_make_window (data_item_win
);
306 scrollok (data_item_win
->handle
, FALSE
);
308 touchwin (data_item_win
->handle
);
310 /* Get the printable representation of the register
312 tui_display_register (data_item_win
);
313 i
++; /* Next register. */
315 cur_y
++; /* Next row. */
320 /* See tui-regs.h. */
323 tui_data_window::display_reg_element_at_line (int start_element_no
,
326 if (!regs_content
.empty ())
328 int element_no
= start_element_no
;
330 if (start_element_no
!= 0 && start_line_no
!= 0)
332 int last_line_no
, first_line_on_last_page
;
334 last_line_no
= last_regs_line_no ();
335 first_line_on_last_page
= last_line_no
- (height
- 2);
336 if (first_line_on_last_page
< 0)
337 first_line_on_last_page
= 0;
339 /* If the element_no causes us to scroll past the end of the
340 registers, adjust what element to really start the
342 if (start_line_no
> first_line_on_last_page
)
343 element_no
= first_reg_element_no_inline (first_line_on_last_page
);
345 display_registers_from (element_no
);
349 /* See tui-regs.h. */
352 tui_data_window::display_registers_from_line (int line_no
)
354 check_and_display_highlight_if_needed ();
355 if (!regs_content
.empty ())
363 /* Make sure that we don't display off the end of the
365 if (line_no
>= last_regs_line_no ())
367 line_no
= line_from_reg_element_no (regs_content
.size () - 1);
373 element_no
= first_reg_element_no_inline (line_no
);
374 if (element_no
< regs_content
.size ())
375 display_reg_element_at_line (element_no
, line_no
);
382 return (-1); /* Nothing was displayed. */
386 /* Answer the index first element displayed. If none are displayed,
389 tui_data_window::first_data_item_displayed ()
391 for (int i
= 0; i
< regs_content
.size (); i
++)
393 struct tui_gen_win_info
*data_item_win
;
395 data_item_win
= ®s_content
[i
];
396 if (data_item_win
->is_visible ())
403 /* See tui-regs.h. */
406 tui_data_window::delete_data_content_windows ()
408 for (auto &&win
: regs_content
)
410 tui_delete_win (win
.handle
);
417 tui_data_window::erase_data_content (const char *prompt
)
420 check_and_display_highlight_if_needed ();
423 int half_width
= (width
- 2) / 2;
426 if (strlen (prompt
) >= half_width
)
429 x_pos
= half_width
- strlen (prompt
);
430 mvwaddstr (handle
, (height
/ 2), x_pos
, (char *) prompt
);
435 /* See tui-regs.h. */
438 tui_data_window::display_all_data ()
440 if (regs_content
.empty ())
441 erase_data_content (NO_DATA_STRING
);
444 erase_data_content (NULL
);
445 delete_data_content_windows ();
446 check_and_display_highlight_if_needed ();
447 display_registers_from (0);
452 /* Function to redisplay the contents of the data window. */
454 tui_data_window::refresh_all ()
456 erase_data_content (NULL
);
457 if (!regs_content
.empty ())
459 int first_element
= first_data_item_displayed ();
461 if (first_element
>= 0) /* Re-use existing windows. */
463 int first_line
= (-1);
465 if (first_element
< regs_content
.size ())
466 first_line
= line_from_reg_element_no (first_element
);
470 erase_data_content (NULL
);
471 display_registers_from_line (first_line
);
478 /* Scroll the data window vertically forward or backward. */
480 tui_data_window::do_scroll_vertical (int num_to_scroll
)
482 int first_element_no
;
483 int first_line
= (-1);
485 first_element_no
= first_data_item_displayed ();
486 if (first_element_no
< regs_content
.size ())
487 first_line
= line_from_reg_element_no (first_element_no
);
489 { /* Calculate the first line from the element number which is in
490 the general data content. */
495 first_line
+= num_to_scroll
;
496 erase_data_content (NULL
);
497 delete_data_content_windows ();
498 display_registers_from_line (first_line
);
502 /* See tui-regs.h. */
505 tui_data_window::rerender ()
507 /* Delete all data item windows. */
508 for (auto &&win
: regs_content
)
510 tui_delete_win (win
.handle
);
516 /* See tui-regs.h. */
519 tui_data_window::refresh_window ()
521 tui_gen_win_info::refresh_window ();
522 for (auto &&win
: regs_content
)
523 win
.refresh_window ();
526 /* This function check all displayed registers for changes in values,
527 given a particular frame. If the values have changed, they are
528 updated with the new value and highlighted. */
530 tui_data_window::check_register_values (struct frame_info
*frame
)
532 if (regs_content
.empty ())
533 show_registers (current_group
);
536 for (auto &&data_item_win
: regs_content
)
540 was_hilighted
= data_item_win
.highlight
;
542 tui_get_register (frame
, &data_item_win
,
543 data_item_win
.item_no
,
544 &data_item_win
.highlight
);
546 if (data_item_win
.highlight
|| was_hilighted
)
547 tui_display_register (&data_item_win
);
552 /* Display a register in a window. If hilite is TRUE, then the value
553 will be displayed in reverse video. */
555 tui_display_register (struct tui_data_item_window
*data
)
557 if (data
->handle
!= NULL
)
562 /* We ignore the return value, casting it to void in order to avoid
563 a compiler warning. The warning itself was introduced by a patch
564 to ncurses 5.7 dated 2009-08-29, changing this macro to expand
565 to code that causes the compiler to generate an unused-value
567 (void) wstandout (data
->handle
);
569 wmove (data
->handle
, 0, 0);
570 for (i
= 1; i
< data
->width
; i
++)
571 waddch (data
->handle
, ' ');
572 wmove (data
->handle
, 0, 0);
574 waddstr (data
->handle
, data
->content
.get ());
577 /* We ignore the return value, casting it to void in order to avoid
578 a compiler warning. The warning itself was introduced by a patch
579 to ncurses 5.7 dated 2009-08-29, changing this macro to expand
580 to code that causes the compiler to generate an unused-value
582 (void) wstandend (data
->handle
);
583 data
->refresh_window ();
587 /* Helper for "tui reg next", wraps a call to REGGROUP_NEXT, but adds wrap
588 around behaviour. Returns the next register group, or NULL if the
589 register window is not currently being displayed. */
591 static struct reggroup
*
592 tui_reg_next (struct reggroup
*current_group
, struct gdbarch
*gdbarch
)
594 struct reggroup
*group
= NULL
;
596 if (current_group
!= NULL
)
598 group
= reggroup_next (gdbarch
, current_group
);
600 group
= reggroup_next (gdbarch
, NULL
);
605 /* Helper for "tui reg prev", wraps a call to REGGROUP_PREV, but adds wrap
606 around behaviour. Returns the previous register group, or NULL if the
607 register window is not currently being displayed. */
609 static struct reggroup
*
610 tui_reg_prev (struct reggroup
*current_group
, struct gdbarch
*gdbarch
)
612 struct reggroup
*group
= NULL
;
614 if (current_group
!= NULL
)
616 group
= reggroup_prev (gdbarch
, current_group
);
618 group
= reggroup_prev (gdbarch
, NULL
);
623 /* A helper function to display the register window in the appropriate
629 enum tui_layout_type cur_layout
= tui_current_layout ();
630 enum tui_layout_type new_layout
;
631 if (cur_layout
== SRC_COMMAND
|| cur_layout
== SRC_DATA_COMMAND
)
632 new_layout
= SRC_DATA_COMMAND
;
634 new_layout
= DISASSEM_DATA_COMMAND
;
635 tui_set_layout (new_layout
);
638 /* Implement the 'tui reg' command. Changes the register group displayed
639 in the tui register window. Displays the tui register window if it is
640 not already on display. */
643 tui_reg_command (const char *args
, int from_tty
)
645 struct gdbarch
*gdbarch
= get_current_arch ();
649 struct reggroup
*group
, *match
= NULL
;
650 size_t len
= strlen (args
);
652 /* Make sure the curses mode is enabled. */
655 /* Make sure the register window is visible. If not, select an
656 appropriate layout. We need to do this before trying to run the
657 'next' or 'prev' commands. */
658 if (TUI_DATA_WIN
== NULL
|| !TUI_DATA_WIN
->is_visible ())
661 struct reggroup
*current_group
= TUI_DATA_WIN
->current_group
;
662 if (strncmp (args
, "next", len
) == 0)
663 match
= tui_reg_next (current_group
, gdbarch
);
664 else if (strncmp (args
, "prev", len
) == 0)
665 match
= tui_reg_prev (current_group
, gdbarch
);
667 /* This loop matches on the initial part of a register group
668 name. If this initial part in ARGS matches only one register
669 group then the switch is made. */
670 for (group
= reggroup_next (gdbarch
, NULL
);
672 group
= reggroup_next (gdbarch
, group
))
674 if (strncmp (reggroup_name (group
), args
, len
) == 0)
677 error (_("ambiguous register group name '%s'"), args
);
683 error (_("unknown register group '%s'"), args
);
685 TUI_DATA_WIN
->show_registers (match
);
689 struct reggroup
*group
;
692 printf_unfiltered (_("\"tui reg\" must be followed by the name of "
693 "either a register group,\nor one of 'next' "
694 "or 'prev'. Known register groups are:\n"));
696 for (first
= 1, group
= reggroup_next (gdbarch
, NULL
);
698 first
= 0, group
= reggroup_next (gdbarch
, group
))
701 printf_unfiltered (", ");
702 printf_unfiltered ("%s", reggroup_name (group
));
705 printf_unfiltered ("\n");
709 /* Complete names of register groups, and add the special "prev" and "next"
713 tui_reggroup_completer (struct cmd_list_element
*ignore
,
714 completion_tracker
&tracker
,
715 const char *text
, const char *word
)
717 static const char *extra
[] = { "next", "prev", NULL
};
718 size_t len
= strlen (word
);
721 reggroup_completer (ignore
, tracker
, text
, word
);
723 /* XXXX use complete_on_enum instead? */
724 for (tmp
= extra
; *tmp
!= NULL
; ++tmp
)
726 if (strncmp (word
, *tmp
, len
) == 0)
727 tracker
.add_completion (make_unique_xstrdup (*tmp
));
732 _initialize_tui_regs (void)
734 struct cmd_list_element
**tuicmd
, *cmd
;
736 tuicmd
= tui_get_cmd_list ();
738 cmd
= add_cmd ("reg", class_tui
, tui_reg_command
, _("\
739 TUI command to control the register window."), tuicmd
);
740 set_cmd_completer (cmd
, tui_reggroup_completer
);