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