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