mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-19 18:13:55 +00:00
lisp/desktop.el: Move code related to saving frames to frameset.el.
Require frameset. (desktop-restore-frames): Doc fix. (desktop-restore-reuses-frames): Rename from desktop-restoring-reuses-frames. (desktop-saved-frameset): Rename from desktop-saved-frame-states. (desktop-clear): Clear frames too. (desktop-filter-parameters-alist): Set from frameset-filter-alist. (desktop--filter-tty*, desktop-save, desktop-read): Use frameset functions. (desktop-before-saving-frames-functions, desktop--filter-*-color) (desktop--filter-minibuffer, desktop--filter-restore-desktop-parm) (desktop--filter-save-desktop-parm, desktop--filter-iconified-position) (desktop-restore-in-original-display-p, desktop--filter-frame-parms) (desktop--process-minibuffer-frames, desktop-save-frames) (desktop--reuse-list, desktop--compute-pos, desktop--move-onscreen) (desktop--find-frame, desktop--select-frame, desktop--make-frame) (desktop--sort-states, desktop-restoring-frames-p) (desktop-restore-frames): Remove. Most code moved to frameset.el. (desktop-restoring-frameset-p, desktop-restore-frameset) (desktop--check-dont-save, desktop-save-frameset): New functions. (desktop--app-id): New constant. (desktop-first-buffer, desktop-buffer-ok-count) (desktop-buffer-fail-count): Move before first use. lisp/frameset.el: New file.
This commit is contained in:
parent
7ee0f047a3
commit
9421876de5
@ -1,3 +1,31 @@
|
||||
2013-08-02 Juanma Barranquero <lekktu@gmail.com>
|
||||
|
||||
Move code related to saving frames to frameset.el.
|
||||
* desktop.el: Require frameset.
|
||||
(desktop-restore-frames): Doc fix.
|
||||
(desktop-restore-reuses-frames): Rename from
|
||||
desktop-restoring-reuses-frames.
|
||||
(desktop-saved-frameset): Rename from desktop-saved-frame-states.
|
||||
(desktop-clear): Clear frames too.
|
||||
(desktop-filter-parameters-alist): Set from frameset-filter-alist.
|
||||
(desktop--filter-tty*, desktop-save, desktop-read):
|
||||
Use frameset functions.
|
||||
(desktop-before-saving-frames-functions, desktop--filter-*-color)
|
||||
(desktop--filter-minibuffer, desktop--filter-restore-desktop-parm)
|
||||
(desktop--filter-save-desktop-parm, desktop--filter-iconified-position)
|
||||
(desktop-restore-in-original-display-p, desktop--filter-frame-parms)
|
||||
(desktop--process-minibuffer-frames, desktop-save-frames)
|
||||
(desktop--reuse-list, desktop--compute-pos, desktop--move-onscreen)
|
||||
(desktop--find-frame, desktop--select-frame, desktop--make-frame)
|
||||
(desktop--sort-states, desktop-restoring-frames-p)
|
||||
(desktop-restore-frames): Remove. Most code moved to frameset.el.
|
||||
(desktop-restoring-frameset-p, desktop-restore-frameset)
|
||||
(desktop--check-dont-save, desktop-save-frameset): New functions.
|
||||
(desktop--app-id): New constant.
|
||||
(desktop-first-buffer, desktop-buffer-ok-count)
|
||||
(desktop-buffer-fail-count): Move before first use.
|
||||
* frameset.el: New file.
|
||||
|
||||
2013-08-01 Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
|
||||
* files.el: Use lexical-binding.
|
||||
|
612
lisp/desktop.el
612
lisp/desktop.el
@ -134,6 +134,7 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'frameset)
|
||||
|
||||
(defvar desktop-file-version "206"
|
||||
"Version number of desktop file format.
|
||||
@ -372,7 +373,7 @@ modes are restored automatically; they should not be listed here."
|
||||
:group 'desktop)
|
||||
|
||||
(defcustom desktop-restore-frames t
|
||||
"When non-nil, save window/frame configuration to desktop file."
|
||||
"When non-nil, save frames to desktop file."
|
||||
:type 'boolean
|
||||
:group 'desktop
|
||||
:version "24.4")
|
||||
@ -399,7 +400,7 @@ few pixels, especially near the right / bottom borders of the screen."
|
||||
:group 'desktop
|
||||
:version "24.4")
|
||||
|
||||
(defcustom desktop-restoring-reuses-frames t
|
||||
(defcustom desktop-restore-reuses-frames t
|
||||
"If t, restoring frames reuses existing frames.
|
||||
If nil, existing frames are deleted.
|
||||
If `keep', existing frames are kept and not reused."
|
||||
@ -409,13 +410,6 @@ If `keep', existing frames are kept and not reused."
|
||||
:group 'desktop
|
||||
:version "24.4")
|
||||
|
||||
(defcustom desktop-before-saving-frames-functions nil
|
||||
"Abnormal hook run before saving frames.
|
||||
Functions in this hook are called with one argument, a live frame."
|
||||
:type 'hook
|
||||
:group 'desktop
|
||||
:version "24.4")
|
||||
|
||||
(defcustom desktop-file-name-format 'absolute
|
||||
"Format in which desktop file names should be saved.
|
||||
Possible values are:
|
||||
@ -599,7 +593,7 @@ DIRNAME omitted or nil means use `desktop-dirname'."
|
||||
"Checksum of the last auto-saved contents of the desktop file.
|
||||
Used to avoid writing contents unchanged between auto-saves.")
|
||||
|
||||
(defvar desktop-saved-frame-states nil
|
||||
(defvar desktop-saved-frameset nil
|
||||
"Saved state of all frames.
|
||||
Only valid during frame saving & restoring; intended for internal use.")
|
||||
|
||||
@ -667,7 +661,17 @@ Furthermore, it clears the variables listed in `desktop-globals-to-clear'."
|
||||
(unless (or (eq (aref bufname 0) ?\s) ;; Don't kill internal buffers
|
||||
(string-match-p preserve-regexp bufname))
|
||||
(kill-buffer buffer)))))
|
||||
(delete-other-windows))
|
||||
(delete-other-windows)
|
||||
(let* ((this (selected-frame))
|
||||
(mini (window-frame (minibuffer-window this)))) ; in case they difer
|
||||
(dolist (frame (sort (frame-list) #'frameset-sort-frames-for-deletion))
|
||||
(condition-case err
|
||||
(unless (or (eq frame this)
|
||||
(eq frame mini)
|
||||
(frame-parameter frame 'desktop-dont-clear))
|
||||
(delete-frame frame))
|
||||
(error
|
||||
(delay-warning 'desktop (error-message-string err)))))))
|
||||
|
||||
;; ----------------------------------------------------------------------------
|
||||
(unless noninteractive
|
||||
@ -890,223 +894,41 @@ DIRNAME must be the directory in which the desktop file will be saved."
|
||||
|
||||
;; ----------------------------------------------------------------------------
|
||||
(defvar desktop-filter-parameters-alist
|
||||
'((background-color . desktop--filter-*-color)
|
||||
(buffer-list . t)
|
||||
(buffer-predicate . t)
|
||||
(buried-buffer-list . t)
|
||||
(desktop--font . desktop--filter-restore-desktop-parm)
|
||||
(desktop--fullscreen . desktop--filter-restore-desktop-parm)
|
||||
(desktop--height . desktop--filter-restore-desktop-parm)
|
||||
(desktop--width . desktop--filter-restore-desktop-parm)
|
||||
(font . desktop--filter-save-desktop-parm)
|
||||
(font-backend . t)
|
||||
(foreground-color . desktop--filter-*-color)
|
||||
(fullscreen . desktop--filter-save-desktop-parm)
|
||||
(height . desktop--filter-save-desktop-parm)
|
||||
(left . desktop--filter-iconified-position)
|
||||
(minibuffer . desktop--filter-minibuffer)
|
||||
(name . t)
|
||||
(outer-window-id . t)
|
||||
(parent-id . t)
|
||||
(top . desktop--filter-iconified-position)
|
||||
(tty . desktop--filter-tty*)
|
||||
(tty-type . desktop--filter-tty*)
|
||||
(width . desktop--filter-save-desktop-parm)
|
||||
(window-id . t)
|
||||
(window-system . t))
|
||||
(append '((font-backend . t)
|
||||
(name . t)
|
||||
(outer-window-id . t)
|
||||
(parent-id . t)
|
||||
(tty . desktop--filter-tty*)
|
||||
(tty-type . desktop--filter-tty*)
|
||||
(window-id . t)
|
||||
(window-system . t))
|
||||
frameset-filter-alist)
|
||||
"Alist of frame parameters and filtering functions.
|
||||
|
||||
Each element is a cons (PARAM . FILTER), where PARAM is a parameter
|
||||
name (a symbol identifying a frame parameter), and FILTER can be t
|
||||
\(meaning the parameter is removed from the parameter list on saving
|
||||
and restoring), or a function that will be called with three args:
|
||||
|
||||
CURRENT a cons (PARAM . VALUE), where PARAM is the one being
|
||||
filtered and VALUE is its current value
|
||||
PARAMETERS the complete alist of parameters being filtered
|
||||
SAVING non-nil if filtering before saving state, nil otherwise
|
||||
|
||||
The FILTER function must return:
|
||||
nil CURRENT is removed from the list
|
||||
t CURRENT is left as is
|
||||
(PARAM' . VALUE') replace CURRENT with this
|
||||
|
||||
Frame parameters not on this list are passed intact.")
|
||||
|
||||
(defvar desktop--target-display nil
|
||||
"Either (minibuffer . VALUE) or nil.
|
||||
This refers to the current frame config being processed inside
|
||||
`frame--restore-frames' and its auxiliary functions (like filtering).
|
||||
If nil, there is no need to change the display.
|
||||
If non-nil, display parameter to use when creating the frame.
|
||||
Internal use only.")
|
||||
|
||||
(defun desktop-switch-to-gui-p (parameters)
|
||||
"True when switching to a graphic display.
|
||||
Return t if PARAMETERS describes a text-only terminal and
|
||||
the target is a graphic display; otherwise return nil.
|
||||
Only meaningful when called from a filtering function in
|
||||
`desktop-filter-parameters-alist'."
|
||||
(and desktop--target-display ; we're switching
|
||||
(null (cdr (assq 'display parameters))) ; from a tty
|
||||
(cdr desktop--target-display))) ; to a GUI display
|
||||
|
||||
(defun desktop-switch-to-tty-p (parameters)
|
||||
"True when switching to a text-only terminal.
|
||||
Return t if PARAMETERS describes a graphic display and
|
||||
the target is a text-only terminal; otherwise return nil.
|
||||
Only meaningful when called from a filtering function in
|
||||
`desktop-filter-parameters-alist'."
|
||||
(and desktop--target-display ; we're switching
|
||||
(cdr (assq 'display parameters)) ; from a GUI display
|
||||
(null (cdr desktop--target-display)))) ; to a tty
|
||||
Its format is identical to `frameset-filter-alist' (which see).")
|
||||
|
||||
(defun desktop--filter-tty* (_current parameters saving)
|
||||
;; Remove tty and tty-type parameters when switching
|
||||
;; to a GUI frame.
|
||||
(or saving
|
||||
(not (desktop-switch-to-gui-p parameters))))
|
||||
(not (frameset-switch-to-gui-p parameters))))
|
||||
|
||||
(defun desktop--filter-*-color (current parameters saving)
|
||||
;; Remove (foreground|background)-color parameters
|
||||
;; when switching to a GUI frame if they denote an
|
||||
;; "unspecified" color.
|
||||
(or saving
|
||||
(not (desktop-switch-to-gui-p parameters))
|
||||
(not (stringp (cdr current)))
|
||||
(not (string-match-p "^unspecified-[fb]g$" (cdr current)))))
|
||||
(defun desktop--check-dont-save (frame)
|
||||
(not (frame-parameter frame 'desktop-dont-save)))
|
||||
|
||||
(defun desktop--filter-minibuffer (current _parameters saving)
|
||||
;; When minibuffer is a window, save it as minibuffer . t
|
||||
(or (not saving)
|
||||
(if (windowp (cdr current))
|
||||
'(minibuffer . t)
|
||||
t)))
|
||||
(defconst desktop--app-id `(desktop . ,desktop-file-version))
|
||||
|
||||
(defun desktop--filter-restore-desktop-parm (current parameters saving)
|
||||
;; When switching to a GUI frame, convert desktop--XXX parameter to XXX
|
||||
(or saving
|
||||
(not (desktop-switch-to-gui-p parameters))
|
||||
(let ((val (cdr current)))
|
||||
(if (eq val :desktop-processed)
|
||||
nil
|
||||
(cons (intern (substring (symbol-name (car current))
|
||||
9)) ;; (length "desktop--")
|
||||
val)))))
|
||||
|
||||
(defun desktop--filter-save-desktop-parm (current parameters saving)
|
||||
;; When switching to a tty frame, save parameter XXX as desktop--XXX so it
|
||||
;; can be restored in a subsequent GUI session, unless it already exists.
|
||||
(cond (saving t)
|
||||
((desktop-switch-to-tty-p parameters)
|
||||
(let ((sym (intern (format "desktop--%s" (car current)))))
|
||||
(if (assq sym parameters)
|
||||
nil
|
||||
(cons sym (cdr current)))))
|
||||
((desktop-switch-to-gui-p parameters)
|
||||
(let* ((dtp (assq (intern (format "desktop--%s" (car current)))
|
||||
parameters))
|
||||
(val (cdr dtp)))
|
||||
(if (eq val :desktop-processed)
|
||||
nil
|
||||
(setcdr dtp :desktop-processed)
|
||||
(cons (car current) val))))
|
||||
(t t)))
|
||||
|
||||
(defun desktop--filter-iconified-position (_current parameters saving)
|
||||
;; When saving an iconified frame, top & left are meaningless,
|
||||
;; so remove them to allow restoring to a default position.
|
||||
(not (and saving (eq (cdr (assq 'visibility parameters)) 'icon))))
|
||||
|
||||
(defun desktop-restore-in-original-display-p ()
|
||||
"True if saved frames' displays should be honored."
|
||||
(cond ((daemonp) t)
|
||||
((eq system-type 'windows-nt) nil)
|
||||
(t (null desktop-restore-in-current-display))))
|
||||
|
||||
(defun desktop--filter-frame-parms (parameters saving)
|
||||
"Filter frame parameters and return filtered list.
|
||||
PARAMETERS is a parameter alist as returned by `frame-parameters'.
|
||||
If SAVING is non-nil, filtering is happening before saving frame state;
|
||||
otherwise, filtering is being done before restoring frame state.
|
||||
Parameters are filtered according to the setting of
|
||||
`desktop-filter-parameters-alist' (which see).
|
||||
Internal use only."
|
||||
(let ((filtered nil))
|
||||
(dolist (param parameters)
|
||||
(let ((filter (cdr (assq (car param) desktop-filter-parameters-alist)))
|
||||
this)
|
||||
(cond (;; no filter: pass param
|
||||
(null filter)
|
||||
(push param filtered))
|
||||
(;; filter = t; skip param
|
||||
(eq filter t))
|
||||
(;; filter func returns nil: skip param
|
||||
(null (setq this (funcall filter param parameters saving))))
|
||||
(;; filter func returns t: pass param
|
||||
(eq this t)
|
||||
(push param filtered))
|
||||
(;; filter func returns a new param: use it
|
||||
t
|
||||
(push this filtered)))))
|
||||
;; Set the display parameter after filtering, so that filter functions
|
||||
;; have access to its original value.
|
||||
(when desktop--target-display
|
||||
(let ((display (assq 'display filtered)))
|
||||
(if display
|
||||
(setcdr display (cdr desktop--target-display))
|
||||
(push desktop--target-display filtered))))
|
||||
filtered))
|
||||
|
||||
(defun desktop--process-minibuffer-frames (frames)
|
||||
;; Adds a desktop--mini parameter to frames
|
||||
;; desktop--mini is a list (MINIBUFFER NUMBER DEFAULT?) where
|
||||
;; MINIBUFFER t if the frame (including minibuffer-only) owns a minibuffer
|
||||
;; NUMBER if MINIBUFFER = t, an ID for the frame; if nil, the ID of
|
||||
;; the frame containing the minibuffer used by this frame
|
||||
;; DEFAULT? if t, this frame is the value of default-minibuffer-frame
|
||||
(let ((count 0))
|
||||
;; Reset desktop--mini for all frames
|
||||
(dolist (frame (frame-list))
|
||||
(set-frame-parameter frame 'desktop--mini nil))
|
||||
;; Number all frames with its own minibuffer
|
||||
(dolist (frame (minibuffer-frame-list))
|
||||
(set-frame-parameter frame 'desktop--mini
|
||||
(list t
|
||||
(cl-incf count)
|
||||
(eq frame default-minibuffer-frame))))
|
||||
;; Now link minibufferless frames with their minibuffer frames
|
||||
(dolist (frame frames)
|
||||
(unless (frame-parameter frame 'desktop--mini)
|
||||
(let ((mb-frame (window-frame (minibuffer-window frame))))
|
||||
;; Frames whose minibuffer frame has been filtered out will have
|
||||
;; desktop--mini = nil, so desktop-restore-frames will restore them
|
||||
;; according to their minibuffer parameter. Set up desktop--mini
|
||||
;; for the rest.
|
||||
(when (memq mb-frame frames)
|
||||
(set-frame-parameter frame 'desktop--mini
|
||||
(list nil
|
||||
(cl-second (frame-parameter mb-frame 'desktop--mini))
|
||||
nil))))))))
|
||||
|
||||
(defun desktop-save-frames ()
|
||||
"Save frame state in `desktop-saved-frame-states'.
|
||||
Runs the hook `desktop-before-saving-frames-functions'.
|
||||
(defun desktop-save-frameset ()
|
||||
"Save the state of existing frames in `desktop-saved-frameset'.
|
||||
Frames with a non-nil `desktop-dont-save' parameter are not saved."
|
||||
(setq desktop-saved-frame-states
|
||||
(setq desktop-saved-frameset
|
||||
(and desktop-restore-frames
|
||||
(let ((frames (cl-delete-if
|
||||
(lambda (frame)
|
||||
(run-hook-with-args 'desktop-before-saving-frames-functions frame)
|
||||
(frame-parameter frame 'desktop-dont-save))
|
||||
(frame-list))))
|
||||
;; In case some frame was deleted by a hook function
|
||||
(setq frames (cl-delete-if-not #'frame-live-p frames))
|
||||
(desktop--process-minibuffer-frames frames)
|
||||
(mapcar (lambda (frame)
|
||||
(cons (desktop--filter-frame-parms (frame-parameters frame) t)
|
||||
(window-state-get (frame-root-window frame) t)))
|
||||
frames)))))
|
||||
(let ((name (concat user-login-name "@" system-name
|
||||
(format-time-string " %Y-%m-%d %T"))))
|
||||
(frameset-save nil
|
||||
:filters desktop-filter-parameters-alist
|
||||
:predicate #'desktop--check-dont-save
|
||||
:properties (list :app desktop--app-id
|
||||
:name name))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun desktop-save (dirname &optional release auto-save)
|
||||
@ -1148,11 +970,11 @@ and don't save the buffer if they are the same."
|
||||
(insert "\n;; Global section:\n")
|
||||
;; Called here because we save the window/frame state as a global
|
||||
;; variable for compatibility with previous Emacsen.
|
||||
(desktop-save-frames)
|
||||
(unless (memq 'desktop-saved-frame-states desktop-globals-to-save)
|
||||
(desktop-outvar 'desktop-saved-frame-states))
|
||||
(desktop-save-frameset)
|
||||
(unless (memq 'desktop-saved-frameset desktop-globals-to-save)
|
||||
(desktop-outvar 'desktop-saved-frameset))
|
||||
(mapc (function desktop-outvar) desktop-globals-to-save)
|
||||
(setq desktop-saved-frame-states nil) ; after saving desktop-globals-to-save
|
||||
(setq desktop-saved-frameset nil) ; after saving desktop-globals-to-save
|
||||
(when (memq 'kill-ring desktop-globals-to-save)
|
||||
(insert
|
||||
"(setq kill-ring-yank-pointer (nthcdr "
|
||||
@ -1210,319 +1032,26 @@ This function also sets `desktop-dirname' to nil."
|
||||
(defvar desktop-lazy-timer nil)
|
||||
|
||||
;; ----------------------------------------------------------------------------
|
||||
(defvar desktop--reuse-list nil
|
||||
"Internal use only.")
|
||||
(defun desktop-restoring-frameset-p ()
|
||||
"True if calling `desktop-restore-frameset' will actually restore it."
|
||||
(and desktop-restore-frames desktop-saved-frameset t))
|
||||
|
||||
(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 minibuffer)
|
||||
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'
|
||||
(defun desktop-restore-frameset ()
|
||||
"Restore the state of a set of frames.
|
||||
This function depends on the value of `desktop-saved-frameset'
|
||||
being set (usually, by reading it from the desktop)."
|
||||
(when (desktop-restoring-frames-p)
|
||||
(let* ((frame-mb-map nil) ;; Alist of frames with their own minibuffer
|
||||
(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)))))
|
||||
(when (desktop-restoring-frameset-p)
|
||||
(frameset-restore desktop-saved-frameset
|
||||
:filters desktop-filter-parameters-alist
|
||||
:reuse-frames desktop-restore-reuses-frames
|
||||
:force-display desktop-restore-in-current-display
|
||||
:force-onscreen desktop-restore-forces-onscreen)))
|
||||
|
||||
;; Sorting saved states allows us to easily restore minibuffer-owning frames
|
||||
;; before minibufferless ones.
|
||||
(setq desktop-saved-frame-states (sort desktop-saved-frame-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-frame-states)
|
||||
(condition-case err
|
||||
(pcase-let* ((`(,frame-cfg . ,window-cfg) state)
|
||||
((and d-mini `(,hasmini ,num ,default))
|
||||
(cdr (assq 'desktop--mini frame-cfg)))
|
||||
(frame nil) (to-tty nil))
|
||||
;; 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.
|
||||
(hasmini ;; Frame has minibuffer (or it is minibuffer-only).
|
||||
(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 minibuffer window.
|
||||
(let ((mb-frame (cdr (assq num frame-mb-map))))
|
||||
(unless (frame-live-p mb-frame)
|
||||
(error "Minibuffer frame %s not found" num))
|
||||
(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 default (setq default-minibuffer-frame frame))
|
||||
;; Store NUM/frame to assign to minibufferless frames.
|
||||
(when hasmini (push (cons num frame) frame-mb-map))))
|
||||
(error
|
||||
(delay-warning 'desktop (error-message-string err) :error))))
|
||||
|
||||
;; In case we try to delete the initial frame, we want to make sure that
|
||||
;; other frames are already visible (discussed in thread for bug#14841).
|
||||
(sit-for 0 t)
|
||||
|
||||
;; Delete remaining frames, but do not fail if some resist being deleted.
|
||||
(unless (eq desktop-restoring-reuses-frames 'keep)
|
||||
(dolist (frame desktop--reuse-list)
|
||||
(condition-case err
|
||||
(delete-frame frame)
|
||||
(error
|
||||
(delay-warning 'desktop (error-message-string err))))))
|
||||
(setq desktop--reuse-list nil)
|
||||
;; Make sure there's at least one visible frame, and select it.
|
||||
(unless (or (daemonp)
|
||||
(cl-find-if #'frame-visible-p (frame-list)))
|
||||
(let ((visible (if (frame-live-p default-minibuffer-frame)
|
||||
default-minibuffer-frame
|
||||
(car (frame-list)))))
|
||||
(make-frame-visible visible)
|
||||
(select-frame-set-input-focus visible))))))
|
||||
;; Just to silence the byte compiler.
|
||||
;; Dynamicaly bound in `desktop-read'.
|
||||
(defvar desktop-first-buffer)
|
||||
(defvar desktop-buffer-ok-count)
|
||||
(defvar desktop-buffer-fail-count)
|
||||
|
||||
;;;###autoload
|
||||
(defun desktop-read (&optional dirname)
|
||||
@ -1583,7 +1112,7 @@ Using it may cause conflicts. Use it anyway? " owner)))))
|
||||
(file-error (message "Couldn't record use of desktop file")
|
||||
(sit-for 1))))
|
||||
|
||||
(unless (desktop-restoring-frames-p)
|
||||
(unless (desktop-restoring-frameset-p)
|
||||
;; `desktop-create-buffer' puts buffers at end of the buffer list.
|
||||
;; We want buffers existing prior to evaluating the desktop (and
|
||||
;; not reused) to be placed at the end of the buffer list, so we
|
||||
@ -1593,9 +1122,14 @@ Using it may cause conflicts. Use it anyway? " owner)))))
|
||||
(switch-to-buffer (car (buffer-list))))
|
||||
(run-hooks 'desktop-delay-hook)
|
||||
(setq desktop-delay-hook nil)
|
||||
(desktop-restore-frames)
|
||||
(desktop-restore-frameset)
|
||||
(run-hooks 'desktop-after-read-hook)
|
||||
(message "Desktop: %d buffer%s restored%s%s."
|
||||
(message "Desktop: %s%d buffer%s restored%s%s."
|
||||
(if desktop-saved-frameset
|
||||
(let ((fn (length (frameset-states desktop-saved-frameset))))
|
||||
(format "%d frame%s, "
|
||||
fn (if (= fn 1) "" "s")))
|
||||
"")
|
||||
desktop-buffer-ok-count
|
||||
(if (= 1 desktop-buffer-ok-count) "" "s")
|
||||
(if (< 0 desktop-buffer-fail-count)
|
||||
@ -1605,7 +1139,7 @@ Using it may cause conflicts. Use it anyway? " owner)))))
|
||||
(format ", %d to restore lazily"
|
||||
(length desktop-buffer-args-list))
|
||||
""))
|
||||
(unless (desktop-restoring-frames-p)
|
||||
(unless (desktop-restoring-frameset-p)
|
||||
;; Bury the *Messages* buffer to not reshow it when burying
|
||||
;; the buffer we switched to above.
|
||||
(when (buffer-live-p (get-buffer "*Messages*"))
|
||||
@ -1743,14 +1277,6 @@ integer, start a new timer to call `desktop-auto-save' in that many seconds."
|
||||
;; Create a buffer, load its file, set its mode, ...;
|
||||
;; called from Desktop file only.
|
||||
|
||||
;; Just to silence the byte compiler.
|
||||
|
||||
(defvar desktop-first-buffer) ; Dynamically bound in `desktop-read'
|
||||
|
||||
;; Bound locally in `desktop-read'.
|
||||
(defvar desktop-buffer-ok-count)
|
||||
(defvar desktop-buffer-fail-count)
|
||||
|
||||
(defun desktop-create-buffer
|
||||
(file-version
|
||||
buffer-filename
|
||||
|
675
lisp/frameset.el
Normal file
675
lisp/frameset.el
Normal file
@ -0,0 +1,675 @@
|
||||
;;; frameset.el --- save and restore frame and window setup -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2013 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Juanma Barranquero <lekktu@gmail.com>
|
||||
;; Keywords: convenience
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This file provides a set of operations to save a frameset (the state
|
||||
;; of all or a subset of the existing frames and windows), both
|
||||
;; in-session and persistently, and restore it at some point in the
|
||||
;; future.
|
||||
;;
|
||||
;; It should be noted that restoring the frames' windows depends on
|
||||
;; the buffers they are displaying, but this package does not provide
|
||||
;; any way to save and restore sets of buffers (see desktop.el for
|
||||
;; that). So, it's up to the user of frameset.el to make sure that
|
||||
;; any relevant buffer is loaded before trying to restore a frameset.
|
||||
;; When a window is restored and a buffer is missing, the window will
|
||||
;; be deleted unless it is the last one in the frame, in which case
|
||||
;; some previous buffer will be shown instead.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
|
||||
|
||||
;; Framesets have two fields:
|
||||
;; - properties: a property list to store both frameset-specific and
|
||||
;; user-defined serializable data. Currently defined properties
|
||||
;; include:
|
||||
;; :version ID - Identifies the version of the frameset struct;
|
||||
;; this is the only property always present and
|
||||
;; must not be modified.
|
||||
;; :app APPINFO - Freeform. Can be used by applications and
|
||||
;; packages to indicate the intended (but by no
|
||||
;; means exclusive) use of the frameset. For
|
||||
;; example, currently desktop.el sets :app to
|
||||
;; `(desktop . ,desktop-file-version).
|
||||
;; :name NAME - The name of the frameset instance; a string.
|
||||
;; :desc TEXT - A description for user consumption (to choose
|
||||
;; among framesets, etc.); a string.
|
||||
;; - states: an alist of items (FRAME-PARAMETERS . WINDOW-STATE) in
|
||||
;; no particular order. Each item represents a frame to be
|
||||
;; restored.
|
||||
|
||||
(cl-defstruct (frameset (:type list) :named
|
||||
(:copier nil)
|
||||
(:predicate nil))
|
||||
properties ;; property list
|
||||
states) ;; list of conses (frame-state . window-state)
|
||||
|
||||
(defun copy-frameset (frameset)
|
||||
"Return a copy of FRAMESET.
|
||||
This is a deep copy done with `copy-tree'."
|
||||
(copy-tree frameset t))
|
||||
|
||||
;;;autoload
|
||||
(defun frameset-p (frameset)
|
||||
"If FRAMESET is a frameset, return its :version.
|
||||
Else return nil."
|
||||
(and (eq (car-safe frameset) 'frameset)
|
||||
(plist-get (cl-second frameset) :version)))
|
||||
|
||||
|
||||
;; Filtering
|
||||
|
||||
(defvar frameset-filter-alist
|
||||
'((background-color . frameset-filter-sanitize-color)
|
||||
(buffer-list . t)
|
||||
(buffer-predicate . t)
|
||||
(buried-buffer-list . t)
|
||||
(font . frameset-filter-save-parm)
|
||||
(foreground-color . frameset-filter-sanitize-color)
|
||||
(fullscreen . frameset-filter-save-parm)
|
||||
(GUI:font . frameset-filter-restore-parm)
|
||||
(GUI:fullscreen . frameset-filter-restore-parm)
|
||||
(GUI:height . frameset-filter-restore-parm)
|
||||
(GUI:width . frameset-filter-restore-parm)
|
||||
(height . frameset-filter-save-parm)
|
||||
(left . frameset-filter-iconified)
|
||||
(minibuffer . frameset-filter-minibuffer)
|
||||
(top . frameset-filter-iconified)
|
||||
(width . frameset-filter-save-parm))
|
||||
"Alist of frame parameters and filtering functions.
|
||||
|
||||
Each element is a cons (PARAM . ACTION), where PARAM is a parameter
|
||||
name (a symbol identifying a frame parameter), and ACTION can be:
|
||||
|
||||
t The parameter is always removed from the parameter list.
|
||||
:save The parameter is removed when saving the frame.
|
||||
:restore The parameter is removed when restoring the frame.
|
||||
FILTER A filter function.
|
||||
|
||||
FILTER can be a symbol FILTER-FUN, or a list (FILTER-FUN ARGS...).
|
||||
It will be called with four arguments CURRENT, FILTERED, PARAMETERS
|
||||
and SAVING, plus any additional ARGS:
|
||||
|
||||
CURRENT A cons (PARAM . VALUE), where PARAM is the one being
|
||||
filtered and VALUE is its current value.
|
||||
FILTERED The alist of parameters filtered so far.
|
||||
PARAMETERS The complete alist of parameters being filtered,
|
||||
SAVING Non-nil if filtering before saving state, nil otherwise.
|
||||
|
||||
The FILTER-FUN function must return:
|
||||
nil CURRENT is removed from the list.
|
||||
t CURRENT is left as is.
|
||||
(PARAM' . VALUE') Replace CURRENT with this.
|
||||
|
||||
Frame parameters not on this list are passed intact.")
|
||||
|
||||
(defvar frameset--target-display nil
|
||||
;; Either (minibuffer . VALUE) or nil.
|
||||
;; This refers to the current frame config being processed inside
|
||||
;; `frame--restore-frames' and its auxiliary functions (like filtering).
|
||||
;; If nil, there is no need to change the display.
|
||||
;; If non-nil, display parameter to use when creating the frame.
|
||||
"Internal use only.")
|
||||
|
||||
(defun frameset-switch-to-gui-p (parameters)
|
||||
"True when switching to a graphic display.
|
||||
Return t if PARAMETERS describes a text-only terminal and
|
||||
the target is a graphic display; otherwise return nil.
|
||||
Only meaningful when called from a filtering function in
|
||||
`frameset-filter-alist'."
|
||||
(and frameset--target-display ; we're switching
|
||||
(null (cdr (assq 'display parameters))) ; from a tty
|
||||
(cdr frameset--target-display))) ; to a GUI display
|
||||
|
||||
(defun frameset-switch-to-tty-p (parameters)
|
||||
"True when switching to a text-only terminal.
|
||||
Return t if PARAMETERS describes a graphic display and
|
||||
the target is a text-only terminal; otherwise return nil.
|
||||
Only meaningful when called from a filtering function in
|
||||
`frameset-filter-alist'."
|
||||
(and frameset--target-display ; we're switching
|
||||
(cdr (assq 'display parameters)) ; from a GUI display
|
||||
(null (cdr frameset--target-display)))) ; to a tty
|
||||
|
||||
(defun frameset-filter-sanitize-color (current _filtered parameters saving)
|
||||
"When switching to a GUI frame, remove \"unspecified\" colors.
|
||||
Useful as a filter function for tty-specific parameters."
|
||||
(or saving
|
||||
(not (frameset-switch-to-gui-p parameters))
|
||||
(not (stringp (cdr current)))
|
||||
(not (string-match-p "^unspecified-[fb]g$" (cdr current)))))
|
||||
|
||||
(defun frameset-filter-minibuffer (current _filtered _parameters saving)
|
||||
"Convert (minibuffer . #<window>) parameter to (minibuffer . t)."
|
||||
(or (not saving)
|
||||
(if (windowp (cdr current))
|
||||
'(minibuffer . t)
|
||||
t)))
|
||||
|
||||
(defun frameset-filter-save-parm (current _filtered parameters saving
|
||||
&optional prefix)
|
||||
"When switching to a tty frame, save parameter P as PREFIX:P.
|
||||
The parameter can be later restored with `frameset-filter-restore-parm'.
|
||||
PREFIX defaults to `GUI'."
|
||||
(unless prefix (setq prefix 'GUI))
|
||||
(cond (saving t)
|
||||
((frameset-switch-to-tty-p parameters)
|
||||
(let ((prefix:p (intern (format "%s:%s" prefix (car current)))))
|
||||
(if (assq prefix:p parameters)
|
||||
nil
|
||||
(cons prefix:p (cdr current)))))
|
||||
((frameset-switch-to-gui-p parameters)
|
||||
(not (assq (intern (format "%s:%s" prefix (car current))) parameters)))
|
||||
(t t)))
|
||||
|
||||
(defun frameset-filter-restore-parm (current filtered parameters saving)
|
||||
"When switching to a GUI frame, restore PREFIX:P parameter as P.
|
||||
CURRENT must be of the form (PREFIX:P . value)."
|
||||
(or saving
|
||||
(not (frameset-switch-to-gui-p parameters))
|
||||
(let* ((prefix:p (symbol-name (car current)))
|
||||
(p (intern (substring prefix:p
|
||||
(1+ (string-match-p ":" prefix:p)))))
|
||||
(val (cdr current))
|
||||
(found (assq p filtered)))
|
||||
(if (not found)
|
||||
(cons p val)
|
||||
(setcdr found val)
|
||||
nil))))
|
||||
|
||||
(defun frameset-filter-iconified (_current _filtered parameters saving)
|
||||
"Remove CURRENT when saving an iconified frame.
|
||||
This is used for positions parameters `left' and `top', which are
|
||||
meaningless in an iconified frame, so the frame is restored in a
|
||||
default position."
|
||||
(not (and saving (eq (cdr (assq 'visibility parameters)) 'icon))))
|
||||
|
||||
(defun frameset-keep-original-display-p (force-display)
|
||||
"True if saved frames' displays should be honored."
|
||||
(cond ((daemonp) t)
|
||||
((eq system-type 'windows-nt) nil)
|
||||
(t (null force-display))))
|
||||
|
||||
(defun frameset-filter-params (parameters filter-alist saving)
|
||||
"Filter parameter list PARAMETERS and return a filtered list.
|
||||
FILTER-ALIST is an alist of parameter filters, in the format of
|
||||
`frameset-filter-alist' (which see).
|
||||
SAVING is non-nil while filtering parameters to save a frameset,
|
||||
nil while the filtering is done to restore it."
|
||||
(let ((filtered nil))
|
||||
(dolist (current parameters)
|
||||
(pcase (cdr (assq (car current) filter-alist))
|
||||
(`nil
|
||||
(push current filtered))
|
||||
(`t
|
||||
nil)
|
||||
(:save
|
||||
(unless saving (push current filtered)))
|
||||
(:restore
|
||||
(when saving (push current filtered)))
|
||||
((or `(,fun . ,args) (and fun (pred fboundp)))
|
||||
(let ((this (apply fun filtered current parameters saving args)))
|
||||
(when this
|
||||
(push (if (eq this t) current this) filtered))))
|
||||
(other
|
||||
(delay-warning 'frameset (format "Unknown filter %S" other) :error))))
|
||||
;; Set the display parameter after filtering, so that filter functions
|
||||
;; have access to its original value.
|
||||
(when frameset--target-display
|
||||
(let ((display (assq 'display filtered)))
|
||||
(if display
|
||||
(setcdr display (cdr frameset--target-display))
|
||||
(push frameset--target-display filtered))))
|
||||
filtered))
|
||||
|
||||
|
||||
;; Saving framesets
|
||||
|
||||
(defun frameset--set-id (frame)
|
||||
"Set FRAME's `frameset-id' if not yet set.
|
||||
Internal use only."
|
||||
(unless (frame-parameter frame 'frameset-id)
|
||||
(set-frame-parameter frame
|
||||
'frameset-id
|
||||
(mapconcat (lambda (n) (format "%04X" n))
|
||||
(cl-loop repeat 4 collect (random 65536))
|
||||
"-"))))
|
||||
|
||||
(defun frameset--process-minibuffer-frames (frame-list)
|
||||
"Process FRAME-LIST and record minibuffer relationships.
|
||||
FRAME-LIST is a list of frames."
|
||||
;; Record frames with their own minibuffer
|
||||
(dolist (frame (minibuffer-frame-list))
|
||||
(when (memq frame frame-list)
|
||||
(frameset--set-id frame)
|
||||
;; For minibuffer-owning frames, frameset--mini is a cons
|
||||
;; (t . DEFAULT?), where DEFAULT? is a boolean indicating whether
|
||||
;; the frame is the one pointed out by `default-minibuffer-frame'.
|
||||
(set-frame-parameter frame
|
||||
'frameset--mini
|
||||
(cons t (eq frame default-minibuffer-frame)))))
|
||||
;; Now link minibufferless frames with their minibuffer frames
|
||||
(dolist (frame frame-list)
|
||||
(unless (frame-parameter frame 'frameset--mini)
|
||||
(frameset--set-id frame)
|
||||
(let* ((mb-frame (window-frame (minibuffer-window frame)))
|
||||
(id (and mb-frame (frame-parameter mb-frame 'frameset-id))))
|
||||
(if (null id)
|
||||
(error "Minibuffer frame %S for %S is excluded" mb-frame frame)
|
||||
;; For minibufferless frames, frameset--mini is a cons
|
||||
;; (nil . FRAME-ID), where FRAME-ID is the frameset-id of
|
||||
;; the frame containing its minibuffer window.
|
||||
(set-frame-parameter frame
|
||||
'frameset--mini
|
||||
(cons nil id)))))))
|
||||
|
||||
;;;autoload
|
||||
(cl-defun frameset-save (frame-list &key filters predicate properties)
|
||||
"Return the frameset of FRAME-LIST, a list of frames.
|
||||
If nil, FRAME-LIST defaults to all live frames.
|
||||
FILTERS is an alist of parameter filters; defaults to `frameset-filter-alist'.
|
||||
PREDICATE is a predicate function, which must return non-nil for frames that
|
||||
should be saved; it defaults to saving all frames from FRAME-LIST.
|
||||
PROPERTIES is a user-defined property list to add to the frameset."
|
||||
(let ((frames (cl-delete-if-not #'frame-live-p
|
||||
(cl-remove-if-not (or predicate #'framep)
|
||||
(or frame-list (frame-list))))))
|
||||
(frameset--process-minibuffer-frames frames)
|
||||
(make-frameset :properties (append '(:version 1) properties)
|
||||
:states (mapcar
|
||||
(lambda (frame)
|
||||
(cons
|
||||
(frameset-filter-params (frame-parameters frame)
|
||||
(or filters
|
||||
frameset-filter-alist)
|
||||
t)
|
||||
(window-state-get (frame-root-window frame) t)))
|
||||
frames))))
|
||||
|
||||
|
||||
;; Restoring framesets
|
||||
|
||||
(defvar frameset--reuse-list nil
|
||||
"Internal use only.")
|
||||
|
||||
(defun frameset--compute-pos (value left/top right/bottom)
|
||||
(pcase value
|
||||
(`(+ ,val) (+ left/top val))
|
||||
(`(- ,val) (+ right/bottom val))
|
||||
(val val)))
|
||||
|
||||
(defun frameset--move-onscreen (frame force-onscreen)
|
||||
"If FRAME is offscreen, move it back onscreen and, if necessary, resize it.
|
||||
For the description of FORCE-ONSCREEN, see `frameset-restore'.
|
||||
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. Internal use only."
|
||||
(pcase-let* ((`(,left ,top ,width ,height) (cl-cdadr (frame-monitor-attributes frame)))
|
||||
(right (+ left width -1))
|
||||
(bottom (+ top height -1))
|
||||
(fr-left (frameset--compute-pos (frame-parameter frame 'left) left right))
|
||||
(fr-top (frameset--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 force-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)))
|
||||
;; Fully inside, no need to do anything.
|
||||
(_ 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 frameset--find-frame (predicate display &rest args)
|
||||
"Find a frame in `frameset--reuse-list' satisfying PREDICATE.
|
||||
Look through available 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."
|
||||
(cl-find-if (lambda (frame)
|
||||
(and (equal (frame-parameter frame 'display) display)
|
||||
(or (null predicate)
|
||||
(apply predicate frame args))))
|
||||
frameset--reuse-list))
|
||||
|
||||
(defun frameset--reuse-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."
|
||||
(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,
|
||||
;; will usually have only one frame, and should already work.
|
||||
(cond ((null display)
|
||||
;; When the target is tty, every existing frame is reusable.
|
||||
(setq frame (frameset--find-frame nil display)))
|
||||
((car (setq mini (cdr (assq 'frameset--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 (frameset--find-frame
|
||||
(lambda (f id)
|
||||
(string= (frame-parameter f 'frameset-id) id))
|
||||
display (cdr 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 (frameset--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 (frameset--find-frame
|
||||
(lambda (f id mini-id)
|
||||
(and (string= (frame-parameter f 'frameset-id) id)
|
||||
(string= (frame-parameter (window-frame (minibuffer-window f))
|
||||
'frameset-id)
|
||||
mini-id)))
|
||||
display (cdr (assq 'frameset-id frame-cfg)) (cdr mini))))
|
||||
(t
|
||||
;; Default to just finding a frame in the same display.
|
||||
(setq frame (frameset--find-frame nil display))))
|
||||
;; If found, remove from the list.
|
||||
(when frame
|
||||
(setq frameset--reuse-list (delq frame frameset--reuse-list)))
|
||||
frame))
|
||||
|
||||
(defun frameset--get-frame (frame-cfg window-cfg filters force-onscreen)
|
||||
"Set up and return a frame according to its saved state.
|
||||
That means either reusing an existing frame or creating one anew.
|
||||
FRAME-CFG is the frame's parameter list; WINDOW-CFG is its window state.
|
||||
For the meaning of FORCE-ONSCREEN, see `frameset-restore'."
|
||||
(let* ((fullscreen (cdr (assq 'fullscreen frame-cfg)))
|
||||
(lines (assq 'tool-bar-lines frame-cfg))
|
||||
(filtered-cfg (frameset-filter-params frame-cfg filters 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 fully 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 (and frameset--reuse-list
|
||||
(frameset--reuse-frame display filtered-cfg))
|
||||
(make-frame-on-display display
|
||||
(cons '(visibility)
|
||||
(cl-loop
|
||||
for param in '(left top width height minibuffer)
|
||||
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 force-onscreen
|
||||
;; FIXME: iconified frames should be checked too,
|
||||
;; but it is impossible without deiconifying them.
|
||||
(not (eq (frame-parameter frame 'visibility) 'icon)))
|
||||
(frameset--move-onscreen frame force-onscreen))
|
||||
|
||||
;; 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 frameset--sort-states (state1 state2)
|
||||
"Predicate to sort frame states in a suitable order to be created.
|
||||
It sorts minibuffer-owning frames before minibufferless ones."
|
||||
(pcase-let ((`(,hasmini1 ,id-def1) (assq 'frameset--mini (car state1)))
|
||||
(`(,hasmini2 ,id-def2) (assq 'frameset--mini (car state2))))
|
||||
(cond ((eq id-def1 t) t)
|
||||
((eq id-def2 t) nil)
|
||||
((not (eq hasmini1 hasmini2)) (eq hasmini1 t))
|
||||
((eq hasmini1 nil) (string< id-def1 id-def2))
|
||||
(t t))))
|
||||
|
||||
(defun frameset-sort-frames-for-deletion (frame1 _frame2)
|
||||
"Predicate to sort live frames for deletion.
|
||||
Minibufferless frames must go first to avoid errors when attempting
|
||||
to delete a frame whose minibuffer window is used by another frame."
|
||||
(not (frame-parameter frame1 'minibuffer)))
|
||||
|
||||
;;;autoload
|
||||
(cl-defun frameset-restore (frameset &key filters reuse-frames force-display force-onscreen)
|
||||
"Restore a FRAMESET into the current display(s).
|
||||
|
||||
FILTERS is a list of parameter filters; defaults to `frameset-filter-alist'.
|
||||
|
||||
REUSE-FRAMES describes how to reuse existing frames while restoring a frameset:
|
||||
t Reuse any existing frame if possible; delete leftover frames.
|
||||
nil Restore frameset in new frames and delete existing frames.
|
||||
keep Restore frameset in new frames and keep the existing ones.
|
||||
LIST A list of frames to reuse; only these will be reused, if possible,
|
||||
and any leftover one will be deleted; other frames not on this
|
||||
list will be kept.
|
||||
|
||||
FORCE-DISPLAY can be:
|
||||
t Frames will be restored in the current display.
|
||||
nil Frames will be restored, if possible, in their original displays.
|
||||
delete Frames in other displays will be deleted instead of restored.
|
||||
|
||||
FORCE-ONSCREEN can be:
|
||||
all Force onscreen any frame fully or partially offscreen.
|
||||
t Force onscreen only those frames that are fully offscreen.
|
||||
nil Do not force any frame back onscreen.
|
||||
|
||||
All keywords default to nil."
|
||||
|
||||
(cl-assert (frameset-p frameset))
|
||||
|
||||
(let* ((delete-saved (eq force-display 'delete))
|
||||
(forcing (not (frameset-keep-original-display-p force-display)))
|
||||
(target (and forcing (cons 'display (frame-parameter nil 'display))))
|
||||
other-frames)
|
||||
|
||||
;; frameset--reuse-list is a list of frames potentially reusable. Later we
|
||||
;; will decide which ones can be reused, and how to deal with any leftover.
|
||||
(pcase reuse-frames
|
||||
((or `nil `keep)
|
||||
(setq frameset--reuse-list nil
|
||||
other-frames (frame-list)))
|
||||
((pred consp)
|
||||
(setq frameset--reuse-list (copy-sequence reuse-frames)
|
||||
other-frames (cl-delete-if (lambda (frame)
|
||||
(memq frame frameset--reuse-list))
|
||||
(frame-list))))
|
||||
(_
|
||||
(setq frameset--reuse-list (frame-list)
|
||||
other-frames nil)))
|
||||
|
||||
;; Sort saved states to guarantee that minibufferless frames will be created
|
||||
;; after the frames that contain their minibuffer windows.
|
||||
(dolist (state (sort (copy-sequence (frameset-states frameset))
|
||||
#'frameset--sort-states))
|
||||
(condition-case-unless-debug err
|
||||
(pcase-let* ((`(,frame-cfg . ,window-cfg) state)
|
||||
((and d-mini `(,hasmini . ,mb-id))
|
||||
(cdr (assq 'frameset--mini frame-cfg)))
|
||||
(default (and (booleanp mb-id) mb-id))
|
||||
(frame nil) (to-tty nil))
|
||||
;; 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 frameset--target-display nil)
|
||||
(setq frameset--target-display target
|
||||
to-tty (null (cdr target))))
|
||||
;; If keeping non-reusable frames, and the frame-id of one of them
|
||||
;; matches the frame-id of a frame being restored (because, for example,
|
||||
;; the frameset has already been read in the same session), remove the
|
||||
;; frame-id from the non-reusable frame, which is not useful anymore.
|
||||
(when (and other-frames
|
||||
(or (eq reuse-frames 'keep) (consp reuse-frames)))
|
||||
(let ((dup (cl-find (cdr (assq 'frameset-frame-id frame-cfg))
|
||||
other-frames
|
||||
:key (lambda (frame)
|
||||
(frame-parameter frame 'frameset-frame-id))
|
||||
:test #'string=)))
|
||||
(when dup
|
||||
(set-frame-parameter dup 'frameset-frame-id nil))))
|
||||
;; 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 frameset--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 frameset--mini. Process as normal frame.
|
||||
(to-tty) ;; Ignore minibuffer stuff and process as normal frame.
|
||||
(hasmini ;; Frame has minibuffer (or it is minibuffer-only).
|
||||
(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 minibuffer window.
|
||||
(let* ((mb-frame (or (cl-find-if
|
||||
(lambda (f)
|
||||
(string= (frame-parameter f 'frameset-id)
|
||||
mb-id))
|
||||
(frame-list))
|
||||
(error "Minibuffer frame %S not found" mb-id)))
|
||||
(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 (frameset--get-frame frame-cfg window-cfg
|
||||
(or filters frameset-filter-alist)
|
||||
force-onscreen))
|
||||
;; Set default-minibuffer if required.
|
||||
(when default (setq default-minibuffer-frame frame)))
|
||||
(error
|
||||
(delay-warning 'frameset (error-message-string err) :error))))
|
||||
|
||||
;; In case we try to delete the initial frame, we want to make sure that
|
||||
;; other frames are already visible (discussed in thread for bug#14841).
|
||||
(sit-for 0 t)
|
||||
|
||||
;; Delete remaining frames, but do not fail if some resist being deleted.
|
||||
(unless (eq reuse-frames 'keep)
|
||||
(dolist (frame (sort (nconc (if (listp reuse-frames) nil other-frames)
|
||||
frameset--reuse-list)
|
||||
#'frameset-sort-frames-for-deletion))
|
||||
(condition-case err
|
||||
(delete-frame frame)
|
||||
(error
|
||||
(delay-warning 'frameset (error-message-string err))))))
|
||||
(setq frameset--reuse-list nil)
|
||||
|
||||
;; Make sure there's at least one visible frame.
|
||||
(unless (or (daemonp) (visible-frame-list))
|
||||
(make-frame-visible (car (frame-list))))))
|
||||
|
||||
(provide 'frameset)
|
||||
|
||||
;;; frameset.el ends here
|
Loading…
Reference in New Issue
Block a user