-(defvar desktop--reuse-list nil
- "Internal use only.")
-
-(defun desktop--compute-pos (value left/top right/bottom)
- (pcase value
- (`(+ ,val) (+ left/top val))
- (`(- ,val) (+ right/bottom val))
- (val val)))
-
-(defun desktop--move-onscreen (frame)
- "If FRAME is offscreen, move it back onscreen and, if necessary, resize it.
-When forced onscreen, frames wider than the monitor's workarea are converted
-to fullwidth, and frames taller than the workarea are converted to fullheight.
-NOTE: This only works for non-iconified frames."
- (pcase-let* ((`(,left ,top ,width ,height) (cl-cdadr (frame-monitor-attributes frame)))
- (right (+ left width -1))
- (bottom (+ top height -1))
- (fr-left (desktop--compute-pos (frame-parameter frame 'left) left right))
- (fr-top (desktop--compute-pos (frame-parameter frame 'top) top bottom))
- (ch-width (frame-char-width frame))
- (ch-height (frame-char-height frame))
- (fr-width (max (frame-pixel-width frame) (* ch-width (frame-width frame))))
- (fr-height (max (frame-pixel-height frame) (* ch-height (frame-height frame))))
- (fr-right (+ fr-left fr-width -1))
- (fr-bottom (+ fr-top fr-height -1)))
- (when (pcase desktop-restore-forces-onscreen
- ;; Any corner is outside the screen.
- (`all (or (< fr-bottom top) (> fr-bottom bottom)
- (< fr-left left) (> fr-left right)
- (< fr-right left) (> fr-right right)
- (< fr-top top) (> fr-top bottom)))
- ;; Displaced to the left, right, above or below the screen.
- (`t (or (> fr-left right)
- (< fr-right left)
- (> fr-top bottom)
- (< fr-bottom top)))
- (_ nil))
- (let ((fullwidth (> fr-width width))
- (fullheight (> fr-height height))
- (params nil))
- ;; Position frame horizontally.
- (cond (fullwidth
- (push `(left . ,left) params))
- ((> fr-right right)
- (push `(left . ,(+ left (- width fr-width))) params))
- ((< fr-left left)
- (push `(left . ,left) params)))
- ;; Position frame vertically.
- (cond (fullheight
- (push `(top . ,top) params))
- ((> fr-bottom bottom)
- (push `(top . ,(+ top (- height fr-height))) params))
- ((< fr-top top)
- (push `(top . ,top) params)))
- ;; Compute fullscreen state, if required.
- (when (or fullwidth fullheight)
- (push (cons 'fullscreen
- (cond ((not fullwidth) 'fullheight)
- ((not fullheight) 'fullwidth)
- (t 'maximized)))
- params))
- ;; Finally, move the frame back onscreen.
- (when params
- (modify-frame-parameters frame params))))))
-
-(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'."
- (cl-find-if (lambda (frame)
- (and (equal (frame-parameter frame 'display) display)
- (or (null predicate)
- (apply predicate frame args))))
- desktop--reuse-list))
-
-(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 ((null display)
- ;; When the target is tty, every existing frame is reusable.
- (setq frame (desktop--find-frame nil display)))
- ((car (setq mini (cdr (assq 'desktop--mini frame-cfg))))
- ;; 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).
- (setq frame (desktop--find-frame
- (lambda (f m)
- (equal (frame-parameter f 'desktop--mini) m))
- display mini))
- ;; If it has not been loaded, and it is not a minibuffer-only frame,
- ;; let's look for an existing non-minibuffer-only frame to reuse.
- (unless (or frame (eq (cdr (assq 'minibuffer frame-cfg)) 'only))
- (setq frame (desktop--find-frame
- (lambda (f)
- (let ((w (frame-parameter f 'minibuffer)))
- (and (window-live-p w)
- (window-minibuffer-p w)
- (eq (window-frame w) f))))
- display))))
- (mini
- ;; For minibufferless frames, check whether they already exist,
- ;; and that they are linked to the right minibuffer frame.
- (setq frame (desktop--find-frame
- (lambda (f n)
- (pcase-let (((and m `(,hasmini ,num))
- (frame-parameter f 'desktop--mini)))
- (and m
- (null hasmini)
- (= num n)
- (equal (cl-second (frame-parameter
- (window-frame (minibuffer-window f))
- 'desktop--mini))
- n))))
- display (cl-second mini))))
- (t
- ;; Default to just finding a frame in the same display.
- (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)))
- (setq filtered-cfg (cl-delete-if (lambda (p)
- (memq p '(visibility fullscreen width height)))
- filtered-cfg :key #'car))
- (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 find or create a frame an apply the big bunch of parameters.
- ;; If a frame needs to be created and it falls partially or wholly offscreen,
- ;; sometimes it gets "pushed back" onscreen; however, moving it afterwards is
- ;; allowed. So we create the frame as invisible and then reapply the full
- ;; parameter list (including position and size parameters).
- (setq frame (or (desktop--select-frame display filtered-cfg)
- (make-frame-on-display display
- (cons '(visibility)
- (cl-loop
- for param in '(left top width height)
- collect (assq param filtered-cfg))))))
- (modify-frame-parameters frame
- (if (eq (frame-parameter frame 'fullscreen) fullscreen)
- ;; Workaround for bug#14949
- (assq-delete-all 'fullscreen filtered-cfg)
- filtered-cfg))
-
- ;; If requested, force frames to be onscreen.
- (when (and desktop-restore-forces-onscreen
- ;; FIXME: iconified frames should be checked too,
- ;; but it is impossible without deiconifying them.
- (not (eq (frame-parameter frame 'visibility) 'icon)))
- (desktop--move-onscreen frame))
-
- ;; 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
- (pcase-let ((`(,_p1 ,hasmini1 ,num1 ,default1) (assq 'desktop--mini (car state1)))
- (`(,_p2 ,hasmini2 ,num2 ,default2) (assq 'desktop--mini (car state2))))
- (cond (default1 t)
- (default2 nil)
- ((eq hasmini1 hasmini2) (< num1 num2))
- (t hasmini1))))
-
-(defun desktop-restoring-frames-p ()
- "True if calling `desktop-restore-frames' will actually restore frames."
- (and desktop-restore-frames desktop-saved-frame-states t))
-
-(defun desktop-restore-frames ()
- "Restore window/frame configuration.
-This function depends on the value of `desktop-saved-frame-states'