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