Remove the TUI annotation hack
[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
2192a9d3
TT
68/* See tui-layout.h. */
69
70void
71tui_apply_current_layout ()
72{
865a5aec
TT
73 struct gdbarch *gdbarch;
74 CORE_ADDR addr;
75
76 extract_display_start_addr (&gdbarch, &addr);
77
78 std::vector<tui_win_info *> saved_windows = std::move (tui_windows);
7eed1a8e 79 tui_windows.clear ();
865a5aec
TT
80
81 for (tui_win_info *win_info : saved_windows)
82 win_info->make_visible (false);
83
2192a9d3 84 applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
865a5aec
TT
85
86 /* Keep the list of internal windows up-to-date. */
87 for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
88 if (tui_win_list[win_type] != nullptr
89 && !tui_win_list[win_type]->is_visible ())
90 tui_win_list[win_type] = nullptr;
91
92 /* This should always be made visible by a layout. */
93 gdb_assert (TUI_CMD_WIN->is_visible ());
94
95 /* Now delete any window that was not re-applied. */
96 tui_win_info *focus = tui_win_with_focus ();
97 for (tui_win_info *win_info : saved_windows)
98 {
99 if (!win_info->is_visible ())
100 {
101 if (focus == win_info)
102 tui_set_win_focus_to (tui_windows[0]);
103 delete win_info;
104 }
105 }
106
107 if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
108 tui_get_begin_asm_address (&gdbarch, &addr);
109 tui_update_source_windows_with_addr (gdbarch, addr);
2192a9d3 110}
c906108c 111
d4eeccfe
TT
112/* See tui-layout. */
113
114void
115tui_adjust_window_height (struct tui_win_info *win, int new_height)
116{
117 applied_layout->adjust_size (win->name (), new_height);
118}
119
416eb92d 120/* Set the current layout to LAYOUT. */
c906108c 121
416eb92d
TT
122static void
123tui_set_layout (tui_layout_split *layout)
c906108c 124{
416eb92d
TT
125 applied_skeleton = layout;
126 applied_layout = layout->clone ();
127 tui_apply_current_layout ();
bc712bbf 128}
c906108c 129
59b8b5d2
TT
130/* See tui-layout.h. */
131
c906108c 132void
080ce8c0 133tui_add_win_to_layout (enum tui_win_type type)
c906108c 134{
59b8b5d2
TT
135 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
136
416eb92d
TT
137 /* If the window already exists, no need to add it. */
138 if (tui_win_list[type] != nullptr)
139 return;
140
141 /* If the window we are trying to replace doesn't exist, we're
142 done. */
143 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
144 if (tui_win_list[other] == nullptr)
145 return;
146
147 const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
148 applied_layout->replace_window (tui_win_list[other]->name (), name);
149 tui_apply_current_layout ();
416eb92d
TT
150}
151
152/* Find LAYOUT in the "layouts" global and return its index. */
c906108c 153
416eb92d
TT
154static size_t
155find_layout (tui_layout_split *layout)
156{
157 for (size_t i = 0; i < layouts.size (); ++i)
c906108c 158 {
416eb92d
TT
159 if (layout == layouts[i].get ())
160 return i;
c906108c 161 }
416eb92d 162 gdb_assert_not_reached (_("layout not found!?"));
6ba8e26f 163}
c906108c 164
416eb92d 165/* Function to set the layout. */
a0145030 166
eb3ff9a5 167static void
416eb92d
TT
168tui_apply_layout (struct cmd_list_element *command,
169 const char *args, int from_tty)
a0145030 170{
416eb92d
TT
171 tui_layout_split *layout
172 = (tui_layout_split *) get_cmd_context (command);
a0145030 173
416eb92d
TT
174 /* Make sure the curses mode is enabled. */
175 tui_enable ();
176 tui_set_layout (layout);
a0145030
AB
177}
178
416eb92d 179/* See tui-layout.h. */
c906108c 180
416eb92d
TT
181void
182tui_next_layout ()
183{
184 size_t index = find_layout (applied_skeleton);
185 ++index;
186 if (index == layouts.size ())
187 index = 0;
188 tui_set_layout (layouts[index].get ());
189}
c906108c 190
416eb92d 191/* Implement the "layout next" command. */
c906108c 192
416eb92d
TT
193static void
194tui_next_layout_command (const char *arg, int from_tty)
195{
0379b883 196 tui_enable ();
416eb92d 197 tui_next_layout ();
e8b915dc 198}
c906108c 199
427326a8
TT
200/* See tui-layout.h. */
201
202void
416eb92d
TT
203tui_set_initial_layout ()
204{
205 tui_set_layout (layouts[0].get ());
206}
207
208/* Implement the "layout prev" command. */
209
210static void
211tui_prev_layout_command (const char *arg, int from_tty)
427326a8 212{
416eb92d
TT
213 tui_enable ();
214 size_t index = find_layout (applied_skeleton);
215 if (index == 0)
216 index = layouts.size ();
217 --index;
218 tui_set_layout (layouts[index].get ());
427326a8 219}
c906108c 220
416eb92d 221
5afe342e
TT
222/* See tui-layout.h. */
223
0dbc2fc7
TT
224void
225tui_regs_layout ()
226{
416eb92d
TT
227 /* If there's already a register window, we're done. */
228 if (TUI_DATA_WIN != nullptr)
229 return;
230
231 tui_set_layout (TUI_DISASM_WIN != nullptr
232 ? asm_regs_layout
233 : src_regs_layout);
234}
235
236/* Implement the "layout regs" command. */
237
238static void
239tui_regs_layout_command (const char *arg, int from_tty)
240{
241 tui_enable ();
242 tui_regs_layout ();
0dbc2fc7
TT
243}
244
245/* See tui-layout.h. */
246
5afe342e
TT
247void
248tui_remove_some_windows ()
249{
250 tui_win_info *focus = tui_win_with_focus ();
251
252 if (strcmp (focus->name (), "cmd") == 0)
253 {
254 /* Try leaving the source or disassembly window. If neither
255 exists, just do nothing. */
256 focus = TUI_SRC_WIN;
257 if (focus == nullptr)
258 focus = TUI_DISASM_WIN;
259 if (focus == nullptr)
260 return;
261 }
262
263 applied_layout->remove_windows (focus->name ());
264 tui_apply_current_layout ();
265}
266
13274fc3
UW
267static void
268extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
c906108c 269{
416eb92d
TT
270 struct gdbarch *gdbarch = nullptr;
271 CORE_ADDR addr = 0;
84b1e7c7 272 CORE_ADDR pc;
52575520 273 struct symtab_and_line cursal = get_current_source_symtab_and_line ();
c906108c 274
416eb92d 275 if (TUI_SRC_WIN != nullptr)
c906108c 276 {
e6e41501 277 gdbarch = TUI_SRC_WIN->gdbarch;
52575520 278 find_line_pc (cursal.symtab,
e6e41501 279 TUI_SRC_WIN->start_line_or_addr.u.line_no,
84b1e7c7 280 &pc);
c774cec6 281 addr = pc;
416eb92d
TT
282 }
283 else if (TUI_DISASM_WIN != nullptr)
284 {
e6e41501
TT
285 gdbarch = TUI_DISASM_WIN->gdbarch;
286 addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
c906108c
SS
287 }
288
13274fc3
UW
289 *gdbarch_p = gdbarch;
290 *addr_p = addr;
6ba8e26f 291}
c906108c 292
d6ba6a11 293void
ee556432
TT
294tui_gen_win_info::resize (int height_, int width_,
295 int origin_x_, int origin_y_)
c906108c 296{
cdaa6eb4 297 if (width == width_ && height == height_
fb3184d8 298 && x == origin_x_ && y == origin_y_
cdaa6eb4
TT
299 && handle != nullptr)
300 return;
301
d6ba6a11 302 width = width_;
db502012 303 height = height_;
fb3184d8
TT
304 x = origin_x_;
305 y = origin_y_;
db502012
TT
306
307 if (handle != nullptr)
308 {
309#ifdef HAVE_WRESIZE
7523da63 310 wresize (handle.get (), height, width);
fb3184d8 311 mvwin (handle.get (), y, x);
7523da63 312 wmove (handle.get (), 0, 0);
db502012 313#else
7523da63 314 handle.reset (nullptr);
db502012
TT
315#endif
316 }
317
318 if (handle == nullptr)
ab0e1f1a 319 make_window ();
3df505f6
TT
320
321 rerender ();
d6ba6a11 322}
c906108c 323
d9fcefd5
TT
324\f
325
0240c8f1
TT
326/* Helper function to create one of the built-in (non-locator)
327 windows. */
328
329template<enum tui_win_type V, class T>
330static tui_gen_win_info *
331make_standard_window (const char *)
332{
333 if (tui_win_list[V] == nullptr)
334 tui_win_list[V] = new T ();
335 return tui_win_list[V];
336}
337
338/* Helper function to wrap tui_locator_win_info_ptr for
339 tui_get_window_by_name. */
340
341static tui_gen_win_info *
342get_locator_window (const char *)
343{
344 return tui_locator_win_info_ptr ();
345}
346
347/* A map holding all the known window types, keyed by name. Note that
348 this is heap-allocated and "leaked" at gdb exit. This avoids
349 ordering issues with destroying elements in the map at shutdown.
350 In particular, destroying this map can occur after Python has been
351 shut down, causing crashes if any window destruction requires
352 running Python code. */
353
354static std::unordered_map<std::string, window_factory> *known_window_types;
355
389e7ddb
TT
356/* Helper function that returns a TUI window, given its name. */
357
358static tui_gen_win_info *
359tui_get_window_by_name (const std::string &name)
360{
0240c8f1
TT
361 for (tui_win_info *window : saved_tui_windows)
362 if (name == window->name ())
363 return window;
364
365 auto iter = known_window_types->find (name);
366 if (iter == known_window_types->end ())
367 error (_("Unknown window type \"%s\""), name.c_str ());
368
369 tui_gen_win_info *result = iter->second (name.c_str ());
370 if (result == nullptr)
371 error (_("Could not create window \"%s\""), name.c_str ());
372 return result;
373}
374
375/* Initialize the known window types. */
376
377static void
378initialize_known_windows ()
379{
380 known_window_types = new std::unordered_map<std::string, window_factory>;
381
382 known_window_types->emplace ("src",
383 make_standard_window<SRC_WIN,
384 tui_source_window>);
385 known_window_types->emplace ("cmd",
386 make_standard_window<CMD_WIN, tui_cmd_window>);
387 known_window_types->emplace ("regs",
388 make_standard_window<DATA_WIN,
389 tui_data_window>);
390 known_window_types->emplace ("asm",
391 make_standard_window<DISASSEM_WIN,
392 tui_disasm_window>);
393 known_window_types->emplace ("status", get_locator_window);
389e7ddb
TT
394}
395
396/* See tui-layout.h. */
397
398std::unique_ptr<tui_layout_base>
399tui_layout_window::clone () const
400{
401 tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
402 return std::unique_ptr<tui_layout_base> (result);
403}
404
405/* See tui-layout.h. */
406
407void
408tui_layout_window::apply (int x_, int y_, int width_, int height_)
409{
410 x = x_;
411 y = y_;
412 width = width_;
413 height = height_;
414 gdb_assert (m_window != nullptr);
415 m_window->resize (height, width, x, y);
7eed1a8e
TT
416 if (dynamic_cast<tui_win_info *> (m_window) != nullptr)
417 tui_windows.push_back ((tui_win_info *) 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);
784 else
785 {
786 if (strcmp (this_name, name) == 0
787 || strcmp (this_name, "cmd") == 0)
788 {
789 /* Keep. */
790 }
791 m_splits.erase (m_splits.begin () + i);
792 --i;
793 }
794 }
795}
796
416eb92d
TT
797/* See tui-layout.h. */
798
799void
800tui_layout_split::replace_window (const char *name, const char *new_window)
801{
802 for (auto &item : m_splits)
803 item.layout->replace_window (name, new_window);
804}
805
ee325b61
TT
806/* See tui-layout.h. */
807
808void
c22fef7e 809tui_layout_split::specification (ui_file *output, int depth)
ee325b61 810{
c22fef7e
TT
811 if (depth > 0)
812 fputs_unfiltered ("{", output);
813
7c043ba6
TT
814 if (!m_vertical)
815 fputs_unfiltered ("-horizontal ", output);
816
ee325b61
TT
817 bool first = true;
818 for (auto &item : m_splits)
819 {
820 if (!first)
821 fputs_unfiltered (" ", output);
822 first = false;
c22fef7e 823 item.layout->specification (output, depth + 1);
ee325b61
TT
824 fprintf_unfiltered (output, " %d", item.weight);
825 }
c22fef7e
TT
826
827 if (depth > 0)
828 fputs_unfiltered ("}", output);
ee325b61
TT
829}
830
416eb92d
TT
831/* Destroy the layout associated with SELF. */
832
2192a9d3 833static void
416eb92d
TT
834destroy_layout (struct cmd_list_element *self, void *context)
835{
836 tui_layout_split *layout = (tui_layout_split *) context;
837 size_t index = find_layout (layout);
838 layouts.erase (layouts.begin () + index);
839}
840
841/* List holding the sub-commands of "layout". */
842
843static struct cmd_list_element *layout_list;
844
845/* Add a "layout" command with name NAME that switches to LAYOUT. */
846
ee325b61 847static struct cmd_list_element *
416eb92d 848add_layout_command (const char *name, tui_layout_split *layout)
2192a9d3 849{
416eb92d 850 struct cmd_list_element *cmd;
2192a9d3 851
ee325b61 852 string_file spec;
c22fef7e 853 layout->specification (&spec, 0);
ee325b61
TT
854
855 gdb::unique_xmalloc_ptr<char> doc
856 (xstrprintf (_("Apply the \"%s\" layout.\n\
857This layout was created using:\n\
858 tui new-layout %s %s"),
859 name, name, spec.c_str ()));
2192a9d3 860
416eb92d
TT
861 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
862 set_cmd_context (cmd, layout);
863 /* There is no API to set this. */
864 cmd->func = tui_apply_layout;
865 cmd->destroyer = destroy_layout;
866 cmd->doc_allocated = 1;
867 doc.release ();
868 layouts.emplace_back (layout);
ee325b61
TT
869
870 return cmd;
416eb92d 871}
2192a9d3 872
416eb92d 873/* Initialize the standard layouts. */
2192a9d3 874
416eb92d
TT
875static void
876initialize_layouts ()
877{
878 tui_layout_split *layout;
879
880 layout = new tui_layout_split ();
881 layout->add_window ("src", 2);
882 layout->add_window ("status", 0);
883 layout->add_window ("cmd", 1);
884 add_layout_command ("src", layout);
885
886 layout = new tui_layout_split ();
887 layout->add_window ("asm", 2);
888 layout->add_window ("status", 0);
889 layout->add_window ("cmd", 1);
890 add_layout_command ("asm", layout);
891
892 layout = new tui_layout_split ();
893 layout->add_window ("src", 1);
894 layout->add_window ("asm", 1);
895 layout->add_window ("status", 0);
896 layout->add_window ("cmd", 1);
897 add_layout_command ("split", layout);
898
899 layout = new tui_layout_split ();
900 layout->add_window ("regs", 1);
901 layout->add_window ("src", 1);
902 layout->add_window ("status", 0);
903 layout->add_window ("cmd", 1);
904 layouts.emplace_back (layout);
905 src_regs_layout = layout;
906
907 layout = new tui_layout_split ();
908 layout->add_window ("regs", 1);
909 layout->add_window ("asm", 1);
910 layout->add_window ("status", 0);
911 layout->add_window ("cmd", 1);
912 layouts.emplace_back (layout);
913 asm_regs_layout = layout;
2192a9d3
TT
914}
915
389e7ddb
TT
916\f
917
ee325b61
TT
918/* A helper function that returns true if NAME is the name of an
919 available window. */
920
921static bool
922validate_window_name (const std::string &name)
923{
0240c8f1
TT
924 auto iter = known_window_types->find (name);
925 return iter != known_window_types->end ();
ee325b61
TT
926}
927
928/* Implementation of the "tui new-layout" command. */
929
930static void
931tui_new_layout_command (const char *spec, int from_tty)
932{
933 std::string new_name = extract_arg (&spec);
934 if (new_name.empty ())
935 error (_("No layout name specified"));
936 if (new_name[0] == '-')
937 error (_("Layout name cannot start with '-'"));
938
7c043ba6
TT
939 bool is_vertical = true;
940 spec = skip_spaces (spec);
941 if (check_for_argument (&spec, "-horizontal"))
942 is_vertical = false;
943
c22fef7e 944 std::vector<std::unique_ptr<tui_layout_split>> splits;
7c043ba6 945 splits.emplace_back (new tui_layout_split (is_vertical));
ee325b61
TT
946 std::unordered_set<std::string> seen_windows;
947 while (true)
948 {
c22fef7e
TT
949 spec = skip_spaces (spec);
950 if (spec[0] == '\0')
ee325b61 951 break;
c22fef7e
TT
952
953 if (spec[0] == '{')
954 {
7c043ba6
TT
955 is_vertical = true;
956 spec = skip_spaces (spec + 1);
957 if (check_for_argument (&spec, "-horizontal"))
958 is_vertical = false;
959 splits.emplace_back (new tui_layout_split (is_vertical));
c22fef7e
TT
960 continue;
961 }
962
963 bool is_close = false;
964 std::string name;
965 if (spec[0] == '}')
966 {
967 is_close = true;
968 ++spec;
969 if (splits.size () == 1)
970 error (_("Extra '}' in layout specification"));
971 }
972 else
973 {
974 name = extract_arg (&spec);
975 if (name.empty ())
976 break;
977 if (!validate_window_name (name))
978 error (_("Unknown window \"%s\""), name.c_str ());
979 if (seen_windows.find (name) != seen_windows.end ())
980 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
981 }
982
983 ULONGEST weight = get_ulongest (&spec, '}');
ee325b61
TT
984 if ((int) weight != weight)
985 error (_("Weight out of range: %s"), pulongest (weight));
c22fef7e
TT
986 if (is_close)
987 {
988 std::unique_ptr<tui_layout_split> last_split
989 = std::move (splits.back ());
990 splits.pop_back ();
991 splits.back ()->add_split (std::move (last_split), weight);
992 }
993 else
994 {
995 splits.back ()->add_window (name.c_str (), weight);
996 seen_windows.insert (name);
997 }
ee325b61 998 }
c22fef7e
TT
999 if (splits.size () > 1)
1000 error (_("Missing '}' in layout specification"));
ee325b61
TT
1001 if (seen_windows.empty ())
1002 error (_("New layout does not contain any windows"));
1003 if (seen_windows.find ("cmd") == seen_windows.end ())
1004 error (_("New layout does not contain the \"cmd\" window"));
1005
1006 gdb::unique_xmalloc_ptr<char> cmd_name
1007 = make_unique_xstrdup (new_name.c_str ());
c22fef7e 1008 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
ee325b61
TT
1009 struct cmd_list_element *cmd
1010 = add_layout_command (cmd_name.get (), new_layout.get ());
1011 cmd->name_allocated = 1;
1012 cmd_name.release ();
1013 new_layout.release ();
1014}
1015
416eb92d
TT
1016/* Base command for "layout". */
1017
1018static void
1019tui_layout_command (const char *layout_name, int from_tty)
1020{
1021 help_list (layout_list, "layout ", all_commands, gdb_stdout);
1022}
1023
d9fcefd5
TT
1024/* Function to initialize gdb commands, for tui window layout
1025 manipulation. */
1026
6c265988 1027void _initialize_tui_layout ();
d9fcefd5 1028void
6c265988 1029_initialize_tui_layout ()
d9fcefd5 1030{
416eb92d 1031 add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
d9fcefd5 1032Change the layout of windows.\n\
416eb92d
TT
1033Usage: layout prev | next | LAYOUT-NAME"),
1034 &layout_list, "layout ", 0, &cmdlist);
1035
1036 add_cmd ("next", class_tui, tui_next_layout_command,
1037 _("Apply the next TUI layout"),
1038 &layout_list);
1039 add_cmd ("prev", class_tui, tui_prev_layout_command,
1040 _("Apply the previous TUI layout"),
1041 &layout_list);
1042 add_cmd ("regs", class_tui, tui_regs_layout_command,
1043 _("Apply the TUI register layout"),
1044 &layout_list);
2192a9d3 1045
ee325b61
TT
1046 add_cmd ("new-layout", class_tui, tui_new_layout_command,
1047 _("Create a new TUI layout.\n\
7c043ba6 1048Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
ee325b61
TT
1049Create a new TUI layout. The new layout will be named NAME,\n\
1050and can be accessed using \"layout NAME\".\n\
1051The windows will be displayed in the specified order.\n\
c22fef7e 1052A WINDOW can also be of the form:\n\
7c043ba6 1053 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
c22fef7e 1054This form indicates a sub-frame.\n\
ee325b61
TT
1055Each WEIGHT is an integer, which holds the relative size\n\
1056to be allocated to the window."),
1057 tui_get_cmd_list ());
1058
2192a9d3 1059 initialize_layouts ();
0240c8f1 1060 initialize_known_windows ();
d9fcefd5 1061}
This page took 2.19202 seconds and 4 git commands to generate.