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