tui-layout.c revision 1.10 1 1.1 christos /* TUI layout window management.
2 1.1 christos
3 1.10 christos Copyright (C) 1998-2023 Free Software Foundation, Inc.
4 1.1 christos
5 1.1 christos Contributed by Hewlett-Packard Company.
6 1.1 christos
7 1.1 christos This file is part of GDB.
8 1.1 christos
9 1.1 christos This program is free software; you can redistribute it and/or modify
10 1.1 christos it under the terms of the GNU General Public License as published by
11 1.1 christos the Free Software Foundation; either version 3 of the License, or
12 1.1 christos (at your option) any later version.
13 1.1 christos
14 1.1 christos This program is distributed in the hope that it will be useful,
15 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
16 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 1.1 christos GNU General Public License for more details.
18 1.1 christos
19 1.1 christos You should have received a copy of the GNU General Public License
20 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 1.1 christos
22 1.1 christos #include "defs.h"
23 1.1 christos #include "arch-utils.h"
24 1.1 christos #include "command.h"
25 1.1 christos #include "symtab.h"
26 1.1 christos #include "frame.h"
27 1.1 christos #include "source.h"
28 1.9 christos #include "cli/cli-cmds.h"
29 1.9 christos #include "cli/cli-decode.h"
30 1.9 christos #include "cli/cli-utils.h"
31 1.1 christos #include <ctype.h>
32 1.9 christos #include <unordered_map>
33 1.9 christos #include <unordered_set>
34 1.1 christos
35 1.1 christos #include "tui/tui.h"
36 1.9 christos #include "tui/tui-command.h"
37 1.1 christos #include "tui/tui-data.h"
38 1.1 christos #include "tui/tui-wingeneral.h"
39 1.1 christos #include "tui/tui-stack.h"
40 1.1 christos #include "tui/tui-regs.h"
41 1.1 christos #include "tui/tui-win.h"
42 1.1 christos #include "tui/tui-winsource.h"
43 1.1 christos #include "tui/tui-disasm.h"
44 1.1 christos #include "tui/tui-layout.h"
45 1.9 christos #include "tui/tui-source.h"
46 1.1 christos #include "gdb_curses.h"
47 1.10 christos #include "safe-ctype.h"
48 1.1 christos
49 1.1 christos static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
50 1.1 christos
51 1.9 christos /* The layouts. */
52 1.9 christos static std::vector<std::unique_ptr<tui_layout_split>> layouts;
53 1.1 christos
54 1.9 christos /* The layout that is currently applied. */
55 1.9 christos static std::unique_ptr<tui_layout_base> applied_layout;
56 1.1 christos
57 1.9 christos /* The "skeleton" version of the layout that is currently applied. */
58 1.9 christos static tui_layout_split *applied_skeleton;
59 1.1 christos
60 1.9 christos /* The two special "regs" layouts. Note that these aren't registered
61 1.9 christos as commands and so can never be deleted. */
62 1.9 christos static tui_layout_split *src_regs_layout;
63 1.9 christos static tui_layout_split *asm_regs_layout;
64 1.9 christos
65 1.9 christos /* See tui-data.h. */
66 1.9 christos std::vector<tui_win_info *> tui_windows;
67 1.9 christos
68 1.9 christos /* See tui-layout.h. */
69 1.9 christos
70 1.9 christos void
71 1.10 christos tui_apply_current_layout (bool preserve_cmd_win_size_p)
72 1.1 christos {
73 1.9 christos struct gdbarch *gdbarch;
74 1.9 christos CORE_ADDR addr;
75 1.9 christos
76 1.9 christos extract_display_start_addr (&gdbarch, &addr);
77 1.9 christos
78 1.10 christos for (tui_win_info *win_info : tui_windows)
79 1.9 christos win_info->make_visible (false);
80 1.9 christos
81 1.10 christos applied_layout->apply (0, 0, tui_term_width (), tui_term_height (),
82 1.10 christos preserve_cmd_win_size_p);
83 1.9 christos
84 1.9 christos /* Keep the list of internal windows up-to-date. */
85 1.9 christos for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
86 1.9 christos if (tui_win_list[win_type] != nullptr
87 1.9 christos && !tui_win_list[win_type]->is_visible ())
88 1.9 christos tui_win_list[win_type] = nullptr;
89 1.9 christos
90 1.9 christos /* This should always be made visible by a layout. */
91 1.10 christos gdb_assert (TUI_CMD_WIN != nullptr);
92 1.9 christos gdb_assert (TUI_CMD_WIN->is_visible ());
93 1.9 christos
94 1.10 christos /* Get the new list of currently visible windows. */
95 1.10 christos std::vector<tui_win_info *> new_tui_windows;
96 1.10 christos applied_layout->get_windows (&new_tui_windows);
97 1.10 christos
98 1.9 christos /* Now delete any window that was not re-applied. */
99 1.9 christos tui_win_info *focus = tui_win_with_focus ();
100 1.10 christos for (tui_win_info *win_info : tui_windows)
101 1.1 christos {
102 1.9 christos if (!win_info->is_visible ())
103 1.1 christos {
104 1.9 christos if (focus == win_info)
105 1.10 christos tui_set_win_focus_to (new_tui_windows[0]);
106 1.9 christos delete win_info;
107 1.1 christos }
108 1.1 christos }
109 1.9 christos
110 1.10 christos /* Replace the global list of active windows. */
111 1.10 christos tui_windows = std::move (new_tui_windows);
112 1.10 christos
113 1.9 christos if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
114 1.9 christos tui_get_begin_asm_address (&gdbarch, &addr);
115 1.9 christos tui_update_source_windows_with_addr (gdbarch, addr);
116 1.10 christos }
117 1.10 christos
118 1.10 christos /* See tui-layout. */
119 1.9 christos
120 1.10 christos void
121 1.10 christos tui_adjust_window_height (struct tui_win_info *win, int new_height)
122 1.10 christos {
123 1.10 christos applied_layout->set_height (win->name (), new_height);
124 1.1 christos }
125 1.1 christos
126 1.9 christos /* See tui-layout. */
127 1.9 christos
128 1.9 christos void
129 1.10 christos tui_adjust_window_width (struct tui_win_info *win, int new_width)
130 1.9 christos {
131 1.10 christos applied_layout->set_width (win->name (), new_width);
132 1.9 christos }
133 1.1 christos
134 1.9 christos /* Set the current layout to LAYOUT. */
135 1.1 christos
136 1.9 christos static void
137 1.9 christos tui_set_layout (tui_layout_split *layout)
138 1.9 christos {
139 1.10 christos std::string old_fingerprint;
140 1.10 christos if (applied_layout != nullptr)
141 1.10 christos old_fingerprint = applied_layout->layout_fingerprint ();
142 1.10 christos
143 1.9 christos applied_skeleton = layout;
144 1.9 christos applied_layout = layout->clone ();
145 1.10 christos
146 1.10 christos std::string new_fingerprint = applied_layout->layout_fingerprint ();
147 1.10 christos bool preserve_command_window_size
148 1.10 christos = (TUI_CMD_WIN != nullptr && old_fingerprint == new_fingerprint);
149 1.10 christos
150 1.10 christos tui_apply_current_layout (preserve_command_window_size);
151 1.1 christos }
152 1.1 christos
153 1.9 christos /* See tui-layout.h. */
154 1.9 christos
155 1.1 christos void
156 1.1 christos tui_add_win_to_layout (enum tui_win_type type)
157 1.1 christos {
158 1.9 christos gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
159 1.9 christos
160 1.9 christos /* If the window already exists, no need to add it. */
161 1.9 christos if (tui_win_list[type] != nullptr)
162 1.9 christos return;
163 1.9 christos
164 1.9 christos /* If the window we are trying to replace doesn't exist, we're
165 1.9 christos done. */
166 1.9 christos enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
167 1.9 christos if (tui_win_list[other] == nullptr)
168 1.9 christos return;
169 1.9 christos
170 1.9 christos const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
171 1.9 christos applied_layout->replace_window (tui_win_list[other]->name (), name);
172 1.10 christos tui_apply_current_layout (true);
173 1.9 christos }
174 1.9 christos
175 1.9 christos /* Find LAYOUT in the "layouts" global and return its index. */
176 1.1 christos
177 1.9 christos static size_t
178 1.9 christos find_layout (tui_layout_split *layout)
179 1.9 christos {
180 1.9 christos for (size_t i = 0; i < layouts.size (); ++i)
181 1.1 christos {
182 1.9 christos if (layout == layouts[i].get ())
183 1.9 christos return i;
184 1.1 christos }
185 1.10 christos gdb_assert_not_reached ("layout not found!?");
186 1.1 christos }
187 1.1 christos
188 1.9 christos /* Function to set the layout. */
189 1.1 christos
190 1.9 christos static void
191 1.10 christos tui_apply_layout (const char *args, int from_tty, cmd_list_element *command)
192 1.1 christos {
193 1.10 christos tui_layout_split *layout = (tui_layout_split *) command->context ();
194 1.1 christos
195 1.9 christos /* Make sure the curses mode is enabled. */
196 1.9 christos tui_enable ();
197 1.9 christos tui_set_layout (layout);
198 1.9 christos }
199 1.9 christos
200 1.9 christos /* See tui-layout.h. */
201 1.1 christos
202 1.9 christos void
203 1.9 christos tui_next_layout ()
204 1.9 christos {
205 1.9 christos size_t index = find_layout (applied_skeleton);
206 1.9 christos ++index;
207 1.9 christos if (index == layouts.size ())
208 1.9 christos index = 0;
209 1.9 christos tui_set_layout (layouts[index].get ());
210 1.1 christos }
211 1.1 christos
212 1.9 christos /* Implement the "layout next" command. */
213 1.1 christos
214 1.9 christos static void
215 1.9 christos tui_next_layout_command (const char *arg, int from_tty)
216 1.1 christos {
217 1.9 christos tui_enable ();
218 1.9 christos tui_next_layout ();
219 1.9 christos }
220 1.1 christos
221 1.9 christos /* See tui-layout.h. */
222 1.1 christos
223 1.9 christos void
224 1.9 christos tui_set_initial_layout ()
225 1.9 christos {
226 1.9 christos tui_set_layout (layouts[0].get ());
227 1.1 christos }
228 1.1 christos
229 1.9 christos /* Implement the "layout prev" command. */
230 1.5 christos
231 1.8 christos static void
232 1.9 christos tui_prev_layout_command (const char *arg, int from_tty)
233 1.5 christos {
234 1.9 christos tui_enable ();
235 1.9 christos size_t index = find_layout (applied_skeleton);
236 1.9 christos if (index == 0)
237 1.9 christos index = layouts.size ();
238 1.9 christos --index;
239 1.9 christos tui_set_layout (layouts[index].get ());
240 1.9 christos }
241 1.5 christos
242 1.1 christos
243 1.9 christos /* See tui-layout.h. */
244 1.1 christos
245 1.1 christos void
246 1.9 christos tui_regs_layout ()
247 1.1 christos {
248 1.9 christos /* If there's already a register window, we're done. */
249 1.9 christos if (TUI_DATA_WIN != nullptr)
250 1.9 christos return;
251 1.5 christos
252 1.9 christos tui_set_layout (TUI_DISASM_WIN != nullptr
253 1.9 christos ? asm_regs_layout
254 1.9 christos : src_regs_layout);
255 1.1 christos }
256 1.1 christos
257 1.9 christos /* Implement the "layout regs" command. */
258 1.1 christos
259 1.9 christos static void
260 1.9 christos tui_regs_layout_command (const char *arg, int from_tty)
261 1.9 christos {
262 1.9 christos tui_enable ();
263 1.9 christos tui_regs_layout ();
264 1.9 christos }
265 1.1 christos
266 1.9 christos /* See tui-layout.h. */
267 1.1 christos
268 1.9 christos void
269 1.9 christos tui_remove_some_windows ()
270 1.1 christos {
271 1.9 christos tui_win_info *focus = tui_win_with_focus ();
272 1.1 christos
273 1.9 christos if (strcmp (focus->name (), CMD_NAME) == 0)
274 1.1 christos {
275 1.9 christos /* Try leaving the source or disassembly window. If neither
276 1.9 christos exists, just do nothing. */
277 1.9 christos focus = TUI_SRC_WIN;
278 1.9 christos if (focus == nullptr)
279 1.9 christos focus = TUI_DISASM_WIN;
280 1.9 christos if (focus == nullptr)
281 1.9 christos return;
282 1.1 christos }
283 1.1 christos
284 1.9 christos applied_layout->remove_windows (focus->name ());
285 1.10 christos tui_apply_current_layout (true);
286 1.1 christos }
287 1.1 christos
288 1.1 christos static void
289 1.1 christos extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
290 1.1 christos {
291 1.9 christos if (TUI_SRC_WIN != nullptr)
292 1.9 christos TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
293 1.9 christos else if (TUI_DISASM_WIN != nullptr)
294 1.9 christos TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
295 1.9 christos else
296 1.9 christos {
297 1.9 christos *gdbarch_p = nullptr;
298 1.9 christos *addr_p = 0;
299 1.9 christos }
300 1.9 christos }
301 1.9 christos
302 1.9 christos void
303 1.9 christos tui_win_info::resize (int height_, int width_,
304 1.9 christos int origin_x_, int origin_y_)
305 1.9 christos {
306 1.9 christos if (width == width_ && height == height_
307 1.9 christos && x == origin_x_ && y == origin_y_
308 1.9 christos && handle != nullptr)
309 1.9 christos return;
310 1.9 christos
311 1.9 christos width = width_;
312 1.9 christos height = height_;
313 1.9 christos x = origin_x_;
314 1.9 christos y = origin_y_;
315 1.1 christos
316 1.9 christos if (handle != nullptr)
317 1.1 christos {
318 1.9 christos #ifdef HAVE_WRESIZE
319 1.9 christos wresize (handle.get (), height, width);
320 1.9 christos mvwin (handle.get (), y, x);
321 1.9 christos wmove (handle.get (), 0, 0);
322 1.9 christos #else
323 1.9 christos handle.reset (nullptr);
324 1.9 christos #endif
325 1.1 christos }
326 1.1 christos
327 1.9 christos if (handle == nullptr)
328 1.9 christos make_window ();
329 1.9 christos
330 1.9 christos rerender ();
331 1.9 christos }
332 1.9 christos
333 1.9 christos
334 1.9 christos
336 1.9 christos /* Helper function to create one of the built-in (non-locator)
337 1.9 christos windows. */
338 1.9 christos
339 1.9 christos template<enum tui_win_type V, class T>
340 1.9 christos static tui_win_info *
341 1.9 christos make_standard_window (const char *)
342 1.9 christos {
343 1.9 christos if (tui_win_list[V] == nullptr)
344 1.9 christos tui_win_list[V] = new T ();
345 1.9 christos return tui_win_list[V];
346 1.9 christos }
347 1.9 christos
348 1.9 christos /* A map holding all the known window types, keyed by name. Note that
349 1.9 christos this is heap-allocated and "leaked" at gdb exit. This avoids
350 1.9 christos ordering issues with destroying elements in the map at shutdown.
351 1.9 christos In particular, destroying this map can occur after Python has been
352 1.9 christos shut down, causing crashes if any window destruction requires
353 1.9 christos running Python code. */
354 1.9 christos
355 1.9 christos static std::unordered_map<std::string, window_factory> *known_window_types;
356 1.9 christos
357 1.9 christos /* Helper function that returns a TUI window, given its name. */
358 1.9 christos
359 1.9 christos static tui_win_info *
360 1.9 christos tui_get_window_by_name (const std::string &name)
361 1.10 christos {
362 1.9 christos for (tui_win_info *window : tui_windows)
363 1.9 christos if (name == window->name ())
364 1.9 christos return window;
365 1.9 christos
366 1.9 christos auto iter = known_window_types->find (name);
367 1.9 christos if (iter == known_window_types->end ())
368 1.9 christos error (_("Unknown window type \"%s\""), name.c_str ());
369 1.9 christos
370 1.9 christos tui_win_info *result = iter->second (name.c_str ());
371 1.9 christos if (result == nullptr)
372 1.9 christos error (_("Could not create window \"%s\""), name.c_str ());
373 1.9 christos return result;
374 1.9 christos }
375 1.9 christos
376 1.1 christos /* Initialize the known window types. */
377 1.1 christos
378 1.9 christos static void
379 1.1 christos initialize_known_windows ()
380 1.9 christos {
381 1.9 christos known_window_types = new std::unordered_map<std::string, window_factory>;
382 1.9 christos
383 1.9 christos known_window_types->emplace (SRC_NAME,
384 1.9 christos make_standard_window<SRC_WIN,
385 1.9 christos tui_source_window>);
386 1.9 christos known_window_types->emplace (CMD_NAME,
387 1.9 christos make_standard_window<CMD_WIN, tui_cmd_window>);
388 1.9 christos known_window_types->emplace (DATA_NAME,
389 1.9 christos make_standard_window<DATA_WIN,
390 1.9 christos tui_data_window>);
391 1.9 christos known_window_types->emplace (DISASSEM_NAME,
392 1.9 christos make_standard_window<DISASSEM_WIN,
393 1.10 christos tui_disasm_window>);
394 1.10 christos known_window_types->emplace (STATUS_NAME,
395 1.10 christos make_standard_window<STATUS_WIN,
396 1.1 christos tui_locator_window>);
397 1.1 christos }
398 1.9 christos
399 1.9 christos /* See tui-layout.h. */
400 1.9 christos
401 1.9 christos void
402 1.1 christos tui_register_window (const char *name, window_factory &&factory)
403 1.9 christos {
404 1.9 christos std::string name_copy = name;
405 1.9 christos
406 1.9 christos if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
407 1.9 christos || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
408 1.9 christos error (_("Window type \"%s\" is built-in"), name);
409 1.10 christos
410 1.10 christos for (const char &c : name_copy)
411 1.10 christos {
412 1.10 christos if (ISSPACE (c))
413 1.10 christos error (_("invalid whitespace character in window name"));
414 1.10 christos
415 1.10 christos if (!ISALNUM (c) && strchr ("-_.", c) == nullptr)
416 1.10 christos error (_("invalid character '%c' in window name"), c);
417 1.10 christos }
418 1.10 christos
419 1.10 christos if (!ISALPHA (name_copy[0]))
420 1.10 christos error (_("window name must start with a letter, not '%c'"), name_copy[0]);
421 1.9 christos
422 1.9 christos known_window_types->emplace (std::move (name_copy),
423 1.9 christos std::move (factory));
424 1.1 christos }
425 1.9 christos
426 1.1 christos /* See tui-layout.h. */
427 1.9 christos
428 1.9 christos std::unique_ptr<tui_layout_base>
429 1.9 christos tui_layout_window::clone () const
430 1.9 christos {
431 1.9 christos tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
432 1.1 christos return std::unique_ptr<tui_layout_base> (result);
433 1.1 christos }
434 1.9 christos
435 1.1 christos /* See tui-layout.h. */
436 1.9 christos
437 1.10 christos void
438 1.10 christos tui_layout_window::apply (int x_, int y_, int width_, int height_,
439 1.1 christos bool preserve_cmd_win_size_p)
440 1.9 christos {
441 1.9 christos x = x_;
442 1.9 christos y = y_;
443 1.9 christos width = width_;
444 1.9 christos height = height_;
445 1.9 christos gdb_assert (m_window != nullptr);
446 1.9 christos m_window->resize (height, width, x, y);
447 1.1 christos }
448 1.9 christos
449 1.9 christos /* See tui-layout.h. */
450 1.9 christos
451 1.9 christos void
452 1.9 christos tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
453 1.10 christos {
454 1.10 christos TUI_SCOPED_DEBUG_ENTER_EXIT;
455 1.9 christos
456 1.9 christos if (m_window == nullptr)
457 1.10 christos m_window = tui_get_window_by_name (m_contents);
458 1.10 christos
459 1.10 christos tui_debug_printf ("window = %s, getting %s",
460 1.10 christos m_window->name (), (height ? "height" : "width"));
461 1.9 christos
462 1.9 christos if (height)
463 1.9 christos {
464 1.9 christos *min_value = m_window->min_height ();
465 1.9 christos *max_value = m_window->max_height ();
466 1.1 christos }
467 1.1 christos else
468 1.9 christos {
469 1.9 christos *min_value = m_window->min_width ();
470 1.1 christos *max_value = m_window->max_width ();
471 1.10 christos }
472 1.10 christos
473 1.9 christos tui_debug_printf ("min = %d, max = %d", *min_value, *max_value);
474 1.1 christos }
475 1.9 christos
476 1.9 christos /* See tui-layout.h. */
477 1.9 christos
478 1.10 christos bool
479 1.9 christos tui_layout_window::first_edge_has_border_p () const
480 1.9 christos {
481 1.9 christos gdb_assert (m_window != nullptr);
482 1.1 christos return m_window->can_box ();
483 1.1 christos }
484 1.9 christos
485 1.9 christos /* See tui-layout.h. */
486 1.9 christos
487 1.10 christos bool
488 1.9 christos tui_layout_window::last_edge_has_border_p () const
489 1.9 christos {
490 1.9 christos gdb_assert (m_window != nullptr);
491 1.9 christos return m_window->can_box ();
492 1.1 christos }
493 1.9 christos
494 1.1 christos /* See tui-layout.h. */
495 1.9 christos
496 1.9 christos void
497 1.1 christos tui_layout_window::replace_window (const char *name, const char *new_window)
498 1.9 christos {
499 1.9 christos if (m_contents == name)
500 1.9 christos {
501 1.9 christos m_contents = new_window;
502 1.9 christos if (m_window != nullptr)
503 1.9 christos {
504 1.9 christos m_window->make_visible (false);
505 1.9 christos m_window = tui_get_window_by_name (m_contents);
506 1.9 christos }
507 1.9 christos }
508 1.9 christos }
509 1.9 christos
510 1.1 christos /* See tui-layout.h. */
511 1.9 christos
512 1.9 christos void
513 1.9 christos tui_layout_window::specification (ui_file *output, int depth)
514 1.10 christos {
515 1.10 christos gdb_puts (get_name (), output);
516 1.10 christos }
517 1.10 christos
518 1.10 christos /* See tui-layout.h. */
519 1.10 christos
520 1.10 christos std::string
521 1.10 christos tui_layout_window::layout_fingerprint () const
522 1.10 christos {
523 1.10 christos if (strcmp (get_name (), "cmd") == 0)
524 1.10 christos return "C";
525 1.10 christos else
526 1.1 christos return "";
527 1.1 christos }
528 1.9 christos
529 1.1 christos /* See tui-layout.h. */
530 1.9 christos
531 1.9 christos void
532 1.9 christos tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
533 1.1 christos int weight)
534 1.9 christos {
535 1.9 christos split s = {weight, std::move (layout)};
536 1.9 christos m_splits.push_back (std::move (s));
537 1.9 christos }
538 1.9 christos
539 1.1 christos /* See tui-layout.h. */
540 1.9 christos
541 1.9 christos void
542 1.9 christos tui_layout_split::add_window (const char *name, int weight)
543 1.9 christos {
544 1.9 christos tui_layout_window *result = new tui_layout_window (name);
545 1.9 christos split s = {weight, std::unique_ptr<tui_layout_base> (result)};
546 1.9 christos m_splits.push_back (std::move (s));
547 1.1 christos }
548 1.9 christos
549 1.1 christos /* See tui-layout.h. */
550 1.9 christos
551 1.9 christos std::unique_ptr<tui_layout_base>
552 1.1 christos tui_layout_split::clone () const
553 1.9 christos {
554 1.9 christos tui_layout_split *result = new tui_layout_split (m_vertical);
555 1.9 christos for (const split &item : m_splits)
556 1.9 christos {
557 1.9 christos std::unique_ptr<tui_layout_base> next = item.layout->clone ();
558 1.9 christos split s = {item.weight, std::move (next)};
559 1.9 christos result->m_splits.push_back (std::move (s));
560 1.9 christos }
561 1.9 christos return std::unique_ptr<tui_layout_base> (result);
562 1.9 christos }
563 1.9 christos
564 1.1 christos /* See tui-layout.h. */
565 1.9 christos
566 1.9 christos void
567 1.9 christos tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
568 1.10 christos {
569 1.10 christos TUI_SCOPED_DEBUG_ENTER_EXIT;
570 1.9 christos
571 1.9 christos *min_value = 0;
572 1.9 christos *max_value = 0;
573 1.9 christos bool first_time = true;
574 1.9 christos for (const split &item : m_splits)
575 1.9 christos {
576 1.9 christos int new_min, new_max;
577 1.9 christos item.layout->get_sizes (height, &new_min, &new_max);
578 1.9 christos /* For the mismatch case, the first time through we want to set
579 1.9 christos the min and max to the computed values -- the "first_time"
580 1.9 christos check here is just a funny way of doing that. */
581 1.9 christos if (height == m_vertical || first_time)
582 1.9 christos {
583 1.9 christos *min_value += new_min;
584 1.9 christos *max_value += new_max;
585 1.9 christos }
586 1.9 christos else
587 1.9 christos {
588 1.9 christos *min_value = std::max (*min_value, new_min);
589 1.9 christos *max_value = std::min (*max_value, new_max);
590 1.9 christos }
591 1.9 christos first_time = false;
592 1.10 christos }
593 1.10 christos
594 1.9 christos tui_debug_printf ("min_value = %d, max_value = %d", *min_value, *max_value);
595 1.1 christos }
596 1.9 christos
597 1.1 christos /* See tui-layout.h. */
598 1.9 christos
599 1.10 christos bool
600 1.1 christos tui_layout_split::first_edge_has_border_p () const
601 1.9 christos {
602 1.9 christos if (m_splits.empty ())
603 1.10 christos return false;
604 1.1 christos return m_splits[0].layout->first_edge_has_border_p ();
605 1.1 christos }
606 1.9 christos
607 1.1 christos /* See tui-layout.h. */
608 1.9 christos
609 1.10 christos bool
610 1.1 christos tui_layout_split::last_edge_has_border_p () const
611 1.9 christos {
612 1.9 christos if (m_splits.empty ())
613 1.10 christos return false;
614 1.1 christos return m_splits.back ().layout->last_edge_has_border_p ();
615 1.1 christos }
616 1.9 christos
617 1.1 christos /* See tui-layout.h. */
618 1.9 christos
619 1.10 christos void
620 1.1 christos tui_layout_split::set_weights_from_sizes ()
621 1.9 christos {
622 1.10 christos for (int i = 0; i < m_splits.size (); ++i)
623 1.10 christos m_splits[i].weight
624 1.10 christos = m_vertical ? m_splits[i].layout->height : m_splits[i].layout->width;
625 1.10 christos }
626 1.10 christos
627 1.10 christos /* See tui-layout.h. */
628 1.10 christos
629 1.10 christos std::string
630 1.10 christos tui_layout_split::tui_debug_weights_to_string () const
631 1.10 christos {
632 1.10 christos std::string str;
633 1.10 christos
634 1.10 christos for (int i = 0; i < m_splits.size (); ++i)
635 1.10 christos {
636 1.10 christos if (i > 0)
637 1.10 christos str += ", ";
638 1.10 christos str += string_printf ("[%d] %d", i, m_splits[i].weight);
639 1.10 christos }
640 1.10 christos
641 1.10 christos return str;
642 1.10 christos }
643 1.10 christos
644 1.10 christos /* See tui-layout.h. */
645 1.10 christos
646 1.10 christos void
647 1.10 christos tui_layout_split::tui_debug_print_size_info
648 1.10 christos (const std::vector<tui_layout_split::size_info> &info)
649 1.10 christos {
650 1.10 christos gdb_assert (debug_tui);
651 1.10 christos
652 1.10 christos tui_debug_printf ("current size info data:");
653 1.10 christos for (int i = 0; i < info.size (); ++i)
654 1.10 christos tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
655 1.10 christos i, info[i].size, info[i].min_size,
656 1.1 christos info[i].max_size, info[i].share_box);
657 1.1 christos }
658 1.9 christos
659 1.1 christos /* See tui-layout.h. */
660 1.9 christos
661 1.10 christos tui_adjust_result
662 1.1 christos tui_layout_split::set_size (const char *name, int new_size, bool set_width_p)
663 1.10 christos {
664 1.10 christos TUI_SCOPED_DEBUG_ENTER_EXIT;
665 1.10 christos
666 1.10 christos tui_debug_printf ("this = %p, name = %s, new_size = %d",
667 1.10 christos this, name, new_size);
668 1.9 christos
669 1.9 christos /* Look through the children. If one is a layout holding the named
670 1.9 christos window, we're done; or if one actually is the named window,
671 1.9 christos update it. */
672 1.9 christos int found_index = -1;
673 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
674 1.10 christos {
675 1.10 christos tui_adjust_result adjusted;
676 1.10 christos if (set_width_p)
677 1.10 christos adjusted = m_splits[i].layout->set_width (name, new_size);
678 1.10 christos else
679 1.9 christos adjusted = m_splits[i].layout->set_height (name, new_size);
680 1.9 christos if (adjusted == HANDLED)
681 1.9 christos return HANDLED;
682 1.9 christos if (adjusted == FOUND)
683 1.10 christos {
684 1.9 christos if (set_width_p ? m_vertical : !m_vertical)
685 1.9 christos return FOUND;
686 1.9 christos found_index = i;
687 1.9 christos break;
688 1.9 christos }
689 1.1 christos }
690 1.9 christos
691 1.9 christos if (found_index == -1)
692 1.10 christos return NOT_FOUND;
693 1.10 christos int curr_size = (set_width_p
694 1.10 christos ? m_splits[found_index].layout->width
695 1.10 christos : m_splits[found_index].layout->height);
696 1.9 christos if (curr_size == new_size)
697 1.9 christos return HANDLED;
698 1.10 christos
699 1.10 christos tui_debug_printf ("found window %s at index %d", name, found_index);
700 1.10 christos
701 1.10 christos set_weights_from_sizes ();
702 1.10 christos int delta = m_splits[found_index].weight - new_size;
703 1.10 christos m_splits[found_index].weight = new_size;
704 1.10 christos
705 1.10 christos tui_debug_printf ("before delta (%d) distribution, weights: %s",
706 1.10 christos delta, tui_debug_weights_to_string ().c_str ());
707 1.10 christos
708 1.10 christos /* Distribute the "delta" over all other windows, while respecting their
709 1.10 christos min/max sizes. We grow each window by 1 line at a time continually
710 1.10 christos looping over all the windows. However, skip the window that the user
711 1.10 christos just resized, obviously we don't want to readjust that window. */
712 1.10 christos bool found_window_that_can_grow_p = true;
713 1.9 christos for (int i = 0; delta != 0; i = (i + 1) % m_splits.size ())
714 1.9 christos {
715 1.10 christos int index = (found_index + 1 + i) % m_splits.size ();
716 1.10 christos if (index == found_index)
717 1.10 christos {
718 1.10 christos if (!found_window_that_can_grow_p)
719 1.10 christos break;
720 1.10 christos found_window_that_can_grow_p = false;
721 1.10 christos continue;
722 1.1 christos }
723 1.9 christos
724 1.9 christos int new_min, new_max;
725 1.1 christos m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
726 1.9 christos
727 1.9 christos if (delta < 0)
728 1.9 christos {
729 1.9 christos /* The primary window grew, so we are trying to shrink other
730 1.10 christos windows. */
731 1.10 christos if (m_splits[index].weight > new_min)
732 1.10 christos {
733 1.10 christos m_splits[index].weight -= 1;
734 1.10 christos delta += 1;
735 1.10 christos found_window_that_can_grow_p = true;
736 1.9 christos }
737 1.1 christos }
738 1.1 christos else
739 1.9 christos {
740 1.9 christos /* The primary window shrank, so we are trying to grow other
741 1.10 christos windows. */
742 1.10 christos if (m_splits[index].weight < new_max)
743 1.10 christos {
744 1.10 christos m_splits[index].weight += 1;
745 1.10 christos delta -= 1;
746 1.10 christos found_window_that_can_grow_p = true;
747 1.1 christos }
748 1.10 christos }
749 1.10 christos
750 1.10 christos tui_debug_printf ("index = %d, weight now: %d",
751 1.1 christos index, m_splits[index].weight);
752 1.9 christos }
753 1.10 christos
754 1.10 christos tui_debug_printf ("after delta (%d) distribution, weights: %s",
755 1.10 christos delta, tui_debug_weights_to_string ().c_str ());
756 1.9 christos
757 1.9 christos if (delta != 0)
758 1.10 christos {
759 1.10 christos if (set_width_p)
760 1.10 christos warning (_("Invalid window width specified"));
761 1.10 christos else
762 1.9 christos warning (_("Invalid window height specified"));
763 1.10 christos /* Effectively undo any modifications made here. */
764 1.9 christos set_weights_from_sizes ();
765 1.9 christos }
766 1.9 christos else
767 1.10 christos {
768 1.10 christos /* Simply re-apply the updated layout. We pass false here so that
769 1.10 christos the cmd window can be resized. However, we should have already
770 1.10 christos resized everything above to be "just right", so the apply call
771 1.10 christos here should not end up changing the sizes at all. */
772 1.9 christos apply (x, y, width, height, false);
773 1.9 christos }
774 1.9 christos
775 1.1 christos return HANDLED;
776 1.1 christos }
777 1.9 christos
778 1.1 christos /* See tui-layout.h. */
779 1.9 christos
780 1.10 christos void
781 1.10 christos tui_layout_split::apply (int x_, int y_, int width_, int height_,
782 1.1 christos bool preserve_cmd_win_size_p)
783 1.10 christos {
784 1.10 christos TUI_SCOPED_DEBUG_ENTER_EXIT;
785 1.9 christos
786 1.9 christos x = x_;
787 1.9 christos y = y_;
788 1.9 christos width = width_;
789 1.9 christos height = height_;
790 1.10 christos
791 1.10 christos /* In some situations we fix the size of the cmd window. However,
792 1.10 christos occasionally this turns out to be a mistake. This struct is used to
793 1.10 christos hold the original information about the cmd window, so we can restore
794 1.10 christos it if needed. */
795 1.9 christos struct old_size_info
796 1.10 christos {
797 1.10 christos /* Constructor. */
798 1.10 christos old_size_info (int index_, int min_size_, int max_size_)
799 1.10 christos : index (index_),
800 1.10 christos min_size (min_size_),
801 1.10 christos max_size (max_size_)
802 1.10 christos { /* Nothing. */ }
803 1.10 christos
804 1.10 christos /* The index in m_splits where the cmd window was found. */
805 1.10 christos int index;
806 1.10 christos
807 1.9 christos /* The previous min/max size. */
808 1.9 christos int min_size;
809 1.9 christos int max_size;
810 1.9 christos };
811 1.10 christos
812 1.10 christos /* This is given a value only if we fix the size of the cmd window. */
813 1.10 christos gdb::optional<old_size_info> old_cmd_info;
814 1.9 christos
815 1.9 christos std::vector<size_info> info (m_splits.size ());
816 1.10 christos
817 1.10 christos tui_debug_printf ("weights are: %s",
818 1.10 christos tui_debug_weights_to_string ().c_str ());
819 1.9 christos
820 1.9 christos /* Step 1: Find the min and max size of each sub-layout.
821 1.9 christos Fixed-sized layouts are given their desired size, and then the
822 1.9 christos remaining space is distributed among the remaining windows
823 1.9 christos according to the weights given. */
824 1.9 christos int available_size = m_vertical ? height : width;
825 1.9 christos int last_index = -1;
826 1.9 christos int total_weight = 0;
827 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
828 1.9 christos {
829 1.9 christos bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
830 1.9 christos
831 1.9 christos /* Always call get_sizes, to ensure that the window is
832 1.9 christos instantiated. This is a bit gross but less gross than adding
833 1.9 christos special cases for this in other places. */
834 1.9 christos m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
835 1.9 christos &info[i].max_size);
836 1.10 christos
837 1.9 christos if (preserve_cmd_win_size_p
838 1.9 christos && cmd_win_already_exists
839 1.9 christos && m_splits[i].layout->get_name () != nullptr
840 1.9 christos && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
841 1.10 christos {
842 1.10 christos /* Save the old cmd window information, in case we need to
843 1.10 christos restore it later. */
844 1.10 christos old_cmd_info.emplace (i, info[i].min_size, info[i].max_size);
845 1.9 christos
846 1.9 christos /* If this layout has never been applied, then it means the
847 1.9 christos user just changed the layout. In this situation, it's
848 1.9 christos desirable to keep the size of the command window the
849 1.9 christos same. Setting the min and max sizes this way ensures
850 1.9 christos that the resizing step, below, does the right thing with
851 1.9 christos this window. */
852 1.9 christos info[i].min_size = (m_vertical
853 1.9 christos ? TUI_CMD_WIN->height
854 1.9 christos : TUI_CMD_WIN->width);
855 1.9 christos info[i].max_size = info[i].min_size;
856 1.9 christos }
857 1.9 christos
858 1.9 christos if (info[i].min_size == info[i].max_size)
859 1.1 christos available_size -= info[i].min_size;
860 1.9 christos else
861 1.9 christos {
862 1.9 christos last_index = i;
863 1.9 christos total_weight += m_splits[i].weight;
864 1.9 christos }
865 1.9 christos
866 1.9 christos /* Two adjacent boxed windows will share a border, making a bit
867 1.9 christos more size available. */
868 1.10 christos if (i > 0
869 1.10 christos && m_splits[i - 1].layout->last_edge_has_border_p ()
870 1.9 christos && m_splits[i].layout->first_edge_has_border_p ())
871 1.1 christos info[i].share_box = true;
872 1.9 christos }
873 1.10 christos
874 1.10 christos /* If last_index is set then we have a window that is not of a fixed
875 1.10 christos size. This window will have its size calculated below, which requires
876 1.10 christos that the total_weight not be zero (we divide by total_weight, so don't
877 1.10 christos want a floating-point exception). */
878 1.10 christos gdb_assert (last_index == -1 || total_weight > 0);
879 1.9 christos
880 1.9 christos /* Step 2: Compute the size of each sub-layout. Fixed-sized items
881 1.9 christos are given their fixed size, while others are resized according to
882 1.9 christos their weight. */
883 1.9 christos int used_size = 0;
884 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
885 1.9 christos {
886 1.9 christos if (info[i].min_size != info[i].max_size)
887 1.10 christos {
888 1.10 christos /* Compute the height and clamp to the allowable range. */
889 1.10 christos info[i].size = available_size * m_splits[i].weight / total_weight;
890 1.10 christos if (info[i].size > info[i].max_size)
891 1.10 christos info[i].size = info[i].max_size;
892 1.10 christos if (info[i].size < info[i].min_size)
893 1.10 christos info[i].size = info[i].min_size;
894 1.10 christos /* Keep a total of all the size we've used so far (we gain some
895 1.10 christos size back if this window can share a border with a preceding
896 1.10 christos window). Any unused space will be distributed between all of
897 1.10 christos the other windows (while respecting min/max sizes) later in
898 1.9 christos this function. */
899 1.9 christos used_size += info[i].size;
900 1.9 christos if (info[i].share_box)
901 1.9 christos --used_size;
902 1.10 christos }
903 1.10 christos else
904 1.10 christos info[i].size = info[i].min_size;
905 1.10 christos }
906 1.10 christos
907 1.10 christos if (debug_tui)
908 1.10 christos {
909 1.10 christos tui_debug_printf ("after initial size calculation");
910 1.10 christos tui_debug_printf ("available_size = %d, used_size = %d",
911 1.10 christos available_size, used_size);
912 1.10 christos tui_debug_printf ("total_weight = %d, last_index = %d",
913 1.10 christos total_weight, last_index);
914 1.1 christos tui_debug_print_size_info (info);
915 1.1 christos }
916 1.10 christos
917 1.10 christos /* If we didn't find any sub-layouts that were of a non-fixed size, but
918 1.10 christos we did find the cmd window, then we can consider that a sort-of
919 1.10 christos non-fixed size sub-layout.
920 1.10 christos
921 1.10 christos The cmd window might, initially, be of a fixed size (see above), but,
922 1.10 christos we are willing to relax this constraint if required to correctly apply
923 1.10 christos this layout (see below). */
924 1.10 christos if (last_index == -1 && old_cmd_info.has_value ())
925 1.10 christos last_index = old_cmd_info->index;
926 1.9 christos
927 1.10 christos /* Allocate any leftover size. */
928 1.10 christos if (available_size != used_size && last_index != -1)
929 1.10 christos {
930 1.10 christos /* Loop over all windows until the amount of used space is equal to
931 1.10 christos the amount of available space. There's an escape hatch within
932 1.10 christos the loop in case we can't find any sub-layouts to resize. */
933 1.10 christos bool found_window_that_can_grow_p = true;
934 1.10 christos for (int idx = last_index;
935 1.10 christos available_size != used_size;
936 1.10 christos idx = (idx + 1) % m_splits.size ())
937 1.10 christos {
938 1.10 christos /* Every time we get back to last_index, which is where the loop
939 1.10 christos started, we check to make sure that we did assign some space
940 1.10 christos to a window, bringing used_size closer to available_size.
941 1.10 christos
942 1.10 christos If we didn't, but the cmd window is of a fixed size, then we
943 1.10 christos can make the console window non-fixed-size, and continue
944 1.10 christos around the loop, hopefully, this will allow the layout to be
945 1.10 christos applied correctly.
946 1.10 christos
947 1.10 christos If we still make it around the loop without moving used_size
948 1.10 christos closer to available_size, then there's nothing more we can do,
949 1.10 christos and we break out of the loop. */
950 1.10 christos if (idx == last_index)
951 1.10 christos {
952 1.10 christos /* If the used_size is greater than the available_size then
953 1.10 christos this indicates that the fixed-sized sub-layouts claimed
954 1.10 christos more space than is available. This layout is not going to
955 1.10 christos work. Our only hope at this point is to make the cmd
956 1.10 christos window non-fixed-size (if possible), and hope we can
957 1.10 christos shrink this enough to fit the rest of the sub-layouts in.
958 1.10 christos
959 1.10 christos Alternatively, we've made it around the loop without
960 1.10 christos adjusting any window's size. This likely means all
961 1.10 christos windows have hit their min or max size. Again, our only
962 1.10 christos hope is to make the cmd window non-fixed-size, and hope
963 1.10 christos this fixes all our problems. */
964 1.10 christos if (old_cmd_info.has_value ()
965 1.10 christos && ((available_size < used_size)
966 1.10 christos || !found_window_that_can_grow_p))
967 1.10 christos {
968 1.10 christos info[old_cmd_info->index].min_size = old_cmd_info->min_size;
969 1.10 christos info[old_cmd_info->index].max_size = old_cmd_info->max_size;
970 1.10 christos tui_debug_printf
971 1.10 christos ("restoring index %d (cmd) size limits, min = %d, max = %d",
972 1.10 christos old_cmd_info->index, old_cmd_info->min_size,
973 1.10 christos old_cmd_info->max_size);
974 1.10 christos old_cmd_info.reset ();
975 1.10 christos }
976 1.10 christos else if (!found_window_that_can_grow_p)
977 1.10 christos break;
978 1.10 christos found_window_that_can_grow_p = false;
979 1.10 christos }
980 1.10 christos
981 1.10 christos if (available_size > used_size
982 1.10 christos && info[idx].size < info[idx].max_size)
983 1.10 christos {
984 1.10 christos found_window_that_can_grow_p = true;
985 1.10 christos info[idx].size += 1;
986 1.10 christos used_size += 1;
987 1.10 christos }
988 1.10 christos else if (available_size < used_size
989 1.10 christos && info[idx].size > info[idx].min_size)
990 1.10 christos {
991 1.10 christos found_window_that_can_grow_p = true;
992 1.10 christos info[idx].size -= 1;
993 1.10 christos used_size -= 1;
994 1.10 christos }
995 1.10 christos }
996 1.10 christos
997 1.10 christos if (debug_tui)
998 1.10 christos {
999 1.10 christos tui_debug_printf ("after final size calculation");
1000 1.10 christos tui_debug_printf ("available_size = %d, used_size = %d",
1001 1.10 christos available_size, used_size);
1002 1.10 christos tui_debug_printf ("total_weight = %d, last_index = %d",
1003 1.10 christos total_weight, last_index);
1004 1.10 christos tui_debug_print_size_info (info);
1005 1.10 christos }
1006 1.9 christos }
1007 1.9 christos
1008 1.9 christos /* Step 3: Resize. */
1009 1.9 christos int size_accum = 0;
1010 1.9 christos const int maximum = m_vertical ? height : width;
1011 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
1012 1.9 christos {
1013 1.9 christos /* If we fall off the bottom, just make allocations overlap.
1014 1.9 christos GIGO. */
1015 1.9 christos if (size_accum + info[i].size > maximum)
1016 1.9 christos size_accum = maximum - info[i].size;
1017 1.9 christos else if (info[i].share_box)
1018 1.9 christos --size_accum;
1019 1.10 christos if (m_vertical)
1020 1.10 christos m_splits[i].layout->apply (x, y + size_accum, width, info[i].size,
1021 1.1 christos preserve_cmd_win_size_p);
1022 1.10 christos else
1023 1.10 christos m_splits[i].layout->apply (x + size_accum, y, info[i].size, height,
1024 1.9 christos preserve_cmd_win_size_p);
1025 1.1 christos size_accum += info[i].size;
1026 1.9 christos }
1027 1.9 christos }
1028 1.9 christos
1029 1.9 christos /* See tui-layout.h. */
1030 1.9 christos
1031 1.9 christos void
1032 1.9 christos tui_layout_split::remove_windows (const char *name)
1033 1.9 christos {
1034 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
1035 1.9 christos {
1036 1.9 christos const char *this_name = m_splits[i].layout->get_name ();
1037 1.9 christos if (this_name == nullptr)
1038 1.9 christos m_splits[i].layout->remove_windows (name);
1039 1.9 christos else if (strcmp (this_name, name) == 0
1040 1.9 christos || strcmp (this_name, CMD_NAME) == 0
1041 1.9 christos || strcmp (this_name, STATUS_NAME) == 0)
1042 1.9 christos {
1043 1.9 christos /* Keep. */
1044 1.9 christos }
1045 1.1 christos else
1046 1.9 christos {
1047 1.9 christos m_splits.erase (m_splits.begin () + i);
1048 1.1 christos --i;
1049 1.1 christos }
1050 1.1 christos }
1051 1.1 christos }
1052 1.9 christos
1053 1.9 christos /* See tui-layout.h. */
1054 1.9 christos
1055 1.9 christos void
1056 1.9 christos tui_layout_split::replace_window (const char *name, const char *new_window)
1057 1.9 christos {
1058 1.9 christos for (auto &item : m_splits)
1059 1.9 christos item.layout->replace_window (name, new_window);
1060 1.9 christos }
1061 1.9 christos
1062 1.9 christos /* See tui-layout.h. */
1063 1.9 christos
1064 1.9 christos void
1065 1.9 christos tui_layout_split::specification (ui_file *output, int depth)
1066 1.9 christos {
1067 1.10 christos if (depth > 0)
1068 1.9 christos gdb_puts ("{", output);
1069 1.9 christos
1070 1.10 christos if (!m_vertical)
1071 1.9 christos gdb_puts ("-horizontal ", output);
1072 1.9 christos
1073 1.9 christos bool first = true;
1074 1.9 christos for (auto &item : m_splits)
1075 1.9 christos {
1076 1.10 christos if (!first)
1077 1.9 christos gdb_puts (" ", output);
1078 1.9 christos first = false;
1079 1.10 christos item.layout->specification (output, depth + 1);
1080 1.9 christos gdb_printf (output, " %d", item.weight);
1081 1.9 christos }
1082 1.9 christos
1083 1.10 christos if (depth > 0)
1084 1.10 christos gdb_puts ("}", output);
1085 1.10 christos }
1086 1.10 christos
1087 1.10 christos /* See tui-layout.h. */
1088 1.10 christos
1089 1.10 christos std::string
1090 1.10 christos tui_layout_split::layout_fingerprint () const
1091 1.10 christos {
1092 1.10 christos for (auto &item : m_splits)
1093 1.10 christos {
1094 1.10 christos std::string fp = item.layout->layout_fingerprint ();
1095 1.10 christos if (!fp.empty ())
1096 1.10 christos return std::string (m_vertical ? "V" : "H") + fp;
1097 1.10 christos }
1098 1.10 christos
1099 1.9 christos return "";
1100 1.9 christos }
1101 1.9 christos
1102 1.9 christos /* Destroy the layout associated with SELF. */
1103 1.9 christos
1104 1.9 christos static void
1105 1.9 christos destroy_layout (struct cmd_list_element *self, void *context)
1106 1.9 christos {
1107 1.9 christos tui_layout_split *layout = (tui_layout_split *) context;
1108 1.9 christos size_t index = find_layout (layout);
1109 1.9 christos layouts.erase (layouts.begin () + index);
1110 1.9 christos }
1111 1.9 christos
1112 1.9 christos /* List holding the sub-commands of "layout". */
1113 1.9 christos
1114 1.9 christos static struct cmd_list_element *layout_list;
1115 1.10 christos
1116 1.10 christos /* Called to implement 'tui layout'. */
1117 1.10 christos
1118 1.10 christos static void
1119 1.10 christos tui_layout_command (const char *args, int from_tty)
1120 1.10 christos {
1121 1.10 christos help_list (layout_list, "tui layout ", all_commands, gdb_stdout);
1122 1.10 christos }
1123 1.9 christos
1124 1.9 christos /* Add a "layout" command with name NAME that switches to LAYOUT. */
1125 1.9 christos
1126 1.9 christos static struct cmd_list_element *
1127 1.9 christos add_layout_command (const char *name, tui_layout_split *layout)
1128 1.9 christos {
1129 1.9 christos struct cmd_list_element *cmd;
1130 1.9 christos
1131 1.9 christos string_file spec;
1132 1.9 christos layout->specification (&spec, 0);
1133 1.9 christos
1134 1.10 christos gdb::unique_xmalloc_ptr<char> doc
1135 1.9 christos = xstrprintf (_("Apply the \"%s\" layout.\n\
1136 1.9 christos This layout was created using:\n\
1137 1.10 christos tui new-layout %s %s"),
1138 1.9 christos name, name, spec.c_str ());
1139 1.9 christos
1140 1.10 christos cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
1141 1.9 christos cmd->set_context (layout);
1142 1.9 christos /* There is no API to set this. */
1143 1.9 christos cmd->func = tui_apply_layout;
1144 1.9 christos cmd->destroyer = destroy_layout;
1145 1.9 christos cmd->doc_allocated = 1;
1146 1.9 christos doc.release ();
1147 1.9 christos layouts.emplace_back (layout);
1148 1.9 christos
1149 1.9 christos return cmd;
1150 1.9 christos }
1151 1.9 christos
1152 1.1 christos /* Initialize the standard layouts. */
1153 1.1 christos
1154 1.9 christos static void
1155 1.9 christos initialize_layouts ()
1156 1.9 christos {
1157 1.9 christos tui_layout_split *layout;
1158 1.9 christos
1159 1.9 christos layout = new tui_layout_split ();
1160 1.9 christos layout->add_window (SRC_NAME, 2);
1161 1.9 christos layout->add_window (STATUS_NAME, 0);
1162 1.9 christos layout->add_window (CMD_NAME, 1);
1163 1.9 christos add_layout_command (SRC_NAME, layout);
1164 1.9 christos
1165 1.9 christos layout = new tui_layout_split ();
1166 1.9 christos layout->add_window (DISASSEM_NAME, 2);
1167 1.9 christos layout->add_window (STATUS_NAME, 0);
1168 1.9 christos layout->add_window (CMD_NAME, 1);
1169 1.9 christos add_layout_command (DISASSEM_NAME, layout);
1170 1.9 christos
1171 1.9 christos layout = new tui_layout_split ();
1172 1.9 christos layout->add_window (SRC_NAME, 1);
1173 1.9 christos layout->add_window (DISASSEM_NAME, 1);
1174 1.9 christos layout->add_window (STATUS_NAME, 0);
1175 1.9 christos layout->add_window (CMD_NAME, 1);
1176 1.9 christos add_layout_command ("split", layout);
1177 1.9 christos
1178 1.9 christos layout = new tui_layout_split ();
1179 1.9 christos layout->add_window (DATA_NAME, 1);
1180 1.9 christos layout->add_window (SRC_NAME, 1);
1181 1.9 christos layout->add_window (STATUS_NAME, 0);
1182 1.9 christos layout->add_window (CMD_NAME, 1);
1183 1.9 christos layouts.emplace_back (layout);
1184 1.9 christos src_regs_layout = layout;
1185 1.9 christos
1186 1.9 christos layout = new tui_layout_split ();
1187 1.9 christos layout->add_window (DATA_NAME, 1);
1188 1.9 christos layout->add_window (DISASSEM_NAME, 1);
1189 1.9 christos layout->add_window (STATUS_NAME, 0);
1190 1.9 christos layout->add_window (CMD_NAME, 1);
1191 1.9 christos layouts.emplace_back (layout);
1192 1.9 christos asm_regs_layout = layout;
1193 1.9 christos }
1194 1.9 christos
1195 1.9 christos
1196 1.9 christos
1198 1.1 christos /* A helper function that returns true if NAME is the name of an
1199 1.9 christos available window. */
1200 1.9 christos
1201 1.9 christos static bool
1202 1.9 christos validate_window_name (const std::string &name)
1203 1.9 christos {
1204 1.1 christos auto iter = known_window_types->find (name);
1205 1.1 christos return iter != known_window_types->end ();
1206 1.9 christos }
1207 1.1 christos
1208 1.1 christos /* Implementation of the "tui new-layout" command. */
1209 1.9 christos
1210 1.1 christos static void
1211 1.9 christos tui_new_layout_command (const char *spec, int from_tty)
1212 1.9 christos {
1213 1.9 christos std::string new_name = extract_arg (&spec);
1214 1.9 christos if (new_name.empty ())
1215 1.9 christos error (_("No layout name specified"));
1216 1.9 christos if (new_name[0] == '-')
1217 1.9 christos error (_("Layout name cannot start with '-'"));
1218 1.9 christos
1219 1.9 christos bool is_vertical = true;
1220 1.9 christos spec = skip_spaces (spec);
1221 1.9 christos if (check_for_argument (&spec, "-horizontal"))
1222 1.9 christos is_vertical = false;
1223 1.9 christos
1224 1.9 christos std::vector<std::unique_ptr<tui_layout_split>> splits;
1225 1.9 christos splits.emplace_back (new tui_layout_split (is_vertical));
1226 1.1 christos std::unordered_set<std::string> seen_windows;
1227 1.9 christos while (true)
1228 1.9 christos {
1229 1.9 christos spec = skip_spaces (spec);
1230 1.1 christos if (spec[0] == '\0')
1231 1.9 christos break;
1232 1.9 christos
1233 1.9 christos if (spec[0] == '{')
1234 1.9 christos {
1235 1.9 christos is_vertical = true;
1236 1.9 christos spec = skip_spaces (spec + 1);
1237 1.9 christos if (check_for_argument (&spec, "-horizontal"))
1238 1.9 christos is_vertical = false;
1239 1.9 christos splits.emplace_back (new tui_layout_split (is_vertical));
1240 1.1 christos continue;
1241 1.9 christos }
1242 1.9 christos
1243 1.9 christos bool is_close = false;
1244 1.9 christos std::string name;
1245 1.9 christos if (spec[0] == '}')
1246 1.9 christos {
1247 1.9 christos is_close = true;
1248 1.9 christos ++spec;
1249 1.9 christos if (splits.size () == 1)
1250 1.1 christos error (_("Extra '}' in layout specification"));
1251 1.9 christos }
1252 1.9 christos else
1253 1.9 christos {
1254 1.9 christos name = extract_arg (&spec);
1255 1.9 christos if (name.empty ())
1256 1.9 christos break;
1257 1.9 christos if (!validate_window_name (name))
1258 1.9 christos error (_("Unknown window \"%s\""), name.c_str ());
1259 1.9 christos if (seen_windows.find (name) != seen_windows.end ())
1260 1.1 christos error (_("Window \"%s\" seen twice in layout"), name.c_str ());
1261 1.9 christos }
1262 1.9 christos
1263 1.9 christos ULONGEST weight = get_ulongest (&spec, '}');
1264 1.9 christos if ((int) weight != weight)
1265 1.1 christos error (_("Weight out of range: %s"), pulongest (weight));
1266 1.9 christos if (is_close)
1267 1.9 christos {
1268 1.9 christos std::unique_ptr<tui_layout_split> last_split
1269 1.9 christos = std::move (splits.back ());
1270 1.1 christos splits.pop_back ();
1271 1.1 christos splits.back ()->add_split (std::move (last_split), weight);
1272 1.1 christos }
1273 1.9 christos else
1274 1.9 christos {
1275 1.1 christos splits.back ()->add_window (name.c_str (), weight);
1276 1.1 christos seen_windows.insert (name);
1277 1.9 christos }
1278 1.9 christos }
1279 1.9 christos if (splits.size () > 1)
1280 1.9 christos error (_("Missing '}' in layout specification"));
1281 1.9 christos if (seen_windows.empty ())
1282 1.9 christos error (_("New layout does not contain any windows"));
1283 1.9 christos if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1284 1.9 christos error (_("New layout does not contain the \"" CMD_NAME "\" window"));
1285 1.9 christos
1286 1.9 christos gdb::unique_xmalloc_ptr<char> cmd_name
1287 1.9 christos = make_unique_xstrdup (new_name.c_str ());
1288 1.9 christos std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
1289 1.9 christos struct cmd_list_element *cmd
1290 1.9 christos = add_layout_command (cmd_name.get (), new_layout.get ());
1291 1.9 christos cmd->name_allocated = 1;
1292 1.9 christos cmd_name.release ();
1293 1.9 christos new_layout.release ();
1294 1.9 christos }
1295 1.9 christos
1296 1.9 christos /* Function to initialize gdb commands, for tui window layout
1297 1.9 christos manipulation. */
1298 1.9 christos
1299 1.9 christos void _initialize_tui_layout ();
1300 1.9 christos void
1301 1.10 christos _initialize_tui_layout ()
1302 1.10 christos {
1303 1.9 christos struct cmd_list_element *layout_cmd
1304 1.10 christos = add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
1305 1.10 christos Change the layout of windows.\n\
1306 1.10 christos Usage: tui layout prev | next | LAYOUT-NAME"),
1307 1.9 christos &layout_list, 0, tui_get_cmd_list ());
1308 1.9 christos add_com_alias ("layout", layout_cmd, class_tui, 0);
1309 1.9 christos
1310 1.9 christos add_cmd ("next", class_tui, tui_next_layout_command,
1311 1.9 christos _("Apply the next TUI layout."),
1312 1.9 christos &layout_list);
1313 1.9 christos add_cmd ("prev", class_tui, tui_prev_layout_command,
1314 1.9 christos _("Apply the previous TUI layout."),
1315 1.9 christos &layout_list);
1316 1.9 christos add_cmd ("regs", class_tui, tui_regs_layout_command,
1317 1.9 christos _("Apply the TUI register layout."),
1318 1.9 christos &layout_list);
1319 1.9 christos
1320 1.9 christos add_cmd ("new-layout", class_tui, tui_new_layout_command,
1321 1.9 christos _("Create a new TUI layout.\n\
1322 1.9 christos Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1323 1.9 christos Create a new TUI layout. The new layout will be named NAME,\n\
1324 1.9 christos and can be accessed using \"layout NAME\".\n\
1325 1.9 christos The windows will be displayed in the specified order.\n\
1326 1.9 christos A WINDOW can also be of the form:\n\
1327 1.9 christos { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1328 1.9 christos This form indicates a sub-frame.\n\
1329 1.9 christos Each WEIGHT is an integer, which holds the relative size\n\
1330 1.9 christos to be allocated to the window."),
1331 1.9 christos tui_get_cmd_list ());
1332 1.9 christos
1333 1.1 christos initialize_layouts ();
1334 initialize_known_windows ();
1335 }
1336