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