I love Emacs GDB mode, but I always found it annoying that there is no given key binding (or function which could be directly mapped into a key binding) for switching between the different views given by gdb-many-windows. The usual window and buffer switching functions are insufficient because the GDB windows are flagged as dedicated (so switch-buffer refuses to swap them in place), and in the case of the Locals/Registers and Breakpoints/Threads windows, the buffer you want to visit doesn’t necessarily even exist until you click that button.
This went from mildly annoying to a major headache when I needed to run the debugger in a remote Emacs session, over SSH from Mac OS X’s Terminal.app which does not support xterm mouse emulation. So I wrote this gdb-select-window function and corresponding key bindings to finally get the desired behavior…
;; For the consistency of gdb-select-window's calling convention...
(defun gdb-comint-buffer-name ()
(buffer-name gud-comint-buffer))
(defun gdb-source-buffer-name ()
(buffer-name (window-buffer gdb-source-window)))
(defun gdb-select-window (header)
"Switch directly to the specified GDB window.
Moves the cursor to the requested window, switching between
`gdb-many-windows' \"tabs\" if necessary in order to get there.
Recognized window header names are: 'comint, 'locals, 'registers,
'stack, 'breakpoints, 'threads, and 'source."
(interactive "Sheader: ")
(let* ((header-alternate (case header
('locals 'registers)
('registers 'locals)
('breakpoints 'threads)
('threads 'breakpoints)))
(buffer (intern (concat "gdb-" (symbol-name header) "-buffer")))
(buffer-names (mapcar (lambda (header)
(funcall (intern (concat "gdb-"
(symbol-name header)
"-buffer-name"))))
(if (null header-alternate)
(list header)
(list header header-alternate))))
(window (if (eql header 'source)
gdb-source-window
(or (get-buffer-window (car buffer-names))
(when (not (null (cadr buffer-names)))
(get-buffer-window (cadr buffer-names)))))))
(when (not (null window))
(let ((was-dedicated (window-dedicated-p window)))
(select-window window)
(set-window-dedicated-p window nil)
(when (member header '(locals registers breakpoints threads))
(switch-to-buffer (gdb-get-buffer-create buffer))
(setq header-line-format (gdb-set-header buffer)))
(set-window-dedicated-p window was-dedicated))
t)))
;; Use global keybindings for the window selection functions so that they
;; work from the source window too...
(mapcar (lambda (setting)
(lexical-let ((key (car setting))
(header (cdr setting)))
(global-set-key (concat "\C-c\C-g" key) #'(lambda ()
(interactive)
(gdb-select-window header)))))
'(("c" . comint)
("l" . locals)
("r" . registers)
("u" . source)
("s" . stack)
("b" . breakpoints)
("t" . threads)))
Put this in your ~/.emacs.el or init.el and fire up gdb-many-windows, and then you’ll be able to quickly switch between the GDB windows with the following keyboard shortcuts:
- C-c C-g c — comint (GDB command) buffer
- C-c C-g l — locals buffer
- C-c C-g r — registers buffer
- C-c C-g u — source window
- C-c C-g s — stack buffer
- C-c C-g b — breakpoints buffer
- C-c C-g t — threads buffer