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