-(defvar desktop--reuse-list nil
- "Internal use only.")
-
-(defun desktop--find-frame (predicate display &rest args)
- "Find a suitable frame in `desktop--reuse-list'.
-Look through frames whose display property matches DISPLAY and
-return the first one for which (PREDICATE frame ARGS) returns t.
-If PREDICATE is nil, it is always satisfied. Internal use only.
-This is an auxiliary function for `desktop--select-frame'."
- (catch :found
- (dolist (frame desktop--reuse-list)
- (when (and (equal (frame-parameter frame 'display) display)
- (or (null predicate)
- (apply predicate frame args)))
- (throw :found frame)))
- nil))
-
-(defun desktop--select-frame (display frame-cfg)
- "Look for an existing frame to reuse.
-DISPLAY is the display where the frame will be shown, and FRAME-CFG
-is the parameter list of the frame being restored. Internal use only."
- (if (eq desktop-restoring-reuses-frames t)
- (let ((frame nil)
- mini)
- ;; There are no fancy heuristics there. We could implement some
- ;; based on frame size and/or position, etc., but it is not clear
- ;; that any "gain" (in the sense of reduced flickering, etc.) is
- ;; worth the added complexity. In fact, the code below mainly
- ;; tries to work nicely when M-x desktop-read is used after a desktop
- ;; session has already been loaded. The other main use case, which
- ;; is the initial desktop-read upon starting Emacs, should usually
- ;; only have one, or very few, frame(s) to reuse.
- (cond (;; When the target is tty, every existing frame is reusable.
- (null display)
- (setq frame (desktop--find-frame nil display)))
- (;; If the frame has its own minibuffer, let's see whether
- ;; that frame has already been loaded (which can happen after
- ;; M-x desktop-read).
- (car (setq mini (cdr (assq 'desktop-mini frame-cfg))))
- (setq frame (or (desktop--find-frame
- (lambda (f m)
- (equal (frame-parameter f 'desktop-mini) m))
- display mini))))
- (;; For minibufferless frames, check whether they already exist,
- ;; and that they are linked to the right minibuffer frame.
- mini
- (setq frame (desktop--find-frame
- (lambda (f n)
- (let ((m (frame-parameter f 'desktop-mini)))
- (and m
- (null (car m))
- (= (cadr m) n)
- (equal (cadr (frame-parameter
- (window-frame (minibuffer-window f))
- 'desktop-mini))
- n))))
- display (cadr mini))))
- (;; Default to just finding a frame in the same display.
- t
- (setq frame (desktop--find-frame nil display))))
- ;; If found, remove from the list.
- (when frame
- (setq desktop--reuse-list (delq frame desktop--reuse-list)))
- frame)
- nil))
-
-(defun desktop--make-frame (frame-cfg window-cfg)
- "Set up a frame according to its saved state.
-That means either creating a new frame or reusing an existing one.
-FRAME-CFG is the parameter list of the new frame; WINDOW-CFG is
-its window state. Internal use only."
- (let* ((fullscreen (cdr (assq 'fullscreen frame-cfg)))
- (lines (assq 'tool-bar-lines frame-cfg))
- (filtered-cfg (desktop--filter-frame-parms frame-cfg nil))
- (display (cdr (assq 'display filtered-cfg))) ;; post-filtering
- alt-cfg frame)
-
- ;; This works around bug#14795 (or feature#14795, if not a bug :-)
- (setq filtered-cfg (assq-delete-all 'tool-bar-lines filtered-cfg))
- (push '(tool-bar-lines . 0) filtered-cfg)
-
- (when fullscreen
- ;; Currently Emacs has the limitation that it does not record the size
- ;; and position of a frame before maximizing it, so we cannot save &
- ;; restore that info. Instead, when restoring, we resort to creating
- ;; invisible "fullscreen" frames of default size and then maximizing them
- ;; (and making them visible) which at least is somewhat user-friendly
- ;; when these frames are later de-maximized.
- (let ((width (and (eq fullscreen 'fullheight) (cdr (assq 'width filtered-cfg))))
- (height (and (eq fullscreen 'fullwidth) (cdr (assq 'height filtered-cfg))))
- (visible (assq 'visibility filtered-cfg)))
- (dolist (parameter '(visibility fullscreen width height))
- (setq filtered-cfg (assq-delete-all parameter filtered-cfg)))
- (when width
- (setq filtered-cfg (append `((user-size . t) (width . ,width))
- filtered-cfg)))
- (when height
- (setq filtered-cfg (append `((user-size . t) (height . ,height))
- filtered-cfg)))
- ;; These are parameters to apply after creating/setting the frame.
- (push visible alt-cfg)
- (push (cons 'fullscreen fullscreen) alt-cfg)))
-
- ;; Time to select or create a frame an apply the big bunch of parameters
- (if (setq frame (desktop--select-frame display filtered-cfg))
- (modify-frame-parameters frame filtered-cfg)
- (setq frame (make-frame-on-display display filtered-cfg)))
-
- ;; Let's give the finishing touches (visibility, tool-bar, maximization).
- (when lines (push lines alt-cfg))
- (when alt-cfg (modify-frame-parameters frame alt-cfg))
- ;; Now restore window state.
- (window-state-put window-cfg (frame-root-window frame) 'safe)
- frame))
-
-(defun desktop--sort-states (state1 state2)
- ;; Order: default minibuffer frame
- ;; other frames with minibuffer, ascending ID
- ;; minibufferless frames, ascending ID
- (let ((dm1 (cdr (assq 'desktop-mini (car state1))))
- (dm2 (cdr (assq 'desktop-mini (car state2)))))
- (cond ((nth 2 dm1) t)
- ((nth 2 dm2) nil)
- ((null (car dm2)) t)
- ((null (car dm1)) nil)
- (t (< (cadr dm1) (cadr dm2))))))
-
-(defun desktop--restore-frames ()
- "Restore window/frame configuration.
-Internal use only."
- (when (and desktop-restore-frames desktop--saved-states)
- (let* ((frame-mb-map nil) ;; Alist of frames with their own minibuffer
- (visible nil)
- (delete-saved (eq desktop-restore-in-current-display 'delete))
- (forcing (not (desktop-restore-in-original-display-p)))
- (target (and forcing (cons 'display (frame-parameter nil 'display)))))
-
- ;; Sorting saved states allows us to easily restore minibuffer-owning frames
- ;; before minibufferless ones.
- (setq desktop--saved-states (sort desktop--saved-states #'desktop--sort-states))
- ;; Potentially all existing frames are reusable. Later we will decide which ones
- ;; to reuse, and how to deal with any leftover.
- (setq desktop--reuse-list (frame-list))
-
- (dolist (state desktop--saved-states)
- (condition-case err
- (let* ((frame-cfg (car state))
- (window-cfg (cdr state))
- (d-mini (cdr (assq 'desktop-mini frame-cfg)))
- num frame to-tty)
- ;; Only set target if forcing displays and the target display is different.
- (if (or (not forcing)
- (equal target (or (assq 'display frame-cfg) '(display . nil))))
- (setq desktop--target-display nil)
- (setq desktop--target-display target
- to-tty (null (cdr target))))
- ;; Time to restore frames and set up their minibuffers as they were.
- ;; We only skip a frame (thus deleting it) if either:
- ;; - we're switching displays, and the user chose the option to delete, or
- ;; - we're switching to tty, and the frame to restore is minibuffer-only.
- (unless (and desktop--target-display
- (or delete-saved
- (and to-tty
- (eq (cdr (assq 'minibuffer frame-cfg)) 'only))))
-
- ;; Restore minibuffers. Some of this stuff could be done in a filter
- ;; function, but it would be messy because restoring minibuffers affects
- ;; global state; it's best to do it here than add a bunch of global
- ;; variables to pass info back-and-forth to/from the filter function.
- (cond
- ((null d-mini)) ;; No desktop-mini. Process as normal frame.
- (to-tty) ;; Ignore minibuffer stuff and process as normal frame.
- ((car d-mini) ;; Frame has its own minibuffer (or it is minibuffer-only).
- (setq num (cadr d-mini))
- (when (eq (cdr (assq 'minibuffer frame-cfg)) 'only)
- (setq frame-cfg (append '((tool-bar-lines . 0) (menu-bar-lines . 0))
- frame-cfg))))
- (t ;; Frame depends on other frame's minibufer window.
- (let ((mb-frame (cdr (assq (cadr d-mini) frame-mb-map))))
- (unless (frame-live-p mb-frame)
- (error "Minibuffer frame %s not found" (cadr d-mini)))
- (let ((mb-param (assq 'minibuffer frame-cfg))
- (mb-window (minibuffer-window mb-frame)))
- (unless (and (window-live-p mb-window)
- (window-minibuffer-p mb-window))
- (error "Not a minibuffer window %s" mb-window))
- (if mb-param
- (setcdr mb-param mb-window)
- (push (cons 'minibuffer mb-window) frame-cfg))))))
- ;; OK, we're ready at last to create (or reuse) a frame and
- ;; restore the window config.
- (setq frame (desktop--make-frame frame-cfg window-cfg))
- ;; Set default-minibuffer if required.
- (when (nth 2 d-mini) (setq default-minibuffer-frame frame))
- ;; Store frame/NUM to assign to minibufferless frames.
- (when num (push (cons num frame) frame-mb-map))
- ;; Try to locate at least one visible frame.
- (when (and (not visible) (frame-visible-p frame))
- (setq visible frame))))
- (error
- (delay-warning 'desktop (error-message-string err) :error))))
-
- ;; Delete remaining frames, but do not fail if some resist being deleted.
- (unless (eq desktop-restoring-reuses-frames 'keep)
- (dolist (frame desktop--reuse-list)
- (ignore-errors (delete-frame frame))))
- (setq desktop--reuse-list nil)
- ;; Make sure there's at least one visible frame, and select it.
- (unless (or visible (daemonp))
- (setq visible (if (frame-live-p default-minibuffer-frame)
- default-minibuffer-frame
- (car (frame-list))))
- (make-frame-visible visible)
- (select-frame-set-input-focus visible)))))