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