tui-layout.c revision 1.9 1 1.1 christos /* TUI layout window management.
2 1.1 christos
3 1.9 christos Copyright (C) 1998-2020 Free Software Foundation, Inc.
4 1.1 christos
5 1.1 christos Contributed by Hewlett-Packard Company.
6 1.1 christos
7 1.1 christos This file is part of GDB.
8 1.1 christos
9 1.1 christos This program is free software; you can redistribute it and/or modify
10 1.1 christos it under the terms of the GNU General Public License as published by
11 1.1 christos the Free Software Foundation; either version 3 of the License, or
12 1.1 christos (at your option) any later version.
13 1.1 christos
14 1.1 christos This program is distributed in the hope that it will be useful,
15 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
16 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 1.1 christos GNU General Public License for more details.
18 1.1 christos
19 1.1 christos You should have received a copy of the GNU General Public License
20 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 1.1 christos
22 1.1 christos #include "defs.h"
23 1.1 christos #include "arch-utils.h"
24 1.1 christos #include "command.h"
25 1.1 christos #include "symtab.h"
26 1.1 christos #include "frame.h"
27 1.1 christos #include "source.h"
28 1.9 christos #include "cli/cli-cmds.h"
29 1.9 christos #include "cli/cli-decode.h"
30 1.9 christos #include "cli/cli-utils.h"
31 1.1 christos #include <ctype.h>
32 1.9 christos #include <unordered_map>
33 1.9 christos #include <unordered_set>
34 1.1 christos
35 1.1 christos #include "tui/tui.h"
36 1.9 christos #include "tui/tui-command.h"
37 1.1 christos #include "tui/tui-data.h"
38 1.1 christos #include "tui/tui-wingeneral.h"
39 1.1 christos #include "tui/tui-stack.h"
40 1.1 christos #include "tui/tui-regs.h"
41 1.1 christos #include "tui/tui-win.h"
42 1.1 christos #include "tui/tui-winsource.h"
43 1.1 christos #include "tui/tui-disasm.h"
44 1.1 christos #include "tui/tui-layout.h"
45 1.9 christos #include "tui/tui-source.h"
46 1.1 christos #include "gdb_curses.h"
47 1.1 christos
48 1.1 christos static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
49 1.1 christos
50 1.9 christos /* The layouts. */
51 1.9 christos static std::vector<std::unique_ptr<tui_layout_split>> layouts;
52 1.1 christos
53 1.9 christos /* The layout that is currently applied. */
54 1.9 christos static std::unique_ptr<tui_layout_base> applied_layout;
55 1.1 christos
56 1.9 christos /* The "skeleton" version of the layout that is currently applied. */
57 1.9 christos static tui_layout_split *applied_skeleton;
58 1.1 christos
59 1.9 christos /* The two special "regs" layouts. Note that these aren't registered
60 1.9 christos as commands and so can never be deleted. */
61 1.9 christos static tui_layout_split *src_regs_layout;
62 1.9 christos static tui_layout_split *asm_regs_layout;
63 1.9 christos
64 1.9 christos /* See tui-data.h. */
65 1.9 christos std::vector<tui_win_info *> tui_windows;
66 1.9 christos
67 1.9 christos /* When applying a layout, this is the list of all windows that were
68 1.9 christos in the previous layout. This is used to re-use windows when
69 1.9 christos changing a layout. */
70 1.9 christos static std::vector<tui_win_info *> saved_tui_windows;
71 1.9 christos
72 1.9 christos /* See tui-layout.h. */
73 1.9 christos
74 1.9 christos void
75 1.9 christos tui_apply_current_layout ()
76 1.1 christos {
77 1.9 christos struct gdbarch *gdbarch;
78 1.9 christos CORE_ADDR addr;
79 1.9 christos
80 1.9 christos extract_display_start_addr (&gdbarch, &addr);
81 1.9 christos
82 1.9 christos saved_tui_windows = std::move (tui_windows);
83 1.9 christos tui_windows.clear ();
84 1.1 christos
85 1.9 christos for (tui_win_info *win_info : saved_tui_windows)
86 1.9 christos win_info->make_visible (false);
87 1.9 christos
88 1.9 christos applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
89 1.9 christos
90 1.9 christos /* Keep the list of internal windows up-to-date. */
91 1.9 christos for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
92 1.9 christos if (tui_win_list[win_type] != nullptr
93 1.9 christos && !tui_win_list[win_type]->is_visible ())
94 1.9 christos tui_win_list[win_type] = nullptr;
95 1.9 christos
96 1.9 christos /* This should always be made visible by a layout. */
97 1.9 christos gdb_assert (TUI_CMD_WIN->is_visible ());
98 1.9 christos
99 1.9 christos /* Now delete any window that was not re-applied. */
100 1.9 christos tui_win_info *focus = tui_win_with_focus ();
101 1.9 christos for (tui_win_info *win_info : saved_tui_windows)
102 1.1 christos {
103 1.9 christos if (!win_info->is_visible ())
104 1.1 christos {
105 1.9 christos if (focus == win_info)
106 1.9 christos tui_set_win_focus_to (tui_windows[0]);
107 1.9 christos delete win_info;
108 1.1 christos }
109 1.1 christos }
110 1.9 christos
111 1.9 christos if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
112 1.9 christos tui_get_begin_asm_address (&gdbarch, &addr);
113 1.9 christos tui_update_source_windows_with_addr (gdbarch, addr);
114 1.9 christos
115 1.9 christos saved_tui_windows.clear ();
116 1.1 christos }
117 1.1 christos
118 1.9 christos /* See tui-layout. */
119 1.9 christos
120 1.9 christos void
121 1.9 christos tui_adjust_window_height (struct tui_win_info *win, int new_height)
122 1.9 christos {
123 1.9 christos applied_layout->adjust_size (win->name (), new_height);
124 1.9 christos }
125 1.1 christos
126 1.9 christos /* Set the current layout to LAYOUT. */
127 1.1 christos
128 1.9 christos static void
129 1.9 christos tui_set_layout (tui_layout_split *layout)
130 1.9 christos {
131 1.9 christos applied_skeleton = layout;
132 1.9 christos applied_layout = layout->clone ();
133 1.9 christos tui_apply_current_layout ();
134 1.1 christos }
135 1.1 christos
136 1.9 christos /* See tui-layout.h. */
137 1.9 christos
138 1.1 christos void
139 1.1 christos tui_add_win_to_layout (enum tui_win_type type)
140 1.1 christos {
141 1.9 christos gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
142 1.9 christos
143 1.9 christos /* If the window already exists, no need to add it. */
144 1.9 christos if (tui_win_list[type] != nullptr)
145 1.9 christos return;
146 1.9 christos
147 1.9 christos /* If the window we are trying to replace doesn't exist, we're
148 1.9 christos done. */
149 1.9 christos enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
150 1.9 christos if (tui_win_list[other] == nullptr)
151 1.9 christos return;
152 1.9 christos
153 1.9 christos const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
154 1.9 christos applied_layout->replace_window (tui_win_list[other]->name (), name);
155 1.9 christos tui_apply_current_layout ();
156 1.9 christos }
157 1.9 christos
158 1.9 christos /* Find LAYOUT in the "layouts" global and return its index. */
159 1.1 christos
160 1.9 christos static size_t
161 1.9 christos find_layout (tui_layout_split *layout)
162 1.9 christos {
163 1.9 christos for (size_t i = 0; i < layouts.size (); ++i)
164 1.1 christos {
165 1.9 christos if (layout == layouts[i].get ())
166 1.9 christos return i;
167 1.1 christos }
168 1.9 christos gdb_assert_not_reached (_("layout not found!?"));
169 1.1 christos }
170 1.1 christos
171 1.9 christos /* Function to set the layout. */
172 1.1 christos
173 1.9 christos static void
174 1.9 christos tui_apply_layout (struct cmd_list_element *command,
175 1.9 christos const char *args, int from_tty)
176 1.1 christos {
177 1.9 christos tui_layout_split *layout
178 1.9 christos = (tui_layout_split *) get_cmd_context (command);
179 1.1 christos
180 1.9 christos /* Make sure the curses mode is enabled. */
181 1.9 christos tui_enable ();
182 1.9 christos tui_set_layout (layout);
183 1.9 christos }
184 1.9 christos
185 1.9 christos /* See tui-layout.h. */
186 1.1 christos
187 1.9 christos void
188 1.9 christos tui_next_layout ()
189 1.9 christos {
190 1.9 christos size_t index = find_layout (applied_skeleton);
191 1.9 christos ++index;
192 1.9 christos if (index == layouts.size ())
193 1.9 christos index = 0;
194 1.9 christos tui_set_layout (layouts[index].get ());
195 1.1 christos }
196 1.1 christos
197 1.9 christos /* Implement the "layout next" command. */
198 1.1 christos
199 1.9 christos static void
200 1.9 christos tui_next_layout_command (const char *arg, int from_tty)
201 1.1 christos {
202 1.9 christos tui_enable ();
203 1.9 christos tui_next_layout ();
204 1.9 christos }
205 1.1 christos
206 1.9 christos /* See tui-layout.h. */
207 1.1 christos
208 1.9 christos void
209 1.9 christos tui_set_initial_layout ()
210 1.9 christos {
211 1.9 christos tui_set_layout (layouts[0].get ());
212 1.1 christos }
213 1.1 christos
214 1.9 christos /* Implement the "layout prev" command. */
215 1.5 christos
216 1.8 christos static void
217 1.9 christos tui_prev_layout_command (const char *arg, int from_tty)
218 1.5 christos {
219 1.9 christos tui_enable ();
220 1.9 christos size_t index = find_layout (applied_skeleton);
221 1.9 christos if (index == 0)
222 1.9 christos index = layouts.size ();
223 1.9 christos --index;
224 1.9 christos tui_set_layout (layouts[index].get ());
225 1.9 christos }
226 1.5 christos
227 1.1 christos
228 1.9 christos /* See tui-layout.h. */
229 1.1 christos
230 1.1 christos void
231 1.9 christos tui_regs_layout ()
232 1.1 christos {
233 1.9 christos /* If there's already a register window, we're done. */
234 1.9 christos if (TUI_DATA_WIN != nullptr)
235 1.9 christos return;
236 1.5 christos
237 1.9 christos tui_set_layout (TUI_DISASM_WIN != nullptr
238 1.9 christos ? asm_regs_layout
239 1.9 christos : src_regs_layout);
240 1.1 christos }
241 1.1 christos
242 1.9 christos /* Implement the "layout regs" command. */
243 1.1 christos
244 1.9 christos static void
245 1.9 christos tui_regs_layout_command (const char *arg, int from_tty)
246 1.9 christos {
247 1.9 christos tui_enable ();
248 1.9 christos tui_regs_layout ();
249 1.9 christos }
250 1.1 christos
251 1.9 christos /* See tui-layout.h. */
252 1.1 christos
253 1.9 christos void
254 1.9 christos tui_remove_some_windows ()
255 1.1 christos {
256 1.9 christos tui_win_info *focus = tui_win_with_focus ();
257 1.1 christos
258 1.9 christos if (strcmp (focus->name (), CMD_NAME) == 0)
259 1.1 christos {
260 1.9 christos /* Try leaving the source or disassembly window. If neither
261 1.9 christos exists, just do nothing. */
262 1.9 christos focus = TUI_SRC_WIN;
263 1.9 christos if (focus == nullptr)
264 1.9 christos focus = TUI_DISASM_WIN;
265 1.9 christos if (focus == nullptr)
266 1.9 christos return;
267 1.1 christos }
268 1.1 christos
269 1.9 christos applied_layout->remove_windows (focus->name ());
270 1.9 christos tui_apply_current_layout ();
271 1.1 christos }
272 1.1 christos
273 1.1 christos static void
274 1.1 christos extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
275 1.1 christos {
276 1.9 christos if (TUI_SRC_WIN != nullptr)
277 1.9 christos TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
278 1.9 christos else if (TUI_DISASM_WIN != nullptr)
279 1.9 christos TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
280 1.9 christos else
281 1.9 christos {
282 1.9 christos *gdbarch_p = nullptr;
283 1.9 christos *addr_p = 0;
284 1.9 christos }
285 1.9 christos }
286 1.9 christos
287 1.9 christos void
288 1.9 christos tui_win_info::resize (int height_, int width_,
289 1.9 christos int origin_x_, int origin_y_)
290 1.9 christos {
291 1.9 christos if (width == width_ && height == height_
292 1.9 christos && x == origin_x_ && y == origin_y_
293 1.9 christos && handle != nullptr)
294 1.9 christos return;
295 1.9 christos
296 1.9 christos width = width_;
297 1.9 christos height = height_;
298 1.9 christos x = origin_x_;
299 1.9 christos y = origin_y_;
300 1.1 christos
301 1.9 christos if (handle != nullptr)
302 1.1 christos {
303 1.9 christos #ifdef HAVE_WRESIZE
304 1.9 christos wresize (handle.get (), height, width);
305 1.9 christos mvwin (handle.get (), y, x);
306 1.9 christos wmove (handle.get (), 0, 0);
307 1.9 christos #else
308 1.9 christos handle.reset (nullptr);
309 1.9 christos #endif
310 1.1 christos }
311 1.1 christos
312 1.9 christos if (handle == nullptr)
313 1.9 christos make_window ();
314 1.9 christos
315 1.9 christos rerender ();
316 1.9 christos }
317 1.9 christos
318 1.9 christos
319 1.9 christos
321 1.9 christos /* Helper function to create one of the built-in (non-locator)
322 1.9 christos windows. */
323 1.9 christos
324 1.9 christos template<enum tui_win_type V, class T>
325 1.9 christos static tui_win_info *
326 1.9 christos make_standard_window (const char *)
327 1.9 christos {
328 1.9 christos if (tui_win_list[V] == nullptr)
329 1.9 christos tui_win_list[V] = new T ();
330 1.9 christos return tui_win_list[V];
331 1.9 christos }
332 1.9 christos
333 1.9 christos /* Helper function to wrap tui_locator_win_info_ptr for
334 1.9 christos tui_get_window_by_name. */
335 1.9 christos
336 1.9 christos static tui_win_info *
337 1.9 christos get_locator_window (const char *)
338 1.9 christos {
339 1.1 christos return tui_locator_win_info_ptr ();
340 1.1 christos }
341 1.9 christos
342 1.9 christos /* A map holding all the known window types, keyed by name. Note that
343 1.9 christos this is heap-allocated and "leaked" at gdb exit. This avoids
344 1.9 christos ordering issues with destroying elements in the map at shutdown.
345 1.9 christos In particular, destroying this map can occur after Python has been
346 1.9 christos shut down, causing crashes if any window destruction requires
347 1.9 christos running Python code. */
348 1.9 christos
349 1.9 christos static std::unordered_map<std::string, window_factory> *known_window_types;
350 1.9 christos
351 1.9 christos /* Helper function that returns a TUI window, given its name. */
352 1.9 christos
353 1.9 christos static tui_win_info *
354 1.9 christos tui_get_window_by_name (const std::string &name)
355 1.9 christos {
356 1.9 christos for (tui_win_info *window : saved_tui_windows)
357 1.9 christos if (name == window->name ())
358 1.9 christos return window;
359 1.9 christos
360 1.9 christos auto iter = known_window_types->find (name);
361 1.9 christos if (iter == known_window_types->end ())
362 1.9 christos error (_("Unknown window type \"%s\""), name.c_str ());
363 1.9 christos
364 1.9 christos tui_win_info *result = iter->second (name.c_str ());
365 1.9 christos if (result == nullptr)
366 1.9 christos error (_("Could not create window \"%s\""), name.c_str ());
367 1.9 christos return result;
368 1.9 christos }
369 1.9 christos
370 1.1 christos /* Initialize the known window types. */
371 1.1 christos
372 1.9 christos static void
373 1.1 christos initialize_known_windows ()
374 1.9 christos {
375 1.9 christos known_window_types = new std::unordered_map<std::string, window_factory>;
376 1.9 christos
377 1.9 christos known_window_types->emplace (SRC_NAME,
378 1.9 christos make_standard_window<SRC_WIN,
379 1.9 christos tui_source_window>);
380 1.9 christos known_window_types->emplace (CMD_NAME,
381 1.9 christos make_standard_window<CMD_WIN, tui_cmd_window>);
382 1.9 christos known_window_types->emplace (DATA_NAME,
383 1.9 christos make_standard_window<DATA_WIN,
384 1.9 christos tui_data_window>);
385 1.9 christos known_window_types->emplace (DISASSEM_NAME,
386 1.9 christos make_standard_window<DISASSEM_WIN,
387 1.9 christos tui_disasm_window>);
388 1.1 christos known_window_types->emplace (STATUS_NAME, get_locator_window);
389 1.1 christos }
390 1.9 christos
391 1.9 christos /* See tui-layout.h. */
392 1.9 christos
393 1.9 christos void
394 1.1 christos tui_register_window (const char *name, window_factory &&factory)
395 1.9 christos {
396 1.9 christos std::string name_copy = name;
397 1.9 christos
398 1.9 christos if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
399 1.9 christos || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
400 1.9 christos error (_("Window type \"%s\" is built-in"), name);
401 1.9 christos
402 1.9 christos known_window_types->emplace (std::move (name_copy),
403 1.9 christos std::move (factory));
404 1.1 christos }
405 1.9 christos
406 1.1 christos /* See tui-layout.h. */
407 1.9 christos
408 1.9 christos std::unique_ptr<tui_layout_base>
409 1.9 christos tui_layout_window::clone () const
410 1.9 christos {
411 1.9 christos tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
412 1.1 christos return std::unique_ptr<tui_layout_base> (result);
413 1.1 christos }
414 1.9 christos
415 1.1 christos /* See tui-layout.h. */
416 1.9 christos
417 1.9 christos void
418 1.1 christos tui_layout_window::apply (int x_, int y_, int width_, int height_)
419 1.9 christos {
420 1.9 christos x = x_;
421 1.9 christos y = y_;
422 1.9 christos width = width_;
423 1.9 christos height = height_;
424 1.9 christos gdb_assert (m_window != nullptr);
425 1.9 christos m_window->resize (height, width, x, y);
426 1.9 christos tui_windows.push_back (m_window);
427 1.1 christos }
428 1.9 christos
429 1.9 christos /* See tui-layout.h. */
430 1.9 christos
431 1.9 christos void
432 1.9 christos tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
433 1.9 christos {
434 1.9 christos if (m_window == nullptr)
435 1.9 christos m_window = tui_get_window_by_name (m_contents);
436 1.9 christos if (height)
437 1.9 christos {
438 1.9 christos *min_value = m_window->min_height ();
439 1.9 christos *max_value = m_window->max_height ();
440 1.1 christos }
441 1.1 christos else
442 1.9 christos {
443 1.9 christos *min_value = m_window->min_width ();
444 1.1 christos *max_value = m_window->max_width ();
445 1.9 christos }
446 1.1 christos }
447 1.9 christos
448 1.9 christos /* See tui-layout.h. */
449 1.9 christos
450 1.9 christos bool
451 1.9 christos tui_layout_window::top_boxed_p () const
452 1.9 christos {
453 1.9 christos gdb_assert (m_window != nullptr);
454 1.1 christos return m_window->can_box ();
455 1.1 christos }
456 1.9 christos
457 1.9 christos /* See tui-layout.h. */
458 1.9 christos
459 1.9 christos bool
460 1.9 christos tui_layout_window::bottom_boxed_p () const
461 1.9 christos {
462 1.9 christos gdb_assert (m_window != nullptr);
463 1.9 christos return m_window->can_box ();
464 1.1 christos }
465 1.9 christos
466 1.1 christos /* See tui-layout.h. */
467 1.9 christos
468 1.9 christos void
469 1.1 christos tui_layout_window::replace_window (const char *name, const char *new_window)
470 1.9 christos {
471 1.9 christos if (m_contents == name)
472 1.9 christos {
473 1.9 christos m_contents = new_window;
474 1.9 christos if (m_window != nullptr)
475 1.9 christos {
476 1.9 christos m_window->make_visible (false);
477 1.9 christos m_window = tui_get_window_by_name (m_contents);
478 1.9 christos }
479 1.9 christos }
480 1.9 christos }
481 1.9 christos
482 1.1 christos /* See tui-layout.h. */
483 1.9 christos
484 1.9 christos void
485 1.9 christos tui_layout_window::specification (ui_file *output, int depth)
486 1.9 christos {
487 1.1 christos fputs_unfiltered (get_name (), output);
488 1.1 christos }
489 1.9 christos
490 1.1 christos /* See tui-layout.h. */
491 1.9 christos
492 1.9 christos void
493 1.9 christos tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
494 1.1 christos int weight)
495 1.9 christos {
496 1.9 christos split s = {weight, std::move (layout)};
497 1.9 christos m_splits.push_back (std::move (s));
498 1.9 christos }
499 1.9 christos
500 1.1 christos /* See tui-layout.h. */
501 1.9 christos
502 1.9 christos void
503 1.9 christos tui_layout_split::add_window (const char *name, int weight)
504 1.9 christos {
505 1.9 christos tui_layout_window *result = new tui_layout_window (name);
506 1.9 christos split s = {weight, std::unique_ptr<tui_layout_base> (result)};
507 1.9 christos m_splits.push_back (std::move (s));
508 1.1 christos }
509 1.9 christos
510 1.1 christos /* See tui-layout.h. */
511 1.9 christos
512 1.9 christos std::unique_ptr<tui_layout_base>
513 1.1 christos tui_layout_split::clone () const
514 1.9 christos {
515 1.9 christos tui_layout_split *result = new tui_layout_split (m_vertical);
516 1.9 christos for (const split &item : m_splits)
517 1.9 christos {
518 1.9 christos std::unique_ptr<tui_layout_base> next = item.layout->clone ();
519 1.9 christos split s = {item.weight, std::move (next)};
520 1.9 christos result->m_splits.push_back (std::move (s));
521 1.9 christos }
522 1.9 christos return std::unique_ptr<tui_layout_base> (result);
523 1.9 christos }
524 1.9 christos
525 1.1 christos /* See tui-layout.h. */
526 1.9 christos
527 1.9 christos void
528 1.9 christos tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
529 1.9 christos {
530 1.9 christos *min_value = 0;
531 1.9 christos *max_value = 0;
532 1.9 christos bool first_time = true;
533 1.9 christos for (const split &item : m_splits)
534 1.9 christos {
535 1.9 christos int new_min, new_max;
536 1.9 christos item.layout->get_sizes (height, &new_min, &new_max);
537 1.9 christos /* For the mismatch case, the first time through we want to set
538 1.9 christos the min and max to the computed values -- the "first_time"
539 1.9 christos check here is just a funny way of doing that. */
540 1.9 christos if (height == m_vertical || first_time)
541 1.9 christos {
542 1.9 christos *min_value += new_min;
543 1.9 christos *max_value += new_max;
544 1.9 christos }
545 1.9 christos else
546 1.9 christos {
547 1.9 christos *min_value = std::max (*min_value, new_min);
548 1.9 christos *max_value = std::min (*max_value, new_max);
549 1.9 christos }
550 1.9 christos first_time = false;
551 1.9 christos }
552 1.1 christos }
553 1.9 christos
554 1.1 christos /* See tui-layout.h. */
555 1.9 christos
556 1.9 christos bool
557 1.1 christos tui_layout_split::top_boxed_p () const
558 1.9 christos {
559 1.9 christos if (m_splits.empty ())
560 1.9 christos return false;
561 1.1 christos return m_splits[0].layout->top_boxed_p ();
562 1.1 christos }
563 1.9 christos
564 1.1 christos /* See tui-layout.h. */
565 1.9 christos
566 1.9 christos bool
567 1.1 christos tui_layout_split::bottom_boxed_p () const
568 1.9 christos {
569 1.9 christos if (m_splits.empty ())
570 1.9 christos return false;
571 1.1 christos return m_splits.back ().layout->top_boxed_p ();
572 1.1 christos }
573 1.9 christos
574 1.1 christos /* See tui-layout.h. */
575 1.9 christos
576 1.9 christos void
577 1.1 christos tui_layout_split::set_weights_from_heights ()
578 1.9 christos {
579 1.9 christos for (int i = 0; i < m_splits.size (); ++i)
580 1.1 christos m_splits[i].weight = m_splits[i].layout->height;
581 1.1 christos }
582 1.9 christos
583 1.1 christos /* See tui-layout.h. */
584 1.9 christos
585 1.9 christos tui_adjust_result
586 1.1 christos tui_layout_split::adjust_size (const char *name, int new_height)
587 1.9 christos {
588 1.9 christos /* Look through the children. If one is a layout holding the named
589 1.9 christos window, we're done; or if one actually is the named window,
590 1.9 christos update it. */
591 1.9 christos int found_index = -1;
592 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
593 1.9 christos {
594 1.9 christos tui_adjust_result adjusted
595 1.9 christos = m_splits[i].layout->adjust_size (name, new_height);
596 1.9 christos if (adjusted == HANDLED)
597 1.9 christos return HANDLED;
598 1.9 christos if (adjusted == FOUND)
599 1.9 christos {
600 1.9 christos if (!m_vertical)
601 1.9 christos return FOUND;
602 1.9 christos found_index = i;
603 1.9 christos break;
604 1.9 christos }
605 1.1 christos }
606 1.9 christos
607 1.9 christos if (found_index == -1)
608 1.9 christos return NOT_FOUND;
609 1.9 christos if (m_splits[found_index].layout->height == new_height)
610 1.9 christos return HANDLED;
611 1.9 christos
612 1.9 christos set_weights_from_heights ();
613 1.9 christos int delta = m_splits[found_index].weight - new_height;
614 1.9 christos m_splits[found_index].weight = new_height;
615 1.9 christos
616 1.9 christos /* Distribute the "delta" over the next window; but if the next
617 1.9 christos window cannot hold it all, keep going until we either find a
618 1.9 christos window that does, or until we loop all the way around. */
619 1.9 christos for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
620 1.9 christos {
621 1.1 christos int index = (found_index + 1 + i) % m_splits.size ();
622 1.9 christos
623 1.9 christos int new_min, new_max;
624 1.1 christos m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
625 1.9 christos
626 1.9 christos if (delta < 0)
627 1.9 christos {
628 1.9 christos /* The primary window grew, so we are trying to shrink other
629 1.9 christos windows. */
630 1.9 christos int available = m_splits[index].weight - new_min;
631 1.9 christos int shrink_by = std::min (available, -delta);
632 1.9 christos m_splits[index].weight -= shrink_by;
633 1.9 christos delta += shrink_by;
634 1.1 christos }
635 1.1 christos else
636 1.9 christos {
637 1.9 christos /* The primary window shrank, so we are trying to grow other
638 1.9 christos windows. */
639 1.9 christos int available = new_max - m_splits[index].weight;
640 1.9 christos int grow_by = std::min (available, delta);
641 1.9 christos m_splits[index].weight += grow_by;
642 1.1 christos delta -= grow_by;
643 1.1 christos }
644 1.9 christos }
645 1.9 christos
646 1.9 christos if (delta != 0)
647 1.9 christos {
648 1.9 christos warning (_("Invalid window height specified"));
649 1.9 christos /* Effectively undo any modifications made here. */
650 1.9 christos set_weights_from_heights ();
651 1.9 christos }
652 1.9 christos else
653 1.9 christos {
654 1.9 christos /* Simply re-apply the updated layout. */
655 1.9 christos apply (x, y, width, height);
656 1.9 christos }
657 1.9 christos
658 1.1 christos return HANDLED;
659 1.1 christos }
660 1.9 christos
661 1.1 christos /* See tui-layout.h. */
662 1.9 christos
663 1.9 christos void
664 1.1 christos tui_layout_split::apply (int x_, int y_, int width_, int height_)
665 1.9 christos {
666 1.9 christos x = x_;
667 1.9 christos y = y_;
668 1.9 christos width = width_;
669 1.9 christos height = height_;
670 1.9 christos
671 1.9 christos struct size_info
672 1.9 christos {
673 1.9 christos int size;
674 1.9 christos int min_size;
675 1.9 christos int max_size;
676 1.9 christos /* True if this window will share a box border with the previous
677 1.9 christos window in the list. */
678 1.9 christos bool share_box;
679 1.9 christos };
680 1.9 christos
681 1.9 christos std::vector<size_info> info (m_splits.size ());
682 1.9 christos
683 1.9 christos /* Step 1: Find the min and max size of each sub-layout.
684 1.9 christos Fixed-sized layouts are given their desired size, and then the
685 1.9 christos remaining space is distributed among the remaining windows
686 1.9 christos according to the weights given. */
687 1.9 christos int available_size = m_vertical ? height : width;
688 1.9 christos int last_index = -1;
689 1.9 christos int total_weight = 0;
690 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
691 1.9 christos {
692 1.9 christos bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
693 1.9 christos
694 1.9 christos /* Always call get_sizes, to ensure that the window is
695 1.9 christos instantiated. This is a bit gross but less gross than adding
696 1.9 christos special cases for this in other places. */
697 1.9 christos m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
698 1.9 christos &info[i].max_size);
699 1.9 christos
700 1.9 christos if (!m_applied
701 1.9 christos && cmd_win_already_exists
702 1.9 christos && m_splits[i].layout->get_name () != nullptr
703 1.9 christos && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
704 1.9 christos {
705 1.9 christos /* If this layout has never been applied, then it means the
706 1.9 christos user just changed the layout. In this situation, it's
707 1.9 christos desirable to keep the size of the command window the
708 1.9 christos same. Setting the min and max sizes this way ensures
709 1.9 christos that the resizing step, below, does the right thing with
710 1.9 christos this window. */
711 1.9 christos info[i].min_size = (m_vertical
712 1.9 christos ? TUI_CMD_WIN->height
713 1.9 christos : TUI_CMD_WIN->width);
714 1.9 christos info[i].max_size = info[i].min_size;
715 1.9 christos }
716 1.9 christos
717 1.9 christos if (info[i].min_size == info[i].max_size)
718 1.1 christos available_size -= info[i].min_size;
719 1.9 christos else
720 1.9 christos {
721 1.9 christos last_index = i;
722 1.9 christos total_weight += m_splits[i].weight;
723 1.9 christos }
724 1.9 christos
725 1.9 christos /* Two adjacent boxed windows will share a border, making a bit
726 1.9 christos more size available. */
727 1.9 christos if (i > 0
728 1.9 christos && m_splits[i - 1].layout->bottom_boxed_p ()
729 1.9 christos && m_splits[i].layout->top_boxed_p ())
730 1.1 christos info[i].share_box = true;
731 1.9 christos }
732 1.9 christos
733 1.9 christos /* Step 2: Compute the size of each sub-layout. Fixed-sized items
734 1.9 christos are given their fixed size, while others are resized according to
735 1.9 christos their weight. */
736 1.9 christos int used_size = 0;
737 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
738 1.9 christos {
739 1.9 christos /* Compute the height and clamp to the allowable range. */
740 1.9 christos info[i].size = available_size * m_splits[i].weight / total_weight;
741 1.9 christos if (info[i].size > info[i].max_size)
742 1.9 christos info[i].size = info[i].max_size;
743 1.9 christos if (info[i].size < info[i].min_size)
744 1.9 christos info[i].size = info[i].min_size;
745 1.9 christos /* If there is any leftover size, just redistribute it to the
746 1.9 christos last resizeable window, by dropping it from the allocated
747 1.9 christos size. We could try to be fancier here perhaps, by
748 1.9 christos redistributing this size among all windows, not just the
749 1.9 christos last window. */
750 1.9 christos if (info[i].min_size != info[i].max_size)
751 1.9 christos {
752 1.9 christos used_size += info[i].size;
753 1.9 christos if (info[i].share_box)
754 1.9 christos --used_size;
755 1.1 christos }
756 1.1 christos }
757 1.9 christos
758 1.9 christos /* Allocate any leftover size. */
759 1.9 christos if (available_size >= used_size && last_index != -1)
760 1.9 christos info[last_index].size += available_size - used_size;
761 1.9 christos
762 1.9 christos /* Step 3: Resize. */
763 1.9 christos int size_accum = 0;
764 1.9 christos const int maximum = m_vertical ? height : width;
765 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
766 1.9 christos {
767 1.9 christos /* If we fall off the bottom, just make allocations overlap.
768 1.9 christos GIGO. */
769 1.9 christos if (size_accum + info[i].size > maximum)
770 1.9 christos size_accum = maximum - info[i].size;
771 1.9 christos else if (info[i].share_box)
772 1.9 christos --size_accum;
773 1.9 christos if (m_vertical)
774 1.1 christos m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
775 1.9 christos else
776 1.9 christos m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
777 1.1 christos size_accum += info[i].size;
778 1.1 christos }
779 1.9 christos
780 1.9 christos m_applied = true;
781 1.9 christos }
782 1.9 christos
783 1.9 christos /* See tui-layout.h. */
784 1.9 christos
785 1.9 christos void
786 1.9 christos tui_layout_split::remove_windows (const char *name)
787 1.9 christos {
788 1.1 christos for (int i = 0; i < m_splits.size (); ++i)
789 1.9 christos {
790 1.9 christos const char *this_name = m_splits[i].layout->get_name ();
791 1.9 christos if (this_name == nullptr)
792 1.9 christos m_splits[i].layout->remove_windows (name);
793 1.9 christos else if (strcmp (this_name, name) == 0
794 1.9 christos || strcmp (this_name, CMD_NAME) == 0
795 1.9 christos || strcmp (this_name, STATUS_NAME) == 0)
796 1.9 christos {
797 1.9 christos /* Keep. */
798 1.9 christos }
799 1.1 christos else
800 1.9 christos {
801 1.9 christos m_splits.erase (m_splits.begin () + i);
802 1.1 christos --i;
803 1.1 christos }
804 1.1 christos }
805 1.1 christos }
806 1.9 christos
807 1.9 christos /* See tui-layout.h. */
808 1.9 christos
809 1.9 christos void
810 1.9 christos tui_layout_split::replace_window (const char *name, const char *new_window)
811 1.9 christos {
812 1.9 christos for (auto &item : m_splits)
813 1.9 christos item.layout->replace_window (name, new_window);
814 1.9 christos }
815 1.9 christos
816 1.9 christos /* See tui-layout.h. */
817 1.9 christos
818 1.9 christos void
819 1.9 christos tui_layout_split::specification (ui_file *output, int depth)
820 1.9 christos {
821 1.9 christos if (depth > 0)
822 1.9 christos fputs_unfiltered ("{", output);
823 1.9 christos
824 1.9 christos if (!m_vertical)
825 1.9 christos fputs_unfiltered ("-horizontal ", output);
826 1.9 christos
827 1.9 christos bool first = true;
828 1.9 christos for (auto &item : m_splits)
829 1.9 christos {
830 1.9 christos if (!first)
831 1.9 christos fputs_unfiltered (" ", output);
832 1.9 christos first = false;
833 1.9 christos item.layout->specification (output, depth + 1);
834 1.9 christos fprintf_unfiltered (output, " %d", item.weight);
835 1.9 christos }
836 1.9 christos
837 1.9 christos if (depth > 0)
838 1.9 christos fputs_unfiltered ("}", output);
839 1.9 christos }
840 1.9 christos
841 1.9 christos /* Destroy the layout associated with SELF. */
842 1.9 christos
843 1.9 christos static void
844 1.9 christos destroy_layout (struct cmd_list_element *self, void *context)
845 1.9 christos {
846 1.9 christos tui_layout_split *layout = (tui_layout_split *) context;
847 1.9 christos size_t index = find_layout (layout);
848 1.9 christos layouts.erase (layouts.begin () + index);
849 1.9 christos }
850 1.9 christos
851 1.9 christos /* List holding the sub-commands of "layout". */
852 1.9 christos
853 1.9 christos static struct cmd_list_element *layout_list;
854 1.9 christos
855 1.9 christos /* Add a "layout" command with name NAME that switches to LAYOUT. */
856 1.9 christos
857 1.9 christos static struct cmd_list_element *
858 1.9 christos add_layout_command (const char *name, tui_layout_split *layout)
859 1.9 christos {
860 1.9 christos struct cmd_list_element *cmd;
861 1.9 christos
862 1.9 christos string_file spec;
863 1.9 christos layout->specification (&spec, 0);
864 1.9 christos
865 1.9 christos gdb::unique_xmalloc_ptr<char> doc
866 1.9 christos (xstrprintf (_("Apply the \"%s\" layout.\n\
867 1.9 christos This layout was created using:\n\
868 1.9 christos tui new-layout %s %s"),
869 1.9 christos name, name, spec.c_str ()));
870 1.9 christos
871 1.9 christos cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
872 1.9 christos set_cmd_context (cmd, layout);
873 1.9 christos /* There is no API to set this. */
874 1.9 christos cmd->func = tui_apply_layout;
875 1.9 christos cmd->destroyer = destroy_layout;
876 1.9 christos cmd->doc_allocated = 1;
877 1.9 christos doc.release ();
878 1.9 christos layouts.emplace_back (layout);
879 1.9 christos
880 1.9 christos return cmd;
881 1.9 christos }
882 1.9 christos
883 1.1 christos /* Initialize the standard layouts. */
884 1.1 christos
885 1.9 christos static void
886 1.9 christos initialize_layouts ()
887 1.9 christos {
888 1.9 christos tui_layout_split *layout;
889 1.9 christos
890 1.9 christos layout = new tui_layout_split ();
891 1.9 christos layout->add_window (SRC_NAME, 2);
892 1.9 christos layout->add_window (STATUS_NAME, 0);
893 1.9 christos layout->add_window (CMD_NAME, 1);
894 1.9 christos add_layout_command (SRC_NAME, layout);
895 1.9 christos
896 1.9 christos layout = new tui_layout_split ();
897 1.9 christos layout->add_window (DISASSEM_NAME, 2);
898 1.9 christos layout->add_window (STATUS_NAME, 0);
899 1.9 christos layout->add_window (CMD_NAME, 1);
900 1.9 christos add_layout_command (DISASSEM_NAME, layout);
901 1.9 christos
902 1.9 christos layout = new tui_layout_split ();
903 1.9 christos layout->add_window (SRC_NAME, 1);
904 1.9 christos layout->add_window (DISASSEM_NAME, 1);
905 1.9 christos layout->add_window (STATUS_NAME, 0);
906 1.9 christos layout->add_window (CMD_NAME, 1);
907 1.9 christos add_layout_command ("split", layout);
908 1.9 christos
909 1.9 christos layout = new tui_layout_split ();
910 1.9 christos layout->add_window (DATA_NAME, 1);
911 1.9 christos layout->add_window (SRC_NAME, 1);
912 1.9 christos layout->add_window (STATUS_NAME, 0);
913 1.9 christos layout->add_window (CMD_NAME, 1);
914 1.9 christos layouts.emplace_back (layout);
915 1.9 christos src_regs_layout = layout;
916 1.9 christos
917 1.9 christos layout = new tui_layout_split ();
918 1.9 christos layout->add_window (DATA_NAME, 1);
919 1.9 christos layout->add_window (DISASSEM_NAME, 1);
920 1.9 christos layout->add_window (STATUS_NAME, 0);
921 1.9 christos layout->add_window (CMD_NAME, 1);
922 1.9 christos layouts.emplace_back (layout);
923 1.9 christos asm_regs_layout = layout;
924 1.9 christos }
925 1.9 christos
926 1.9 christos
927 1.9 christos
929 1.1 christos /* A helper function that returns true if NAME is the name of an
930 1.9 christos available window. */
931 1.9 christos
932 1.9 christos static bool
933 1.9 christos validate_window_name (const std::string &name)
934 1.9 christos {
935 1.1 christos auto iter = known_window_types->find (name);
936 1.1 christos return iter != known_window_types->end ();
937 1.9 christos }
938 1.1 christos
939 1.1 christos /* Implementation of the "tui new-layout" command. */
940 1.9 christos
941 1.1 christos static void
942 1.9 christos tui_new_layout_command (const char *spec, int from_tty)
943 1.9 christos {
944 1.9 christos std::string new_name = extract_arg (&spec);
945 1.9 christos if (new_name.empty ())
946 1.9 christos error (_("No layout name specified"));
947 1.9 christos if (new_name[0] == '-')
948 1.9 christos error (_("Layout name cannot start with '-'"));
949 1.9 christos
950 1.9 christos bool is_vertical = true;
951 1.9 christos spec = skip_spaces (spec);
952 1.9 christos if (check_for_argument (&spec, "-horizontal"))
953 1.9 christos is_vertical = false;
954 1.9 christos
955 1.9 christos std::vector<std::unique_ptr<tui_layout_split>> splits;
956 1.9 christos splits.emplace_back (new tui_layout_split (is_vertical));
957 1.1 christos std::unordered_set<std::string> seen_windows;
958 1.9 christos while (true)
959 1.9 christos {
960 1.9 christos spec = skip_spaces (spec);
961 1.1 christos if (spec[0] == '\0')
962 1.9 christos break;
963 1.9 christos
964 1.9 christos if (spec[0] == '{')
965 1.9 christos {
966 1.9 christos is_vertical = true;
967 1.9 christos spec = skip_spaces (spec + 1);
968 1.9 christos if (check_for_argument (&spec, "-horizontal"))
969 1.9 christos is_vertical = false;
970 1.9 christos splits.emplace_back (new tui_layout_split (is_vertical));
971 1.1 christos continue;
972 1.9 christos }
973 1.9 christos
974 1.9 christos bool is_close = false;
975 1.9 christos std::string name;
976 1.9 christos if (spec[0] == '}')
977 1.9 christos {
978 1.9 christos is_close = true;
979 1.9 christos ++spec;
980 1.9 christos if (splits.size () == 1)
981 1.1 christos error (_("Extra '}' in layout specification"));
982 1.9 christos }
983 1.9 christos else
984 1.9 christos {
985 1.9 christos name = extract_arg (&spec);
986 1.9 christos if (name.empty ())
987 1.9 christos break;
988 1.9 christos if (!validate_window_name (name))
989 1.9 christos error (_("Unknown window \"%s\""), name.c_str ());
990 1.9 christos if (seen_windows.find (name) != seen_windows.end ())
991 1.1 christos error (_("Window \"%s\" seen twice in layout"), name.c_str ());
992 1.9 christos }
993 1.9 christos
994 1.9 christos ULONGEST weight = get_ulongest (&spec, '}');
995 1.9 christos if ((int) weight != weight)
996 1.1 christos error (_("Weight out of range: %s"), pulongest (weight));
997 1.9 christos if (is_close)
998 1.9 christos {
999 1.9 christos std::unique_ptr<tui_layout_split> last_split
1000 1.9 christos = std::move (splits.back ());
1001 1.1 christos splits.pop_back ();
1002 1.1 christos splits.back ()->add_split (std::move (last_split), weight);
1003 1.1 christos }
1004 1.9 christos else
1005 1.9 christos {
1006 1.1 christos splits.back ()->add_window (name.c_str (), weight);
1007 1.1 christos seen_windows.insert (name);
1008 1.9 christos }
1009 1.9 christos }
1010 1.9 christos if (splits.size () > 1)
1011 1.9 christos error (_("Missing '}' in layout specification"));
1012 1.9 christos if (seen_windows.empty ())
1013 1.9 christos error (_("New layout does not contain any windows"));
1014 1.9 christos if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1015 1.9 christos error (_("New layout does not contain the \"" CMD_NAME "\" window"));
1016 1.9 christos
1017 1.9 christos gdb::unique_xmalloc_ptr<char> cmd_name
1018 1.9 christos = make_unique_xstrdup (new_name.c_str ());
1019 1.9 christos std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
1020 1.9 christos struct cmd_list_element *cmd
1021 1.9 christos = add_layout_command (cmd_name.get (), new_layout.get ());
1022 1.9 christos cmd->name_allocated = 1;
1023 1.9 christos cmd_name.release ();
1024 1.9 christos new_layout.release ();
1025 1.9 christos }
1026 1.9 christos
1027 1.9 christos /* Function to initialize gdb commands, for tui window layout
1028 1.9 christos manipulation. */
1029 1.9 christos
1030 1.9 christos void _initialize_tui_layout ();
1031 1.9 christos void
1032 1.9 christos _initialize_tui_layout ()
1033 1.9 christos {
1034 1.9 christos add_basic_prefix_cmd ("layout", class_tui, _("\
1035 1.9 christos Change the layout of windows.\n\
1036 1.9 christos Usage: layout prev | next | LAYOUT-NAME"),
1037 1.9 christos &layout_list, "layout ", 0, &cmdlist);
1038 1.9 christos
1039 1.9 christos add_cmd ("next", class_tui, tui_next_layout_command,
1040 1.9 christos _("Apply the next TUI layout."),
1041 1.9 christos &layout_list);
1042 1.9 christos add_cmd ("prev", class_tui, tui_prev_layout_command,
1043 1.9 christos _("Apply the previous TUI layout."),
1044 1.9 christos &layout_list);
1045 1.9 christos add_cmd ("regs", class_tui, tui_regs_layout_command,
1046 1.9 christos _("Apply the TUI register layout."),
1047 1.9 christos &layout_list);
1048 1.9 christos
1049 1.9 christos add_cmd ("new-layout", class_tui, tui_new_layout_command,
1050 1.9 christos _("Create a new TUI layout.\n\
1051 1.9 christos Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1052 1.9 christos Create a new TUI layout. The new layout will be named NAME,\n\
1053 1.9 christos and can be accessed using \"layout NAME\".\n\
1054 1.9 christos The windows will be displayed in the specified order.\n\
1055 1.9 christos A WINDOW can also be of the form:\n\
1056 1.9 christos { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1057 1.9 christos This form indicates a sub-frame.\n\
1058 1.9 christos Each WEIGHT is an integer, which holds the relative size\n\
1059 1.9 christos to be allocated to the window."),
1060 1.9 christos tui_get_cmd_list ());
1061 1.9 christos
1062 1.1 christos initialize_layouts ();
1063 initialize_known_windows ();
1064 }
1065