Use macros for TUI window names
[deliverable/binutils-gdb.git] / gdb / tui / tui-layout.c
CommitLineData
f377b406 1/* TUI layout window management.
f33c6cbf 2
b811d2c2 3 Copyright (C) 1998-2020 Free Software Foundation, Inc.
f33c6cbf 4
f377b406 5 Contributed by Hewlett-Packard Company.
c906108c 6
f377b406
SC
7 This file is part of GDB.
8
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"
957b8b5a 23#include "arch-utils.h"
c906108c
SS
24#include "command.h"
25#include "symtab.h"
26#include "frame.h"
52575520 27#include "source.h"
416eb92d
TT
28#include "cli/cli-cmds.h"
29#include "cli/cli-decode.h"
ee325b61 30#include "cli/cli-utils.h"
84b1e7c7 31#include <ctype.h>
0240c8f1 32#include <unordered_map>
ee325b61 33#include <unordered_set>
c906108c 34
d7b2e967 35#include "tui/tui.h"
ce38393b 36#include "tui/tui-command.h"
d7b2e967 37#include "tui/tui-data.h"
d7b2e967
AC
38#include "tui/tui-wingeneral.h"
39#include "tui/tui-stack.h"
40#include "tui/tui-regs.h"
41#include "tui/tui-win.h"
42#include "tui/tui-winsource.h"
43#include "tui/tui-disasm.h"
2c0b251b 44#include "tui/tui-layout.h"
bfad4537 45#include "tui/tui-source.h"
6a83354a 46#include "gdb_curses.h"
96ec9981 47
13274fc3 48static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
c906108c 49
416eb92d
TT
50/* The layouts. */
51static std::vector<std::unique_ptr<tui_layout_split>> layouts;
2192a9d3
TT
52
53/* The layout that is currently applied. */
54static std::unique_ptr<tui_layout_base> applied_layout;
55
416eb92d
TT
56/* The "skeleton" version of the layout that is currently applied. */
57static tui_layout_split *applied_skeleton;
62cf57fe 58
416eb92d
TT
59/* The two special "regs" layouts. Note that these aren't registered
60 as commands and so can never be deleted. */
61static tui_layout_split *src_regs_layout;
62static tui_layout_split *asm_regs_layout;
62cf57fe 63
7eed1a8e
TT
64/* See tui-data.h. */
65std::vector<tui_win_info *> tui_windows;
66
01b1af32
TT
67/* When applying a layout, this is the list of all windows that were
68 in the previous layout. This is used to re-use windows when
69 changing a layout. */
70static std::vector<tui_win_info *> saved_tui_windows;
71
2192a9d3
TT
72/* See tui-layout.h. */
73
74void
75tui_apply_current_layout ()
76{
865a5aec
TT
77 struct gdbarch *gdbarch;
78 CORE_ADDR addr;
79
80 extract_display_start_addr (&gdbarch, &addr);
81
01b1af32 82 saved_tui_windows = std::move (tui_windows);
7eed1a8e 83 tui_windows.clear ();
865a5aec 84
01b1af32 85 for (tui_win_info *win_info : saved_tui_windows)
865a5aec
TT
86 win_info->make_visible (false);
87
2192a9d3 88 applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
865a5aec
TT
89
90 /* Keep the list of internal windows up-to-date. */
91 for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
92 if (tui_win_list[win_type] != nullptr
93 && !tui_win_list[win_type]->is_visible ())
94 tui_win_list[win_type] = nullptr;
95
96 /* This should always be made visible by a layout. */
97 gdb_assert (TUI_CMD_WIN->is_visible ());
98
99 /* Now delete any window that was not re-applied. */
100 tui_win_info *focus = tui_win_with_focus ();
01b1af32 101 for (tui_win_info *win_info : saved_tui_windows)
865a5aec
TT
102 {
103 if (!win_info->is_visible ())
104 {
105 if (focus == win_info)
106 tui_set_win_focus_to (tui_windows[0]);
107 delete win_info;
108 }
109 }
110
111 if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
112 tui_get_begin_asm_address (&gdbarch, &addr);
113 tui_update_source_windows_with_addr (gdbarch, addr);
01b1af32
TT
114
115 saved_tui_windows.clear ();
2192a9d3 116}
c906108c 117
d4eeccfe
TT
118/* See tui-layout. */
119
120void
121tui_adjust_window_height (struct tui_win_info *win, int new_height)
122{
123 applied_layout->adjust_size (win->name (), new_height);
124}
125
416eb92d 126/* Set the current layout to LAYOUT. */
c906108c 127
416eb92d
TT
128static void
129tui_set_layout (tui_layout_split *layout)
c906108c 130{
416eb92d
TT
131 applied_skeleton = layout;
132 applied_layout = layout->clone ();
133 tui_apply_current_layout ();
bc712bbf 134}
c906108c 135
59b8b5d2
TT
136/* See tui-layout.h. */
137
c906108c 138void
080ce8c0 139tui_add_win_to_layout (enum tui_win_type type)
c906108c 140{
59b8b5d2
TT
141 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
142
416eb92d
TT
143 /* If the window already exists, no need to add it. */
144 if (tui_win_list[type] != nullptr)
145 return;
146
147 /* If the window we are trying to replace doesn't exist, we're
148 done. */
149 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
150 if (tui_win_list[other] == nullptr)
151 return;
152
153 const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
154 applied_layout->replace_window (tui_win_list[other]->name (), name);
155 tui_apply_current_layout ();
416eb92d
TT
156}
157
158/* Find LAYOUT in the "layouts" global and return its index. */
c906108c 159
416eb92d
TT
160static size_t
161find_layout (tui_layout_split *layout)
162{
163 for (size_t i = 0; i < layouts.size (); ++i)
c906108c 164 {
416eb92d
TT
165 if (layout == layouts[i].get ())
166 return i;
c906108c 167 }
416eb92d 168 gdb_assert_not_reached (_("layout not found!?"));
6ba8e26f 169}
c906108c 170
416eb92d 171/* Function to set the layout. */
a0145030 172
eb3ff9a5 173static void
416eb92d
TT
174tui_apply_layout (struct cmd_list_element *command,
175 const char *args, int from_tty)
a0145030 176{
416eb92d
TT
177 tui_layout_split *layout
178 = (tui_layout_split *) get_cmd_context (command);
a0145030 179
416eb92d
TT
180 /* Make sure the curses mode is enabled. */
181 tui_enable ();
182 tui_set_layout (layout);
a0145030
AB
183}
184
416eb92d 185/* See tui-layout.h. */
c906108c 186
416eb92d
TT
187void
188tui_next_layout ()
189{
190 size_t index = find_layout (applied_skeleton);
191 ++index;
192 if (index == layouts.size ())
193 index = 0;
194 tui_set_layout (layouts[index].get ());
195}
c906108c 196
416eb92d 197/* Implement the "layout next" command. */
c906108c 198
416eb92d
TT
199static void
200tui_next_layout_command (const char *arg, int from_tty)
201{
0379b883 202 tui_enable ();
416eb92d 203 tui_next_layout ();
e8b915dc 204}
c906108c 205
427326a8
TT
206/* See tui-layout.h. */
207
208void
416eb92d
TT
209tui_set_initial_layout ()
210{
211 tui_set_layout (layouts[0].get ());
212}
213
214/* Implement the "layout prev" command. */
215
216static void
217tui_prev_layout_command (const char *arg, int from_tty)
427326a8 218{
416eb92d
TT
219 tui_enable ();
220 size_t index = find_layout (applied_skeleton);
221 if (index == 0)
222 index = layouts.size ();
223 --index;
224 tui_set_layout (layouts[index].get ());
427326a8 225}
c906108c 226
416eb92d 227
5afe342e
TT
228/* See tui-layout.h. */
229
0dbc2fc7
TT
230void
231tui_regs_layout ()
232{
416eb92d
TT
233 /* If there's already a register window, we're done. */
234 if (TUI_DATA_WIN != nullptr)
235 return;
236
237 tui_set_layout (TUI_DISASM_WIN != nullptr
238 ? asm_regs_layout
239 : src_regs_layout);
240}
241
242/* Implement the "layout regs" command. */
243
244static void
245tui_regs_layout_command (const char *arg, int from_tty)
246{
247 tui_enable ();
248 tui_regs_layout ();
0dbc2fc7
TT
249}
250
251/* See tui-layout.h. */
252
5afe342e
TT
253void
254tui_remove_some_windows ()
255{
256 tui_win_info *focus = tui_win_with_focus ();
257
de543742 258 if (strcmp (focus->name (), CMD_NAME) == 0)
5afe342e
TT
259 {
260 /* Try leaving the source or disassembly window. If neither
261 exists, just do nothing. */
262 focus = TUI_SRC_WIN;
263 if (focus == nullptr)
264 focus = TUI_DISASM_WIN;
265 if (focus == nullptr)
266 return;
267 }
268
269 applied_layout->remove_windows (focus->name ());
270 tui_apply_current_layout ();
271}
272
13274fc3
UW
273static void
274extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
c906108c 275{
416eb92d 276 if (TUI_SRC_WIN != nullptr)
432b5c40 277 TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
416eb92d 278 else if (TUI_DISASM_WIN != nullptr)
432b5c40
TT
279 TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
280 else
416eb92d 281 {
432b5c40
TT
282 *gdbarch_p = nullptr;
283 *addr_p = 0;
c906108c 284 }
6ba8e26f 285}
c906108c 286
d6ba6a11 287void
ee556432
TT
288tui_gen_win_info::resize (int height_, int width_,
289 int origin_x_, int origin_y_)
c906108c 290{
cdaa6eb4 291 if (width == width_ && height == height_
fb3184d8 292 && x == origin_x_ && y == origin_y_
cdaa6eb4
TT
293 && handle != nullptr)
294 return;
295
d6ba6a11 296 width = width_;
db502012 297 height = height_;
fb3184d8
TT
298 x = origin_x_;
299 y = origin_y_;
db502012
TT
300
301 if (handle != nullptr)
302 {
303#ifdef HAVE_WRESIZE
7523da63 304 wresize (handle.get (), height, width);
fb3184d8 305 mvwin (handle.get (), y, x);
7523da63 306 wmove (handle.get (), 0, 0);
db502012 307#else
7523da63 308 handle.reset (nullptr);
db502012
TT
309#endif
310 }
311
312 if (handle == nullptr)
ab0e1f1a 313 make_window ();
3df505f6
TT
314
315 rerender ();
d6ba6a11 316}
c906108c 317
d9fcefd5
TT
318\f
319
0240c8f1
TT
320/* Helper function to create one of the built-in (non-locator)
321 windows. */
322
323template<enum tui_win_type V, class T>
324static tui_gen_win_info *
325make_standard_window (const char *)
326{
327 if (tui_win_list[V] == nullptr)
328 tui_win_list[V] = new T ();
329 return tui_win_list[V];
330}
331
332/* Helper function to wrap tui_locator_win_info_ptr for
333 tui_get_window_by_name. */
334
335static tui_gen_win_info *
336get_locator_window (const char *)
337{
338 return tui_locator_win_info_ptr ();
339}
340
341/* A map holding all the known window types, keyed by name. Note that
342 this is heap-allocated and "leaked" at gdb exit. This avoids
343 ordering issues with destroying elements in the map at shutdown.
344 In particular, destroying this map can occur after Python has been
345 shut down, causing crashes if any window destruction requires
346 running Python code. */
347
348static std::unordered_map<std::string, window_factory> *known_window_types;
349
389e7ddb
TT
350/* Helper function that returns a TUI window, given its name. */
351
352static tui_gen_win_info *
353tui_get_window_by_name (const std::string &name)
354{
0240c8f1
TT
355 for (tui_win_info *window : saved_tui_windows)
356 if (name == window->name ())
357 return window;
358
359 auto iter = known_window_types->find (name);
360 if (iter == known_window_types->end ())
361 error (_("Unknown window type \"%s\""), name.c_str ());
362
363 tui_gen_win_info *result = iter->second (name.c_str ());
364 if (result == nullptr)
365 error (_("Could not create window \"%s\""), name.c_str ());
366 return result;
367}
368
369/* Initialize the known window types. */
370
371static void
372initialize_known_windows ()
373{
374 known_window_types = new std::unordered_map<std::string, window_factory>;
375
de543742 376 known_window_types->emplace (SRC_NAME,
0240c8f1
TT
377 make_standard_window<SRC_WIN,
378 tui_source_window>);
de543742 379 known_window_types->emplace (CMD_NAME,
0240c8f1 380 make_standard_window<CMD_WIN, tui_cmd_window>);
de543742 381 known_window_types->emplace (DATA_NAME,
0240c8f1
TT
382 make_standard_window<DATA_WIN,
383 tui_data_window>);
de543742 384 known_window_types->emplace (DISASSEM_NAME,
0240c8f1
TT
385 make_standard_window<DISASSEM_WIN,
386 tui_disasm_window>);
de543742 387 known_window_types->emplace (STATUS_NAME, get_locator_window);
389e7ddb
TT
388}
389
390/* See tui-layout.h. */
391
01b1af32
TT
392void
393tui_register_window (const char *name, window_factory &&factory)
394{
395 std::string name_copy = name;
396
de543742
TT
397 if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
398 || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
01b1af32
TT
399 error (_("Window type \"%s\" is built-in"), name);
400
401 known_window_types->emplace (std::move (name_copy),
402 std::move (factory));
403}
404
405/* See tui-layout.h. */
406
389e7ddb
TT
407std::unique_ptr<tui_layout_base>
408tui_layout_window::clone () const
409{
410 tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
411 return std::unique_ptr<tui_layout_base> (result);
412}
413
414/* See tui-layout.h. */
415
416void
417tui_layout_window::apply (int x_, int y_, int width_, int height_)
418{
419 x = x_;
420 y = y_;
421 width = width_;
422 height = height_;
423 gdb_assert (m_window != nullptr);
424 m_window->resize (height, width, x, y);
7eed1a8e
TT
425 if (dynamic_cast<tui_win_info *> (m_window) != nullptr)
426 tui_windows.push_back ((tui_win_info *) m_window);
389e7ddb
TT
427}
428
429/* See tui-layout.h. */
430
431void
7c043ba6 432tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
389e7ddb
TT
433{
434 if (m_window == nullptr)
435 m_window = tui_get_window_by_name (m_contents);
7c043ba6
TT
436 if (height)
437 {
438 *min_value = m_window->min_height ();
439 *max_value = m_window->max_height ();
440 }
441 else
442 {
443 *min_value = m_window->min_width ();
444 *max_value = m_window->max_width ();
445 }
389e7ddb
TT
446}
447
448/* See tui-layout.h. */
449
450bool
451tui_layout_window::top_boxed_p () const
452{
453 gdb_assert (m_window != nullptr);
454 return m_window->can_box ();
455}
456
457/* See tui-layout.h. */
458
459bool
460tui_layout_window::bottom_boxed_p () const
461{
462 gdb_assert (m_window != nullptr);
463 return m_window->can_box ();
464}
465
466/* See tui-layout.h. */
467
416eb92d
TT
468void
469tui_layout_window::replace_window (const char *name, const char *new_window)
470{
471 if (m_contents == name)
472 {
473 m_contents = new_window;
474 if (m_window != nullptr)
475 {
476 m_window->make_visible (false);
477 m_window = tui_get_window_by_name (m_contents);
478 }
479 }
480}
481
482/* See tui-layout.h. */
483
ee325b61 484void
c22fef7e 485tui_layout_window::specification (ui_file *output, int depth)
ee325b61
TT
486{
487 fputs_unfiltered (get_name (), output);
488}
489
490/* See tui-layout.h. */
491
c22fef7e
TT
492void
493tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
494 int weight)
389e7ddb 495{
c22fef7e 496 split s = {weight, std::move (layout)};
389e7ddb 497 m_splits.push_back (std::move (s));
389e7ddb
TT
498}
499
500/* See tui-layout.h. */
501
502void
503tui_layout_split::add_window (const char *name, int weight)
504{
505 tui_layout_window *result = new tui_layout_window (name);
506 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
507 m_splits.push_back (std::move (s));
508}
509
510/* See tui-layout.h. */
511
512std::unique_ptr<tui_layout_base>
513tui_layout_split::clone () const
514{
7c043ba6 515 tui_layout_split *result = new tui_layout_split (m_vertical);
389e7ddb
TT
516 for (const split &item : m_splits)
517 {
518 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
519 split s = {item.weight, std::move (next)};
520 result->m_splits.push_back (std::move (s));
521 }
522 return std::unique_ptr<tui_layout_base> (result);
523}
524
525/* See tui-layout.h. */
526
527void
7c043ba6 528tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
389e7ddb 529{
7c043ba6
TT
530 *min_value = 0;
531 *max_value = 0;
532 bool first_time = true;
389e7ddb
TT
533 for (const split &item : m_splits)
534 {
535 int new_min, new_max;
7c043ba6
TT
536 item.layout->get_sizes (height, &new_min, &new_max);
537 /* For the mismatch case, the first time through we want to set
538 the min and max to the computed values -- the "first_time"
539 check here is just a funny way of doing that. */
540 if (height == m_vertical || first_time)
541 {
542 *min_value += new_min;
543 *max_value += new_max;
544 }
545 else
546 {
547 *min_value = std::max (*min_value, new_min);
548 *max_value = std::min (*max_value, new_max);
549 }
550 first_time = false;
389e7ddb
TT
551 }
552}
553
554/* See tui-layout.h. */
555
556bool
557tui_layout_split::top_boxed_p () const
558{
559 if (m_splits.empty ())
560 return false;
561 return m_splits[0].layout->top_boxed_p ();
562}
563
564/* See tui-layout.h. */
565
566bool
567tui_layout_split::bottom_boxed_p () const
568{
569 if (m_splits.empty ())
570 return false;
571 return m_splits.back ().layout->top_boxed_p ();
572}
573
574/* See tui-layout.h. */
575
576void
577tui_layout_split::set_weights_from_heights ()
578{
579 for (int i = 0; i < m_splits.size (); ++i)
580 m_splits[i].weight = m_splits[i].layout->height;
581}
582
583/* See tui-layout.h. */
584
6bc56648 585tui_adjust_result
389e7ddb
TT
586tui_layout_split::adjust_size (const char *name, int new_height)
587{
588 /* Look through the children. If one is a layout holding the named
589 window, we're done; or if one actually is the named window,
590 update it. */
591 int found_index = -1;
592 for (int i = 0; i < m_splits.size (); ++i)
593 {
6bc56648
TT
594 tui_adjust_result adjusted
595 = m_splits[i].layout->adjust_size (name, new_height);
596 if (adjusted == HANDLED)
597 return HANDLED;
598 if (adjusted == FOUND)
389e7ddb 599 {
7c043ba6
TT
600 if (!m_vertical)
601 return FOUND;
389e7ddb
TT
602 found_index = i;
603 break;
604 }
605 }
606
607 if (found_index == -1)
6bc56648 608 return NOT_FOUND;
389e7ddb 609 if (m_splits[found_index].layout->height == new_height)
6bc56648 610 return HANDLED;
389e7ddb
TT
611
612 set_weights_from_heights ();
613 int delta = m_splits[found_index].weight - new_height;
614 m_splits[found_index].weight = new_height;
615
616 /* Distribute the "delta" over the next window; but if the next
617 window cannot hold it all, keep going until we either find a
618 window that does, or until we loop all the way around. */
619 for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
620 {
621 int index = (found_index + 1 + i) % m_splits.size ();
622
623 int new_min, new_max;
7c043ba6 624 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
389e7ddb
TT
625
626 if (delta < 0)
627 {
628 /* The primary window grew, so we are trying to shrink other
629 windows. */
630 int available = m_splits[index].weight - new_min;
631 int shrink_by = std::min (available, -delta);
632 m_splits[index].weight -= shrink_by;
633 delta += shrink_by;
634 }
635 else
636 {
637 /* The primary window shrank, so we are trying to grow other
638 windows. */
639 int available = new_max - m_splits[index].weight;
640 int grow_by = std::min (available, delta);
641 m_splits[index].weight += grow_by;
642 delta -= grow_by;
643 }
644 }
645
646 if (delta != 0)
647 {
648 warning (_("Invalid window height specified"));
649 /* Effectively undo any modifications made here. */
650 set_weights_from_heights ();
651 }
652 else
653 {
654 /* Simply re-apply the updated layout. */
655 apply (x, y, width, height);
656 }
657
6bc56648 658 return HANDLED;
389e7ddb
TT
659}
660
661/* See tui-layout.h. */
662
663void
664tui_layout_split::apply (int x_, int y_, int width_, int height_)
665{
666 x = x_;
667 y = y_;
668 width = width_;
669 height = height_;
670
7c043ba6 671 struct size_info
389e7ddb 672 {
7c043ba6
TT
673 int size;
674 int min_size;
675 int max_size;
389e7ddb
TT
676 /* True if this window will share a box border with the previous
677 window in the list. */
678 bool share_box;
679 };
680
7c043ba6 681 std::vector<size_info> info (m_splits.size ());
389e7ddb 682
7c043ba6
TT
683 /* Step 1: Find the min and max size of each sub-layout.
684 Fixed-sized layouts are given their desired size, and then the
389e7ddb
TT
685 remaining space is distributed among the remaining windows
686 according to the weights given. */
7c043ba6 687 int available_size = m_vertical ? height : width;
389e7ddb
TT
688 int last_index = -1;
689 int total_weight = 0;
690 for (int i = 0; i < m_splits.size (); ++i)
691 {
692 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
693
694 /* Always call get_sizes, to ensure that the window is
695 instantiated. This is a bit gross but less gross than adding
696 special cases for this in other places. */
7c043ba6
TT
697 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
698 &info[i].max_size);
389e7ddb
TT
699
700 if (!m_applied
701 && cmd_win_already_exists
702 && m_splits[i].layout->get_name () != nullptr
703 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
704 {
705 /* If this layout has never been applied, then it means the
706 user just changed the layout. In this situation, it's
707 desirable to keep the size of the command window the
7c043ba6 708 same. Setting the min and max sizes this way ensures
389e7ddb
TT
709 that the resizing step, below, does the right thing with
710 this window. */
7c043ba6
TT
711 info[i].min_size = (m_vertical
712 ? TUI_CMD_WIN->height
713 : TUI_CMD_WIN->width);
714 info[i].max_size = info[i].min_size;
389e7ddb
TT
715 }
716
7c043ba6
TT
717 if (info[i].min_size == info[i].max_size)
718 available_size -= info[i].min_size;
389e7ddb
TT
719 else
720 {
721 last_index = i;
722 total_weight += m_splits[i].weight;
723 }
724
725 /* Two adjacent boxed windows will share a border, making a bit
7c043ba6 726 more size available. */
389e7ddb
TT
727 if (i > 0
728 && m_splits[i - 1].layout->bottom_boxed_p ()
729 && m_splits[i].layout->top_boxed_p ())
730 info[i].share_box = true;
731 }
732
7c043ba6 733 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
389e7ddb
TT
734 are given their fixed size, while others are resized according to
735 their weight. */
7c043ba6 736 int used_size = 0;
389e7ddb
TT
737 for (int i = 0; i < m_splits.size (); ++i)
738 {
739 /* Compute the height and clamp to the allowable range. */
7c043ba6
TT
740 info[i].size = available_size * m_splits[i].weight / total_weight;
741 if (info[i].size > info[i].max_size)
742 info[i].size = info[i].max_size;
743 if (info[i].size < info[i].min_size)
744 info[i].size = info[i].min_size;
745 /* If there is any leftover size, just redistribute it to the
389e7ddb 746 last resizeable window, by dropping it from the allocated
7c043ba6
TT
747 size. We could try to be fancier here perhaps, by
748 redistributing this size among all windows, not just the
389e7ddb 749 last window. */
7c043ba6 750 if (info[i].min_size != info[i].max_size)
389e7ddb 751 {
7c043ba6 752 used_size += info[i].size;
389e7ddb 753 if (info[i].share_box)
7c043ba6 754 --used_size;
389e7ddb
TT
755 }
756 }
757
7c043ba6
TT
758 /* Allocate any leftover size. */
759 if (available_size >= used_size && last_index != -1)
760 info[last_index].size += available_size - used_size;
389e7ddb
TT
761
762 /* Step 3: Resize. */
7c043ba6
TT
763 int size_accum = 0;
764 const int maximum = m_vertical ? height : width;
389e7ddb
TT
765 for (int i = 0; i < m_splits.size (); ++i)
766 {
767 /* If we fall off the bottom, just make allocations overlap.
768 GIGO. */
7c043ba6
TT
769 if (size_accum + info[i].size > maximum)
770 size_accum = maximum - info[i].size;
389e7ddb 771 else if (info[i].share_box)
7c043ba6
TT
772 --size_accum;
773 if (m_vertical)
774 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
775 else
776 m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
777 size_accum += info[i].size;
389e7ddb
TT
778 }
779
780 m_applied = true;
781}
782
5afe342e
TT
783/* See tui-layout.h. */
784
785void
786tui_layout_split::remove_windows (const char *name)
787{
788 for (int i = 0; i < m_splits.size (); ++i)
789 {
790 const char *this_name = m_splits[i].layout->get_name ();
791 if (this_name == nullptr)
792 m_splits[i].layout->remove_windows (name);
39ec0490 793 else if (strcmp (this_name, name) == 0
de543742
TT
794 || strcmp (this_name, CMD_NAME) == 0
795 || strcmp (this_name, STATUS_NAME) == 0)
39ec0490
TT
796 {
797 /* Keep. */
798 }
5afe342e
TT
799 else
800 {
5afe342e
TT
801 m_splits.erase (m_splits.begin () + i);
802 --i;
803 }
804 }
805}
806
416eb92d
TT
807/* See tui-layout.h. */
808
809void
810tui_layout_split::replace_window (const char *name, const char *new_window)
811{
812 for (auto &item : m_splits)
813 item.layout->replace_window (name, new_window);
814}
815
ee325b61
TT
816/* See tui-layout.h. */
817
818void
c22fef7e 819tui_layout_split::specification (ui_file *output, int depth)
ee325b61 820{
c22fef7e
TT
821 if (depth > 0)
822 fputs_unfiltered ("{", output);
823
7c043ba6
TT
824 if (!m_vertical)
825 fputs_unfiltered ("-horizontal ", output);
826
ee325b61
TT
827 bool first = true;
828 for (auto &item : m_splits)
829 {
830 if (!first)
831 fputs_unfiltered (" ", output);
832 first = false;
c22fef7e 833 item.layout->specification (output, depth + 1);
ee325b61
TT
834 fprintf_unfiltered (output, " %d", item.weight);
835 }
c22fef7e
TT
836
837 if (depth > 0)
838 fputs_unfiltered ("}", output);
ee325b61
TT
839}
840
416eb92d
TT
841/* Destroy the layout associated with SELF. */
842
2192a9d3 843static void
416eb92d
TT
844destroy_layout (struct cmd_list_element *self, void *context)
845{
846 tui_layout_split *layout = (tui_layout_split *) context;
847 size_t index = find_layout (layout);
848 layouts.erase (layouts.begin () + index);
849}
850
851/* List holding the sub-commands of "layout". */
852
853static struct cmd_list_element *layout_list;
854
855/* Add a "layout" command with name NAME that switches to LAYOUT. */
856
ee325b61 857static struct cmd_list_element *
416eb92d 858add_layout_command (const char *name, tui_layout_split *layout)
2192a9d3 859{
416eb92d 860 struct cmd_list_element *cmd;
2192a9d3 861
ee325b61 862 string_file spec;
c22fef7e 863 layout->specification (&spec, 0);
ee325b61
TT
864
865 gdb::unique_xmalloc_ptr<char> doc
866 (xstrprintf (_("Apply the \"%s\" layout.\n\
867This layout was created using:\n\
868 tui new-layout %s %s"),
869 name, name, spec.c_str ()));
2192a9d3 870
416eb92d
TT
871 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
872 set_cmd_context (cmd, layout);
873 /* There is no API to set this. */
874 cmd->func = tui_apply_layout;
875 cmd->destroyer = destroy_layout;
876 cmd->doc_allocated = 1;
877 doc.release ();
878 layouts.emplace_back (layout);
ee325b61
TT
879
880 return cmd;
416eb92d 881}
2192a9d3 882
416eb92d 883/* Initialize the standard layouts. */
2192a9d3 884
416eb92d
TT
885static void
886initialize_layouts ()
887{
888 tui_layout_split *layout;
889
890 layout = new tui_layout_split ();
de543742
TT
891 layout->add_window (SRC_NAME, 2);
892 layout->add_window (STATUS_NAME, 0);
893 layout->add_window (CMD_NAME, 1);
894 add_layout_command (SRC_NAME, layout);
416eb92d
TT
895
896 layout = new tui_layout_split ();
de543742
TT
897 layout->add_window (DISASSEM_NAME, 2);
898 layout->add_window (STATUS_NAME, 0);
899 layout->add_window (CMD_NAME, 1);
900 add_layout_command (DISASSEM_NAME, layout);
416eb92d
TT
901
902 layout = new tui_layout_split ();
de543742
TT
903 layout->add_window (SRC_NAME, 1);
904 layout->add_window (DISASSEM_NAME, 1);
905 layout->add_window (STATUS_NAME, 0);
906 layout->add_window (CMD_NAME, 1);
416eb92d
TT
907 add_layout_command ("split", layout);
908
909 layout = new tui_layout_split ();
de543742
TT
910 layout->add_window (DATA_NAME, 1);
911 layout->add_window (SRC_NAME, 1);
912 layout->add_window (STATUS_NAME, 0);
913 layout->add_window (CMD_NAME, 1);
416eb92d
TT
914 layouts.emplace_back (layout);
915 src_regs_layout = layout;
916
917 layout = new tui_layout_split ();
de543742
TT
918 layout->add_window (DATA_NAME, 1);
919 layout->add_window (DISASSEM_NAME, 1);
920 layout->add_window (STATUS_NAME, 0);
921 layout->add_window (CMD_NAME, 1);
416eb92d
TT
922 layouts.emplace_back (layout);
923 asm_regs_layout = layout;
2192a9d3
TT
924}
925
389e7ddb
TT
926\f
927
ee325b61
TT
928/* A helper function that returns true if NAME is the name of an
929 available window. */
930
931static bool
932validate_window_name (const std::string &name)
933{
0240c8f1
TT
934 auto iter = known_window_types->find (name);
935 return iter != known_window_types->end ();
ee325b61
TT
936}
937
938/* Implementation of the "tui new-layout" command. */
939
940static void
941tui_new_layout_command (const char *spec, int from_tty)
942{
943 std::string new_name = extract_arg (&spec);
944 if (new_name.empty ())
945 error (_("No layout name specified"));
946 if (new_name[0] == '-')
947 error (_("Layout name cannot start with '-'"));
948
7c043ba6
TT
949 bool is_vertical = true;
950 spec = skip_spaces (spec);
951 if (check_for_argument (&spec, "-horizontal"))
952 is_vertical = false;
953
c22fef7e 954 std::vector<std::unique_ptr<tui_layout_split>> splits;
7c043ba6 955 splits.emplace_back (new tui_layout_split (is_vertical));
ee325b61
TT
956 std::unordered_set<std::string> seen_windows;
957 while (true)
958 {
c22fef7e
TT
959 spec = skip_spaces (spec);
960 if (spec[0] == '\0')
ee325b61 961 break;
c22fef7e
TT
962
963 if (spec[0] == '{')
964 {
7c043ba6
TT
965 is_vertical = true;
966 spec = skip_spaces (spec + 1);
967 if (check_for_argument (&spec, "-horizontal"))
968 is_vertical = false;
969 splits.emplace_back (new tui_layout_split (is_vertical));
c22fef7e
TT
970 continue;
971 }
972
973 bool is_close = false;
974 std::string name;
975 if (spec[0] == '}')
976 {
977 is_close = true;
978 ++spec;
979 if (splits.size () == 1)
980 error (_("Extra '}' in layout specification"));
981 }
982 else
983 {
984 name = extract_arg (&spec);
985 if (name.empty ())
986 break;
987 if (!validate_window_name (name))
988 error (_("Unknown window \"%s\""), name.c_str ());
989 if (seen_windows.find (name) != seen_windows.end ())
990 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
991 }
992
993 ULONGEST weight = get_ulongest (&spec, '}');
ee325b61
TT
994 if ((int) weight != weight)
995 error (_("Weight out of range: %s"), pulongest (weight));
c22fef7e
TT
996 if (is_close)
997 {
998 std::unique_ptr<tui_layout_split> last_split
999 = std::move (splits.back ());
1000 splits.pop_back ();
1001 splits.back ()->add_split (std::move (last_split), weight);
1002 }
1003 else
1004 {
1005 splits.back ()->add_window (name.c_str (), weight);
1006 seen_windows.insert (name);
1007 }
ee325b61 1008 }
c22fef7e
TT
1009 if (splits.size () > 1)
1010 error (_("Missing '}' in layout specification"));
ee325b61
TT
1011 if (seen_windows.empty ())
1012 error (_("New layout does not contain any windows"));
de543742
TT
1013 if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1014 error (_("New layout does not contain the \"" CMD_NAME "\" window"));
ee325b61
TT
1015
1016 gdb::unique_xmalloc_ptr<char> cmd_name
1017 = make_unique_xstrdup (new_name.c_str ());
c22fef7e 1018 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
ee325b61
TT
1019 struct cmd_list_element *cmd
1020 = add_layout_command (cmd_name.get (), new_layout.get ());
1021 cmd->name_allocated = 1;
1022 cmd_name.release ();
1023 new_layout.release ();
1024}
1025
d9fcefd5
TT
1026/* Function to initialize gdb commands, for tui window layout
1027 manipulation. */
1028
6c265988 1029void _initialize_tui_layout ();
d9fcefd5 1030void
6c265988 1031_initialize_tui_layout ()
d9fcefd5 1032{
0743fc83 1033 add_basic_prefix_cmd ("layout", class_tui, _("\
d9fcefd5 1034Change the layout of windows.\n\
416eb92d 1035Usage: layout prev | next | LAYOUT-NAME"),
0743fc83 1036 &layout_list, "layout ", 0, &cmdlist);
416eb92d
TT
1037
1038 add_cmd ("next", class_tui, tui_next_layout_command,
c9af6521 1039 _("Apply the next TUI layout."),
416eb92d
TT
1040 &layout_list);
1041 add_cmd ("prev", class_tui, tui_prev_layout_command,
c9af6521 1042 _("Apply the previous TUI layout."),
416eb92d
TT
1043 &layout_list);
1044 add_cmd ("regs", class_tui, tui_regs_layout_command,
c9af6521 1045 _("Apply the TUI register layout."),
416eb92d 1046 &layout_list);
2192a9d3 1047
ee325b61
TT
1048 add_cmd ("new-layout", class_tui, tui_new_layout_command,
1049 _("Create a new TUI layout.\n\
7c043ba6 1050Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
ee325b61
TT
1051Create a new TUI layout. The new layout will be named NAME,\n\
1052and can be accessed using \"layout NAME\".\n\
1053The windows will be displayed in the specified order.\n\
c22fef7e 1054A WINDOW can also be of the form:\n\
7c043ba6 1055 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
c22fef7e 1056This form indicates a sub-frame.\n\
ee325b61
TT
1057Each WEIGHT is an integer, which holds the relative size\n\
1058to be allocated to the window."),
1059 tui_get_cmd_list ());
1060
2192a9d3 1061 initialize_layouts ();
0240c8f1 1062 initialize_known_windows ();
d9fcefd5 1063}
This page took 2.362202 seconds and 4 git commands to generate.