]> code.delx.au - gnu-emacs/blobdiff - lisp/desktop.el
* lisp/simple.el (command-execute): Respect nil disabled-command-function.
[gnu-emacs] / lisp / desktop.el
index d3bb6648099aa0a9ed5a7780cf61b7e7b1c70625..5e43c03ade77d7e199195d88c00e107810dd0ed2 100644 (file)
@@ -1,7 +1,6 @@
-;;; desktop.el --- save partial status of Emacs when killed
+;;; desktop.el --- save partial status of Emacs when killed -*- lexical-binding: t -*-
 
 
-;; Copyright (C) 1993-1995, 1997, 2000-2013 Free Software Foundation,
-;; Inc.
+;; Copyright (C) 1993-1995, 1997, 2000-2014 Free Software Foundation, Inc.
 
 ;; Author: Morten Welinder <terra@diku.dk>
 ;; Keywords: convenience
 
 ;; Author: Morten Welinder <terra@diku.dk>
 ;; Keywords: convenience
 ;;            f89-kam@nada.kth.se (Klas Mellbourn)   for a mh-e tip.
 ;;            kifer@sbkifer.cs.sunysb.edu (M. Kifer) for a bug hunt.
 ;;            treese@lcs.mit.edu (Win Treese)        for ange-ftp tips.
 ;;            f89-kam@nada.kth.se (Klas Mellbourn)   for a mh-e tip.
 ;;            kifer@sbkifer.cs.sunysb.edu (M. Kifer) for a bug hunt.
 ;;            treese@lcs.mit.edu (Win Treese)        for ange-ftp tips.
-;;            pot@cnuce.cnr.it (Francesco Potorti`)  for misc. tips.
+;;            pot@cnuce.cnr.it (Francesco Potortì)  for misc. tips.
 ;; ---------------------------------------------------------------------------
 ;; TODO:
 ;;
 ;; ---------------------------------------------------------------------------
 ;; TODO:
 ;;
 ;;; Code:
 
 (require 'cl-lib)
 ;;; Code:
 
 (require 'cl-lib)
+(require 'frameset)
 
 (defvar desktop-file-version "206"
   "Version number of desktop file format.
 
 (defvar desktop-file-version "206"
   "Version number of desktop file format.
@@ -153,15 +153,29 @@ backward compatibility.")
 ;;;###autoload
 (define-minor-mode desktop-save-mode
   "Toggle desktop saving (Desktop Save mode).
 ;;;###autoload
 (define-minor-mode desktop-save-mode
   "Toggle desktop saving (Desktop Save mode).
-With a prefix argument ARG, enable Desktop Save mode if ARG is
-positive, and disable it otherwise.  If called from Lisp, enable
-the mode if ARG is omitted or nil.
+With a prefix argument ARG, enable Desktop Save mode if ARG is positive,
+and disable it otherwise.  If called from Lisp, enable the mode if ARG
+is omitted or nil.
 
 
-If Desktop Save mode is enabled, the state of Emacs is saved from
-one session to another.  See variable `desktop-save' and function
-`desktop-read' for details."
+When Desktop Save mode is enabled, the state of Emacs is saved from
+one session to another.  In particular, Emacs will save the desktop when
+it exits (this may prompt you; see the option `desktop-save').  The next
+time Emacs starts, if this mode is active it will restore the desktop.
+
+To manually save the desktop at any time, use the command `M-x desktop-save'.
+To load it, use `M-x desktop-read'.
+
+Once a desktop file exists, Emacs will auto-save it according to the
+option `desktop-auto-save-timeout'.
+
+To see all the options you can set, browse the `desktop' customization group.
+
+For further details, see info node `(emacs)Saving Emacs Sessions'."
   :global t
   :global t
-  :group 'desktop)
+  :group 'desktop
+  (if desktop-save-mode
+      (desktop-auto-save-set-timer)
+    (desktop-auto-save-cancel-timer)))
 
 (defun desktop-save-mode-off ()
   "Disable `desktop-save-mode'.  Provided for use in hooks."
 
 (defun desktop-save-mode-off ()
   "Disable `desktop-save-mode'.  Provided for use in hooks."
@@ -191,9 +205,10 @@ determine where the desktop is saved."
   :group 'desktop
   :version "22.1")
 
   :group 'desktop
   :version "22.1")
 
-(defcustom desktop-auto-save-timeout nil
-  "Number of seconds between auto-saves of the desktop.
-Zero or nil means disable timer-based auto-saving."
+(defcustom desktop-auto-save-timeout auto-save-timeout
+  "Number of seconds idle time before auto-save of the desktop.
+This applies to an existing desktop file when `desktop-save-mode' is enabled.
+Zero or nil means disable auto-saving due to idleness."
   :type '(choice (const :tag "Off" nil)
                  (integer :tag "Seconds"))
   :set (lambda (symbol value)
   :type '(choice (const :tag "Off" nil)
                  (integer :tag "Seconds"))
   :set (lambda (symbol value)
@@ -349,11 +364,11 @@ modes are restored automatically; they should not be listed here."
   :type '(repeat symbol)
   :group 'desktop)
 
   :type '(repeat symbol)
   :group 'desktop)
 
-(defcustom desktop-buffers-not-to-save nil
+(defcustom desktop-buffers-not-to-save "\\` "
   "Regexp identifying buffers that are to be excluded from saving."
   :type '(choice (const :tag "None" nil)
                 regexp)
   "Regexp identifying buffers that are to be excluded from saving."
   :type '(choice (const :tag "None" nil)
                 regexp)
-  :version "23.2"                       ; set to nil
+  :version "24.4"                  ; skip invisible temporary buffers
   :group 'desktop)
 
 ;; Skip tramp and ange-ftp files
   :group 'desktop)
 
 ;; Skip tramp and ange-ftp files
@@ -372,47 +387,45 @@ modes are restored automatically; they should not be listed here."
   :group 'desktop)
 
 (defcustom desktop-restore-frames t
   :group 'desktop)
 
 (defcustom desktop-restore-frames t
-  "When non-nil, save window/frame configuration to desktop file."
+  "When non-nil, save and restore the frame and window configuration.
+See related options `desktop-restore-reuses-frames',
+`desktop-restore-in-current-display', and `desktop-restore-forces-onscreen'."
   :type 'boolean
   :group 'desktop
   :version "24.4")
 
 (defcustom desktop-restore-in-current-display nil
   :type 'boolean
   :group 'desktop
   :version "24.4")
 
 (defcustom desktop-restore-in-current-display nil
-  "If t, frames are restored in the current display.
-If nil, frames are restored, if possible, in their original displays.
-If `delete', frames on other displays are deleted instead of restored."
+  "Controls how restoring of frames treats displays.
+If t, restores frames into the current display.
+If nil, restores frames into their original displays (if possible).
+If `delete', deletes frames on other displays instead of restoring them."
   :type '(choice (const :tag "Restore in current display" t)
                 (const :tag "Restore in original display" nil)
   :type '(choice (const :tag "Restore in current display" t)
                 (const :tag "Restore in original display" nil)
-                (const :tag "Delete frames in other displays" 'delete))
+                (const :tag "Delete frames in other displays" delete))
   :group 'desktop
   :version "24.4")
 
 (defcustom desktop-restore-forces-onscreen t
   :group 'desktop
   :version "24.4")
 
 (defcustom desktop-restore-forces-onscreen t
-  "If t, offscreen frames are restored onscreen instead.
-If `all', frames that are partially offscreen are also forced onscren.
-NOTE: Checking of frame boundaries is only approximate and can fail
-to reliably detect frames whose onscreen/offscreen state depends on a
-few pixels, especially near the right / bottom borders of the screen."
+  "If t, restores frames that are fully offscreen onscreen instead.
+If `all', also restores frames that are partially offscreen onscreen.
+
+Note that checking of frame boundaries is only approximate.
+It can fail to reliably detect frames whose onscreen/offscreen state
+depends on a few pixels, especially near the right / bottom borders
+of the screen."
   :type '(choice (const :tag "Only fully offscreen frames" t)
   :type '(choice (const :tag "Only fully offscreen frames" t)
-                (const :tag "Also partially offscreen frames" 'all)
+                (const :tag "Also partially offscreen frames" all)
                 (const :tag "Do not force frames onscreen" nil))
   :group 'desktop
   :version "24.4")
 
                 (const :tag "Do not force frames onscreen" nil))
   :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 t, restoring frames reuses existing frames.
-If nil, existing frames are deleted.
-If `keep', existing frames are kept and not reused."
+If nil, deletes existing frames.
+If `keep', keeps existing frames and does not reuse them."
   :type '(choice (const :tag "Reuse existing frames" t)
                 (const :tag "Delete existing frames" nil)
   :type '(choice (const :tag "Reuse existing frames" t)
                 (const :tag "Delete existing frames" nil)
-                (const :tag "Keep existing frames" 'keep))
-  :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
+                (const :tag "Keep existing frames" :keep))
   :group 'desktop
   :version "24.4")
 
   :group 'desktop
   :version "24.4")
 
@@ -483,13 +496,13 @@ Handlers are called with argument list
 
 Furthermore, they may use the following variables:
 
 
 Furthermore, they may use the following variables:
 
-   desktop-file-version
-   desktop-buffer-major-mode
-   desktop-buffer-minor-modes
-   desktop-buffer-point
-   desktop-buffer-mark
-   desktop-buffer-read-only
-   desktop-buffer-locals
+   `desktop-file-version'
+   `desktop-buffer-major-mode'
+   `desktop-buffer-minor-modes'
+   `desktop-buffer-point'
+   `desktop-buffer-mark'
+   `desktop-buffer-read-only'
+   `desktop-buffer-locals'
 
 If a handler returns a buffer, then the saved mode settings
 and variable values for that buffer are copied into it.
 
 If a handler returns a buffer, then the saved mode settings
 and variable values for that buffer are copied into it.
@@ -543,15 +556,15 @@ Handlers are called with argument list
 
 Furthermore, they may use the following variables:
 
 
 Furthermore, they may use the following variables:
 
-   desktop-file-version
-   desktop-buffer-file-name
-   desktop-buffer-name
-   desktop-buffer-major-mode
-   desktop-buffer-minor-modes
-   desktop-buffer-point
-   desktop-buffer-mark
-   desktop-buffer-read-only
-   desktop-buffer-misc
+   `desktop-file-version'
+   `desktop-buffer-file-name'
+   `desktop-buffer-name'
+   `desktop-buffer-major-mode'
+   `desktop-buffer-minor-modes'
+   `desktop-buffer-point'
+   `desktop-buffer-mark'
+   `desktop-buffer-read-only'
+   `desktop-buffer-misc'
 
 When a handler is called, the buffer has been created and the major mode has
 been set, but local variables listed in desktop-buffer-locals has not yet been
 
 When a handler is called, the buffer has been created and the major mode has
 been set, but local variables listed in desktop-buffer-locals has not yet been
@@ -599,7 +612,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.")
 
   "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.")
 
   "Saved state of all frames.
 Only valid during frame saving & restoring; intended for internal use.")
 
@@ -649,7 +662,10 @@ DIRNAME omitted or nil means use `desktop-dirname'."
   "Empty the Desktop.
 This kills all buffers except for internal ones and those with names matched by
 a regular expression in the list `desktop-clear-preserve-buffers'.
   "Empty the Desktop.
 This kills all buffers except for internal ones and those with names matched by
 a regular expression in the list `desktop-clear-preserve-buffers'.
-Furthermore, it clears the variables listed in `desktop-globals-to-clear'."
+Furthermore, it clears the variables listed in `desktop-globals-to-clear'.
+When called interactively and `desktop-restore-frames' is non-nil, it also
+deletes all frames except the selected one (and its minibuffer frame,
+if different)."
   (interactive)
   (desktop-lazy-abort)
   (dolist (var desktop-globals-to-clear)
   (interactive)
   (desktop-lazy-abort)
   (dolist (var desktop-globals-to-clear)
@@ -667,7 +683,21 @@ 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)))))
        (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)
+  (when (and desktop-restore-frames
+            ;; Non-interactive calls to desktop-clear happen before desktop-read
+            ;; which already takes care of frame restoration and deletion.
+            (called-interactively-p 'any))
+    (let* ((this (selected-frame))
+          (mini (window-frame (minibuffer-window this)))) ; in case they differ
+      (dolist (frame (sort (frame-list) #'frameset-minibufferless-first-p))
+       (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
 
 ;; ----------------------------------------------------------------------------
 (unless noninteractive
@@ -853,23 +883,25 @@ FILENAME is the visited file name, BUFNAME is the buffer name, and
 MODE is the major mode.
 \n\(fn FILENAME BUFNAME MODE)"
   (let ((case-fold-search nil)
 MODE is the major mode.
 \n\(fn FILENAME BUFNAME MODE)"
   (let ((case-fold-search nil)
-        dired-skip)
-    (and (not (and (stringp desktop-buffers-not-to-save)
-                  (not filename)
-                  (string-match-p desktop-buffers-not-to-save bufname)))
-         (not (memq mode desktop-modes-not-to-save))
-         ;; FIXME this is broken if desktop-files-not-to-save is nil.
-         (or (and filename
-                 (stringp desktop-files-not-to-save)
-                  (not (string-match-p desktop-files-not-to-save filename)))
-             (and (memq mode '(dired-mode vc-dir-mode))
-                  (with-current-buffer bufname
-                    (not (setq dired-skip
-                               (string-match-p desktop-files-not-to-save
-                                               default-directory)))))
-             (and (null filename)
-                  (null dired-skip)     ; bug#5755
-                 (with-current-buffer bufname desktop-save-buffer))))))
+       (no-regexp-to-check (not (stringp desktop-files-not-to-save)))
+       dired-skip)
+    (and (or filename
+            (not (stringp desktop-buffers-not-to-save))
+            (not (string-match-p desktop-buffers-not-to-save bufname)))
+        (not (memq mode desktop-modes-not-to-save))
+        (or (and filename
+                 (or no-regexp-to-check
+                     (not (string-match-p desktop-files-not-to-save filename))))
+            (and (memq mode '(dired-mode vc-dir-mode))
+                 (or no-regexp-to-check
+                     (not (setq dired-skip
+                                (with-current-buffer bufname
+                                  (string-match-p desktop-files-not-to-save
+                                                  default-directory))))))
+            (and (null filename)
+                 (null dired-skip)  ; bug#5755
+                 (with-current-buffer bufname desktop-save-buffer)))
+        t)))
 
 ;; ----------------------------------------------------------------------------
 (defun desktop-file-name (filename dirname)
 
 ;; ----------------------------------------------------------------------------
 (defun desktop-file-name (filename dirname)
@@ -889,224 +921,20 @@ 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))
-  "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
-
-(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))))
-
-(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--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)))
-
-(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--check-dont-save (frame)
+  (not (frame-parameter frame 'desktop-dont-save)))
+
+(defconst desktop--app-id `(desktop . ,desktop-file-version))
+
+(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."
 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
        (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)))))
+            (frameset-save nil
+                           :app desktop--app-id
+                           :name (concat user-login-name "@" system-name)
+                           :predicate #'desktop--check-dont-save))))
 
 ;;;###autoload
 (defun desktop-save (dirname &optional release auto-save)
 
 ;;;###autoload
 (defun desktop-save (dirname &optional release auto-save)
@@ -1115,7 +943,13 @@ Parameter DIRNAME specifies where to save the desktop file.
 Optional parameter RELEASE says whether we're done with this desktop.
 If AUTO-SAVE is non-nil, compare the saved contents to the one last saved,
 and don't save the buffer if they are the same."
 Optional parameter RELEASE says whether we're done with this desktop.
 If AUTO-SAVE is non-nil, compare the saved contents to the one last saved,
 and don't save the buffer if they are the same."
-  (interactive "DDirectory to save desktop file in: ")
+  (interactive (list
+                ;; Or should we just use (car desktop-path)?
+                (let ((default (if (member "." desktop-path)
+                                   default-directory
+                                 user-emacs-directory)))
+                  (read-directory-name "Directory to save desktop file in: "
+                                       default default t))))
   (setq desktop-dirname (file-name-as-directory (expand-file-name dirname)))
   (save-excursion
     (let ((eager desktop-restore-eager)
   (setq desktop-dirname (file-name-as-directory (expand-file-name dirname)))
   (save-excursion
     (let ((eager desktop-restore-eager)
@@ -1148,11 +982,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.
          (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)
          (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 "
          (when (memq 'kill-ring desktop-globals-to-save)
            (insert
             "(setq kill-ring-yank-pointer (nthcdr "
@@ -1180,12 +1014,21 @@ and don't save the buffer if they are the same."
                (insert ")\n\n"))))
 
          (setq default-directory desktop-dirname)
                (insert ")\n\n"))))
 
          (setq default-directory desktop-dirname)
-         ;; If auto-saving, avoid writing if nothing has changed since the last write.
-         ;; Don't check 300 characters of the header that contains the timestamp.
-         (let ((checksum (and auto-save (md5 (current-buffer)
-                                             (+ (point-min) 300) (point-max)
-                                             'emacs-mule))))
-           (unless (and auto-save (equal checksum desktop-file-checksum))
+         ;; When auto-saving, avoid writing if nothing has changed since the last write.
+         (let* ((beg (and auto-save
+                          (save-excursion
+                            (goto-char (point-min))
+                            ;; Don't check the header with changing timestamp
+                            (and (search-forward "Global section" nil t)
+                                 ;; Also skip the timestamp in desktop-saved-frameset
+                                 ;; if it's saved in the first non-header line
+                                 (search-forward "desktop-saved-frameset"
+                                                 (line-beginning-position 3) t)
+                                 ;; This is saved after the timestamp
+                                 (search-forward (format "%S" desktop--app-id) nil t))
+                            (point))))
+                (checksum (and beg (md5 (current-buffer) beg (point-max) 'emacs-mule))))
+           (unless (and checksum (equal checksum desktop-file-checksum))
              (let ((coding-system-for-write 'emacs-mule))
                (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage))
              (setq desktop-file-checksum checksum)
              (let ((coding-system-for-write 'emacs-mule))
                (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage))
              (setq desktop-file-checksum checksum)
@@ -1210,320 +1053,28 @@ This function also sets `desktop-dirname' to nil."
 (defvar desktop-lazy-timer nil)
 
 ;; ----------------------------------------------------------------------------
 (defvar desktop-lazy-timer nil)
 
 ;; ----------------------------------------------------------------------------
-(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'
+(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-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)."
 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)))))
-
-      ;; 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))))))
+  (when (desktop-restoring-frameset-p)
+    (frameset-restore desktop-saved-frameset
+                     :reuse-frames (eq desktop-restore-reuses-frames t)
+                     :cleanup-frames (not (eq desktop-restore-reuses-frames 'keep))
+                     :force-display desktop-restore-in-current-display
+                     :force-onscreen desktop-restore-forces-onscreen)))
 
 
+;; Just to silence the byte compiler.
+;; Dynamically bound in `desktop-read'.
+(defvar desktop-first-buffer)
+(defvar desktop-buffer-ok-count)
+(defvar desktop-buffer-fail-count)
+
+;; FIXME Interactively, this should have the option to prompt for dirname.
 ;;;###autoload
 (defun desktop-read (&optional dirname)
   "Read and process the desktop file in directory DIRNAME.
 ;;;###autoload
 (defun desktop-read (&optional dirname)
   "Read and process the desktop file in directory DIRNAME.
@@ -1577,13 +1128,13 @@ Using it may cause conflicts.  Use it anyway? " owner)))))
            (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))
            ;; If it wasn't already, mark it as in-use, to bother other
            ;; desktop instances.
            (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))
            ;; If it wasn't already, mark it as in-use, to bother other
            ;; desktop instances.
-           (unless owner
+           (unless (eq (emacs-pid) owner)
              (condition-case nil
                  (desktop-claim-lock)
                (file-error (message "Couldn't record use of desktop file")
                            (sit-for 1))))
 
              (condition-case nil
                  (desktop-claim-lock)
                (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
              ;; `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 +1144,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)
              (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)
            (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)
                     desktop-buffer-ok-count
                     (if (= 1 desktop-buffer-ok-count) "" "s")
                     (if (< 0 desktop-buffer-fail-count)
@@ -1605,7 +1161,7 @@ Using it may cause conflicts.  Use it anyway? " owner)))))
                         (format ", %d to restore lazily"
                                 (length desktop-buffer-args-list))
                       ""))
                         (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*"))
              ;; Bury the *Messages* buffer to not reshow it when burying
              ;; the buffer we switched to above.
              (when (buffer-live-p (get-buffer "*Messages*"))
@@ -1618,6 +1174,7 @@ Using it may cause conflicts.  Use it anyway? " owner)))))
              (walk-window-tree (lambda (window)
                                  (set-window-prev-buffers window nil)
                                  (set-window-next-buffers window nil))))
              (walk-window-tree (lambda (window)
                                  (set-window-prev-buffers window nil)
                                  (set-window-next-buffers window nil))))
+           (setq desktop-saved-frameset nil)
            t))
       ;; No desktop file found.
       (desktop-clear)
            t))
       ;; No desktop file found.
       (desktop-clear)
@@ -1675,21 +1232,24 @@ Called by the timer created in `desktop-auto-save-set-timer'."
             ;; Save only to own desktop file.
             (eq (emacs-pid) (desktop-owner))
             desktop-dirname)
             ;; Save only to own desktop file.
             (eq (emacs-pid) (desktop-owner))
             desktop-dirname)
-    (desktop-save desktop-dirname nil t))
-  (desktop-auto-save-set-timer))
+    (desktop-save desktop-dirname nil t)))
 
 (defun desktop-auto-save-set-timer ()
 
 (defun desktop-auto-save-set-timer ()
-  "Reset the auto-save timer.
+  "Set the auto-save timer.
 Cancel any previous timer.  When `desktop-auto-save-timeout' is a positive
 Cancel any previous timer.  When `desktop-auto-save-timeout' is a positive
-integer, start a new timer to call `desktop-auto-save' in that many seconds."
-  (when desktop-auto-save-timer
-    (cancel-timer desktop-auto-save-timer)
-    (setq desktop-auto-save-timer nil))
+integer, start a new idle timer to call `desktop-auto-save' repeatedly
+after that many seconds of idle time."
+  (desktop-auto-save-cancel-timer)
   (when (and (integerp desktop-auto-save-timeout)
             (> desktop-auto-save-timeout 0))
     (setq desktop-auto-save-timer
   (when (and (integerp desktop-auto-save-timeout)
             (> desktop-auto-save-timeout 0))
     (setq desktop-auto-save-timer
-         (run-with-timer desktop-auto-save-timeout nil
-                         'desktop-auto-save))))
+         (run-with-idle-timer desktop-auto-save-timeout t
+                              'desktop-auto-save))))
+
+(defun desktop-auto-save-cancel-timer ()
+  (when desktop-auto-save-timer
+    (cancel-timer desktop-auto-save-timer)
+    (setq desktop-auto-save-timer nil)))
 
 ;; ----------------------------------------------------------------------------
 ;;;###autoload
 
 ;; ----------------------------------------------------------------------------
 ;;;###autoload
@@ -1743,14 +1303,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.
 
 ;; 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
 (defun desktop-create-buffer
     (file-version
      buffer-filename
@@ -1834,20 +1386,21 @@ integer, start a new timer to call `desktop-auto-save' in that many seconds."
                 (eval desktop-buffer-point)
               (error (message "%s" (error-message-string err)) 1))))
          (when desktop-buffer-mark
                 (eval desktop-buffer-point)
               (error (message "%s" (error-message-string err)) 1))))
          (when desktop-buffer-mark
-           (if (consp desktop-buffer-mark)
-               (progn
-                 (set-mark (car desktop-buffer-mark))
-                 (setq mark-active (car (cdr desktop-buffer-mark))))
-             (set-mark desktop-buffer-mark)))
+            (if (consp desktop-buffer-mark)
+                (progn
+                  (move-marker (mark-marker) (car desktop-buffer-mark))
+                  ;; FIXME: Should we call (de)activate-mark instead?
+                  (setq mark-active (car (cdr desktop-buffer-mark))))
+              (move-marker (mark-marker) desktop-buffer-mark)))
          ;; Never override file system if the file really is read-only marked.
          (when desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
          (dolist (this desktop-buffer-locals)
            (if (consp this)
          ;; Never override file system if the file really is read-only marked.
          (when desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
          (dolist (this desktop-buffer-locals)
            (if (consp this)
-               ;; an entry of this form `(symbol . value)'
+               ;; An entry of this form `(symbol . value)'.
                (progn
                  (make-local-variable (car this))
                  (set (car this) (cdr this)))
                (progn
                  (make-local-variable (car this))
                  (set (car this) (cdr this)))
-             ;; an entry of the form `symbol'
+             ;; An entry of the form `symbol'.
              (make-local-variable this)
              (makunbound this))))))))
 
              (make-local-variable this)
              (makunbound this))))))))
 
@@ -1939,10 +1492,9 @@ If there are no buffers left to create, kill the timer."
     (let ((key "--no-desktop"))
       (when (member key command-line-args)
         (setq command-line-args (delete key command-line-args))
     (let ((key "--no-desktop"))
       (when (member key command-line-args)
         (setq command-line-args (delete key command-line-args))
-        (setq desktop-save-mode nil)))
+        (desktop-save-mode 0)))
     (when desktop-save-mode
       (desktop-read)
     (when desktop-save-mode
       (desktop-read)
-      (desktop-auto-save-set-timer)
       (setq inhibit-startup-screen t))))
 
 ;; So we can restore vc-dir buffers.
       (setq inhibit-startup-screen t))))
 
 ;; So we can restore vc-dir buffers.
@@ -1951,3 +1503,7 @@ If there are no buffers left to create, kill the timer."
 (provide 'desktop)
 
 ;;; desktop.el ends here
 (provide 'desktop)
 
 ;;; desktop.el ends here
+
+;; Local Variables:
+;; coding: utf-8
+;; End: