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