92
92
(require 'json )
93
93
(require 'bindat )
94
94
(require 'cl-lib )
95
+ (require 'cl-seq )
96
+ (eval-when-compile (require 'pcase ))
95
97
96
98
(declare-function speedbar-change-initial-expansion-list
97
99
" speedbar" (new-default))
@@ -253,6 +255,27 @@ Possible values are these symbols:
253
255
disposition of output generated by commands that
254
256
gdb mode sends to gdb on its own behalf." )
255
257
258
+ (defvar gdb--window-configuration-before nil
259
+ " Stores the window configuration before starting GDB." )
260
+
261
+ (defcustom gdb-restore-window-configuration-after-quit nil
262
+ " If non-nil, restore window configuration as of before GDB started.
263
+
264
+ Possible values are:
265
+ t -- Always restore.
266
+ nil -- Don't restore.
267
+ `if-gdb-show-main' -- Restore only if variable `gdb-show-main'
268
+ is non-nil
269
+ `if-gdb-many-windows' -- Restore only if variable `gdb-many-windows'
270
+ is non-nil."
271
+ :type '(choice
272
+ (const :tag " Always restore" t )
273
+ (const :tag " Don't restore" nil )
274
+ (const :tag " Depends on `gdb-show-main' " 'if-gdb-show-main )
275
+ (const :tag " Depends on `gdb-many-windows' " 'if-gdb-many-windows ))
276
+ :group 'gdb
277
+ :version " 28.1" )
278
+
256
279
(defcustom gdb-discard-unordered-replies t
257
280
" Non-nil means discard any out-of-order GDB replies.
258
281
This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +626,25 @@ Also display the main routine in the disassembly buffer if present."
603
626
:group 'gdb
604
627
:version " 22.1" )
605
628
629
+ (defcustom gdb-window-configuration-directory user-emacs-directory
630
+ " Directory where GDB window configuration files are stored.
631
+ If nil, use `default-directory' ."
632
+ :type 'string
633
+ :group 'gdb
634
+ :version " 28.1" )
635
+
636
+ (defcustom gdb-default-window-configuration-file nil
637
+ " If non-nil, load this window configuration (layout) on startup.
638
+ This should be the full name of the window configuration file.
639
+ If this is not an absolute path, GDB treats it as a relative path
640
+ and looks under `gdb-window-configuration-directory' .
641
+
642
+ Note that this variable only takes effect when variable
643
+ `gdb-many-windows' is t."
644
+ :type 'string
645
+ :group 'gdb
646
+ :version " 28.1" )
647
+
606
648
(defvar gdbmi-debug-mode nil
607
649
" When non-nil, print the messages sent/received from GDB/MI in *Messages*." )
608
650
@@ -761,6 +803,12 @@ detailed description of this mode.
761
803
(gdb-restore-windows )
762
804
(error
763
805
" Multiple debugging requires restarting in text command mode" ))
806
+
807
+ ; ; Save window configuration before starting gdb so we can restore
808
+ ; ; it after gdb quits. Save it regardless of the value of
809
+ ; ; `gdb-restore-window-configuration-after-quit' .
810
+ (setq gdb--window-configuration-before (window-state-get ))
811
+
764
812
; ;
765
813
(gud-common-init command-line nil 'gud-gdbmi-marker-filter )
766
814
@@ -4494,6 +4542,26 @@ SPLIT-HORIZONTAL and show BUF in the new window."
4494
4542
(define-key gud-menu-map [displays]
4495
4543
`(menu-item " GDB-Windows" , menu
4496
4544
:visible (eq gud-minor-mode 'gdbmi )))
4545
+ (define-key menu [gdb-restore-windows]
4546
+ '(menu-item " Restore Initial Layout" gdb-restore-windows
4547
+ :help " Restore the initial GDB window layout." ))
4548
+ ; ; Window layout vs window configuration: We use "window layout" in
4549
+ ; ; GDB UI. Internally we refer to "window configuration" because
4550
+ ; ; that's the data structure used to store window layouts. Though
4551
+ ; ; bare in mind that there is a small difference between what we
4552
+ ; ; store and what normal window configuration functions
4553
+ ; ; output. Because GDB buffers (source, local, breakpoint, etc) are
4554
+ ; ; different between each debugging sessions, simply save/load
4555
+ ; ; window configurations doesn't
4556
+ ; ; work. `gdb-save-window-configuration' and
4557
+ ; ; `gdb-load-window-configuration' do some tricks to store and
4558
+ ; ; recreate each buffer in the layout.
4559
+ (define-key menu [load-layout] '(" Load Layout" " Load GDB window configuration (layout) from a file" . gdb-load-window-configuration))
4560
+ (define-key menu [save-layout] '(" Save Layout" " Save current GDB window configuration (layout) to a file" . gdb-save-window-configuration))
4561
+ (define-key menu [restore-layout-after-quit]
4562
+ '(menu-item " Restore Layout After Quit" gdb-toggle-restore-window-configuration
4563
+ :button (:toggle . gdb-restore-window-configuration-after-quit)
4564
+ :help " Toggle between always restore the window configuration (layout) after GDB quits and never restore.\n You can also change this setting in Customize to conditionally restore." ))
4497
4565
(define-key menu [gdb] '(" Gdb" . gdb-display-gdb-buffer))
4498
4566
(define-key menu [threads] '(" Threads" . gdb-display-threads-buffer))
4499
4567
(define-key menu [memory] '(" Memory" . gdb-display-memory-buffer))
@@ -4532,9 +4600,6 @@ SPLIT-HORIZONTAL and show BUF in the new window."
4532
4600
'(menu-item " Display Other Windows" gdb-many-windows
4533
4601
:help " Toggle display of locals, stack and breakpoint information"
4534
4602
:button (:toggle . gdb-many-windows)))
4535
- (define-key menu [gdb-restore-windows]
4536
- '(menu-item " Restore Window Layout" gdb-restore-windows
4537
- :help " Restore standard layout for debug session." ))
4538
4603
(define-key menu [sep1]
4539
4604
'(menu-item " --" ))
4540
4605
(define-key menu [all-threads]
@@ -4609,41 +4674,172 @@ window is dedicated."
4609
4674
(set-window-buffer window (get-buffer name))
4610
4675
(set-window-dedicated-p window t ))
4611
4676
4677
+ (defun gdb-toggle-restore-window-configuration ()
4678
+ " Toggle whether to restore window configuration when GDB quits."
4679
+ (interactive )
4680
+ (setq gdb-restore-window-configuration-after-quit
4681
+ (not gdb-restore-window-configuration-after-quit)))
4682
+
4683
+ (defun gdb-get-source-buffer ()
4684
+ " Return a buffer displaying source file or nil if we can't find one.
4685
+ The source file is the file that contains the source location
4686
+ where GDB stops. There could be multiple source files during a
4687
+ debugging session, we get the most recently showed one. If
4688
+ program hasn't started running yet, the source file is the \" main
4689
+ file\" where the GDB session starts (see `gdb-main-file' )."
4690
+ (if gud-last-last-frame
4691
+ (gud-find-file (car gud-last-last-frame))
4692
+ (when gdb-main-file
4693
+ (gud-find-file gdb-main-file))))
4694
+
4612
4695
(defun gdb-setup-windows ()
4613
- " Layout the window pattern for option `gdb-many-windows' ."
4614
- (gdb-get-buffer-create 'gdb-locals-buffer )
4615
- (gdb-get-buffer-create 'gdb-stack-buffer )
4616
- (gdb-get-buffer-create 'gdb-breakpoints-buffer )
4617
- (set-window-dedicated-p (selected-window ) nil )
4618
- (switch-to-buffer gud-comint-buffer)
4619
- (delete-other-windows )
4620
- (let ((win0 (selected-window ))
4621
- (win1 (split-window nil ( / ( * (window-height ) 3 ) 4 )))
4622
- (win2 (split-window nil ( / (window-height ) 3 )))
4623
- (win3 (split-window-right )))
4624
- (gdb-set-window-buffer (gdb-locals-buffer-name ) nil win3)
4625
- (select-window win2)
4626
- (set-window-buffer
4627
- win2
4628
- (if gud-last-last-frame
4629
- (gud-find-file (car gud-last-last-frame))
4630
- (if gdb-main-file
4631
- (gud-find-file gdb-main-file)
4632
- ; ; Put buffer list in window if we
4633
- ; ; can't find a source file.
4634
- (list-buffers-noselect ))))
4635
- (setq gdb-source-window (selected-window ))
4636
- (let ((win4 (split-window-right )))
4637
- (gdb-set-window-buffer
4638
- (gdb-get-buffer-create 'gdb-inferior-io ) nil win4))
4639
- (select-window win1)
4640
- (gdb-set-window-buffer (gdb-stack-buffer-name ))
4641
- (let ((win5 (split-window-right )))
4642
- (gdb-set-window-buffer (if gdb-show-threads-by-default
4643
- (gdb-threads-buffer-name )
4644
- (gdb-breakpoints-buffer-name ))
4645
- nil win5))
4646
- (select-window win0)))
4696
+ " Lay out the window pattern for option `gdb-many-windows' ."
4697
+ (if gdb-default-window-configuration-file
4698
+ (gdb-load-window-configuration
4699
+ (if (file-name-absolute-p gdb-default-window-configuration-file)
4700
+ gdb-default-window-configuration-file
4701
+ (expand-file-name gdb-default-window-configuration-file
4702
+ gdb-window-configuration-directory)))
4703
+ ; ; Create default layout as before.
4704
+ (gdb-get-buffer-create 'gdb-locals-buffer )
4705
+ (gdb-get-buffer-create 'gdb-stack-buffer )
4706
+ (gdb-get-buffer-create 'gdb-breakpoints-buffer )
4707
+ (set-window-dedicated-p (selected-window ) nil )
4708
+ (switch-to-buffer gud-comint-buffer)
4709
+ (delete-other-windows )
4710
+ (let ((win0 (selected-window ))
4711
+ (win1 (split-window nil ( / ( * (window-height ) 3 ) 4 )))
4712
+ (win2 (split-window nil ( / (window-height ) 3 )))
4713
+ (win3 (split-window-right )))
4714
+ (gdb-set-window-buffer (gdb-locals-buffer-name ) nil win3)
4715
+ (select-window win2)
4716
+ (set-window-buffer win2 (or (gdb-get-source-buffer)
4717
+ (list-buffers-noselect )))
4718
+ (setq gdb-source-window (selected-window ))
4719
+ (let ((win4 (split-window-right )))
4720
+ (gdb-set-window-buffer
4721
+ (gdb-get-buffer-create 'gdb-inferior-io ) nil win4))
4722
+ (select-window win1)
4723
+ (gdb-set-window-buffer (gdb-stack-buffer-name ))
4724
+ (let ((win5 (split-window-right )))
4725
+ (gdb-set-window-buffer (if gdb-show-threads-by-default
4726
+ (gdb-threads-buffer-name )
4727
+ (gdb-breakpoints-buffer-name ))
4728
+ nil win5))
4729
+ (select-window win0))))
4730
+
4731
+ (defun gdb-buffer-p (buffer )
4732
+ " Return t if BUFFER is GDB-related."
4733
+ (with-current-buffer buffer
4734
+ (eq gud-minor-mode 'gdbmi )))
4735
+
4736
+ (defun gdb-function-buffer-p (buffer )
4737
+ " Return t if BUFFER is a GDB function buffer.
4738
+
4739
+ Function buffers are locals buffer, registers buffer, etc, but
4740
+ not including main command buffer (the one where you type GDB
4741
+ commands) or source buffers (that display program source code)."
4742
+ (with-current-buffer buffer
4743
+ (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode )))
4744
+
4745
+ (defun gdb--buffer-type (buffer )
4746
+ " Return the type of BUFFER if it is a function buffer.
4747
+ Buffer type is like `gdb-registers-type' , `gdb-stack-buffer' .
4748
+ These symbols are used by `gdb-get-buffer-create' .
4749
+
4750
+ Return nil if BUFFER is not a GDB function buffer."
4751
+ (with-current-buffer buffer
4752
+ (cl-loop for rule in gdb-buffer-rules
4753
+ for mode-name = (gdb-rules-buffer-mode rule)
4754
+ for type = (car rule)
4755
+ if (eq mode-name major-mode)
4756
+ return type
4757
+ finally return nil )))
4758
+
4759
+ (defun gdb-save-window-configuration (file )
4760
+ " Save current window configuration (layout) to FILE.
4761
+ You can later restore this configuration from that file by
4762
+ `gdb-load-window-configuration' ."
4763
+ (interactive (list (read-file-name
4764
+ " Save window configuration to file: "
4765
+ (or gdb-window-configuration-directory
4766
+ default-directory))))
4767
+ ; ; We replace the buffer in each window with a placeholder, store
4768
+ ; ; the buffer type (register, breakpoint, etc) in window parameters,
4769
+ ; ; and write the window configuration to the file.
4770
+ (save-window-excursion
4771
+ (let ((placeholder (get-buffer-create " *gdb-placeholder*" ))
4772
+ (window-persistent-parameters
4773
+ (cons '(gdb-buffer-type . writable) window-persistent-parameters)))
4774
+ (unwind-protect
4775
+ (dolist (win (window-list nil 'no-minibuffer ))
4776
+ (select-window win)
4777
+ (when (gdb-buffer-p (current-buffer ))
4778
+ (set-window-parameter
4779
+ nil 'gdb-buffer-type
4780
+ (cond ((gdb-function-buffer-p (current-buffer ))
4781
+ ; ; 1) If a user arranged the window
4782
+ ; ; configuration herself and saves it, windows
4783
+ ; ; are probably not dedicated. 2) We use the
4784
+ ; ; same dedication flag as in
4785
+ ; ; `gdb-display-buffer' .
4786
+ (set-window-dedicated-p nil t )
4787
+ ; ; We save this gdb-buffer-type symbol so
4788
+ ; ; we can later pass it to `gdb-get-buffer-create' ;
4789
+ ; ; one example: `gdb-registers-buffer' .
4790
+ (or (gdb--buffer-type (current-buffer ))
4791
+ (error " Unrecognized gdb buffer mode: %s " major-mode)))
4792
+ ; ; Command buffer.
4793
+ ((derived-mode-p 'gud-mode ) 'command )
4794
+ ((equal (selected-window ) gdb-source-window) 'source )))
4795
+ (with-window-non-dedicated nil
4796
+ (set-window-buffer nil placeholder)
4797
+ (set-window-prev-buffers (selected-window ) nil )
4798
+ (set-window-next-buffers (selected-window ) nil ))))
4799
+ ; ; Save the window configuration to FILE.
4800
+ (let ((window-config (window-state-get nil t )))
4801
+ (with-temp-buffer
4802
+ (prin1 window-config (current-buffer ))
4803
+ (write-file file t )))
4804
+ (kill-buffer placeholder)))))
4805
+
4806
+ (defun gdb-load-window-configuration (file )
4807
+ " Restore window configuration (layout) from FILE.
4808
+ FILE should be a window configuration file saved by
4809
+ `gdb-save-window-configuration' ."
4810
+ (interactive (list (read-file-name
4811
+ " Restore window configuration from file: "
4812
+ (or gdb-window-configuration-directory
4813
+ default-directory))))
4814
+ ; ; Basically, we restore window configuration and go through each
4815
+ ; ; window and restore the function buffers.
4816
+ (let* ((placeholder (get-buffer-create " *gdb-placeholder*" )))
4817
+ (unwind-protect ; Don't leak buffer.
4818
+ (let ((window-config (with-temp-buffer
4819
+ (insert-file-contents file)
4820
+ ; ; We need to go to point-min because
4821
+ ; ; `read' reads from point
4822
+ (goto-char (point-min ))
4823
+ (read (current-buffer ))))
4824
+ (source-buffer (or (gdb-get-source-buffer)
4825
+ ; ; Do the same thing as in
4826
+ ; ; `gdb-setup-windows' if no source
4827
+ ; ; buffer is found.
4828
+ (list-buffers-noselect )))
4829
+ buffer-type)
4830
+ (window-state-put window-config (frame-root-window ))
4831
+ (dolist (window (window-list nil 'no-minibuffer ))
4832
+ (with-selected-window window
4833
+ (setq buffer-type (window-parameter nil 'gdb-buffer-type ))
4834
+ (pcase buffer-type
4835
+ ('source (when source-buffer
4836
+ (set-window-buffer nil source-buffer)
4837
+ (setq gdb-source-window (selected-window ))))
4838
+ ('command (switch-to-buffer gud-comint-buffer))
4839
+ (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
4840
+ (with-window-non-dedicated nil
4841
+ (set-window-buffer nil buffer))))))))
4842
+ (kill-buffer placeholder))))
4647
4843
4648
4844
(define-minor-mode gdb-many-windows
4649
4845
" If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4661,7 +4857,12 @@ of the debugged program. Non-nil means display the layout shown for
4661
4857
4662
4858
(defun gdb-restore-windows ()
4663
4859
" Restore the basic arrangement of windows used by gdb.
4664
- This arrangement depends on the value of option `gdb-many-windows' ."
4860
+ This arrangement depends on the values of variable
4861
+ `gdb-many-windows' and `gdb-default-window-configuration-file' ."
4862
+ ; ; This function is used when the user messed up window
4863
+ ; ; configuration and wants to "reset to default". The function that
4864
+ ; ; sets up window configuration on start up is
4865
+ ; ; `gdb-get-source-file' .
4665
4866
(interactive )
4666
4867
(switch-to-buffer gud-comint-buffer) ; Select the right window and frame.
4667
4868
(delete-other-windows )
@@ -4708,11 +4909,25 @@ Kills the gdb buffers, and resets variables and the source buffers."
4708
4909
(if (boundp 'speedbar-frame ) (speedbar-timer-fn ))
4709
4910
(setq gud-running nil )
4710
4911
(setq gdb-active-process nil )
4711
- (remove-hook 'after-save-hook 'gdb-create-define-alist t ))
4912
+ (remove-hook 'after-save-hook 'gdb-create-define-alist t )
4913
+ ; ; Recover window configuration.
4914
+ (when (or (eq gdb-restore-window-configuration-after-quit t )
4915
+ (and (eq gdb-restore-window-configuration-after-quit
4916
+ 'if-gdb-show-main )
4917
+ gdb-show-main)
4918
+ (and (eq gdb-restore-window-configuration-after-quit
4919
+ 'if-gdb-many-windows )
4920
+ gdb-many-windows))
4921
+ (when gdb--window-configuration-before
4922
+ (window-state-put gdb--window-configuration-before)
4923
+ ; ; This way we don't accidentally restore an outdated window
4924
+ ; ; configuration.
4925
+ (setq gdb--window-configuration-before nil ))))
4712
4926
4713
4927
(defun gdb-get-source-file ()
4714
4928
" Find the source file where the program starts and display it with related
4715
4929
buffers, if required."
4930
+ ; ; This function is called only once on startup.
4716
4931
(goto-char (point-min ))
4717
4932
(if (re-search-forward gdb-source-file-regexp nil t )
4718
4933
(setq gdb-main-file (read (match-string 1 ))))
0 commit comments