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