diff options
| author | Juanma Barranquero | 2013-07-15 02:07:51 +0200 |
|---|---|---|
| committer | Juanma Barranquero | 2013-07-15 02:07:51 +0200 |
| commit | b958c0ad58a129eca0e65bbdd72a135b5017d4a6 (patch) | |
| tree | 26a5179f96ea28c995c93b6741ec705ac294f3ac | |
| parent | 5c97beae62104207d0c9099919c7955c8e6f57fd (diff) | |
| download | emacs-b958c0ad58a129eca0e65bbdd72a135b5017d4a6.tar.gz emacs-b958c0ad58a129eca0e65bbdd72a135b5017d4a6.zip | |
lisp/desktop.el (desktop-restore-frames): Change default to t.
(desktop-restore-in-current-display): Now offer more options.
(desktop-restoring-reuses-frames): New customization option.
(desktop--saved-states): Doc fix.
(desktop-filter-parameters-alist): New variable, renamed and expanded from
desktop--excluded-frame-parameters.
(desktop--target-display): New variable.
(desktop-switch-to-gui-p, desktop-switch-to-tty-p, desktop--filter-tty*)
(desktop--filter-*-color, desktop--filter-minibuffer)
(desktop--filter-restore-desktop-parm, desktop--filter-save-desktop-parm)
(desktop-restore-in-original-display-p): New functions.
(desktop--filter-frame-parms): Use new desktop-filter-parameters-alist.
(desktop--save-minibuffer-frames): New function, inspired by a similar
function from Martin Rudalics.
(desktop--save-frames): Call it; play nice with desktop-globals-to-save.
(desktop--restore-in-this-display-p): Remove.
(desktop--find-frame): Rename from desktop--find-frame-in-display and add
predicate argument.
(desktop--make-full-frame): Remove, integrated into desktop--make-frame.
(desktop--reuse-list): New variable.
(desktop--select-frame, desktop--make-frame, desktop--sort-states):
New functions.
(desktop--restore-frames): Add support for "minibuffer-special" frames.
| -rw-r--r-- | etc/NEWS | 6 | ||||
| -rw-r--r-- | lisp/ChangeLog | 27 | ||||
| -rw-r--r-- | lisp/desktop.el | 507 |
3 files changed, 448 insertions, 92 deletions
| @@ -255,8 +255,10 @@ on the given date. | |||
| 255 | *** `desktop-auto-save-timeout' defines the number of seconds between | 255 | *** `desktop-auto-save-timeout' defines the number of seconds between |
| 256 | auto-saves of the desktop. | 256 | auto-saves of the desktop. |
| 257 | 257 | ||
| 258 | *** `desktop-restore-frames' enables saving and restoring the window/frame | 258 | *** `desktop-restore-frames', enabled by default, allows saving and |
| 259 | configuration. | 259 | restoring the window/frame configuration. Additional options |
| 260 | `desktop-restore-in-current-display' and | ||
| 261 | `desktop-restoring-reuses-frames' allow further customization. | ||
| 260 | 262 | ||
| 261 | ** Dired | 263 | ** Dired |
| 262 | 264 | ||
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 25d666f8f40..34154551fcd 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,30 @@ | |||
| 1 | 2013-07-14 Juanma Barranquero <lekktu@gmail.com> | ||
| 2 | |||
| 3 | * desktop.el (desktop-restore-frames): Change default to t. | ||
| 4 | (desktop-restore-in-current-display): Now offer more options. | ||
| 5 | (desktop-restoring-reuses-frames): New customization option. | ||
| 6 | (desktop--saved-states): Doc fix. | ||
| 7 | (desktop-filter-parameters-alist): New variable, renamed and expanded | ||
| 8 | from desktop--excluded-frame-parameters. | ||
| 9 | (desktop--target-display): New variable. | ||
| 10 | (desktop-switch-to-gui-p, desktop-switch-to-tty-p) | ||
| 11 | (desktop--filter-tty*, desktop--filter-*-color) | ||
| 12 | (desktop--filter-minibuffer, desktop--filter-restore-desktop-parm) | ||
| 13 | (desktop--filter-save-desktop-parm) | ||
| 14 | (desktop-restore-in-original-display-p): New functions. | ||
| 15 | (desktop--filter-frame-parms): Use new desktop-filter-parameters-alist. | ||
| 16 | (desktop--save-minibuffer-frames): New function, inspired by a similar | ||
| 17 | function from Martin Rudalics. | ||
| 18 | (desktop--save-frames): Call it; play nice with desktop-globals-to-save. | ||
| 19 | (desktop--restore-in-this-display-p): Remove. | ||
| 20 | (desktop--find-frame): Rename from desktop--find-frame-in-display | ||
| 21 | and add predicate argument. | ||
| 22 | (desktop--make-full-frame): Remove, integrated into desktop--make-frame. | ||
| 23 | (desktop--reuse-list): New variable. | ||
| 24 | (desktop--select-frame, desktop--make-frame, desktop--sort-states): | ||
| 25 | New functions. | ||
| 26 | (desktop--restore-frames): Add support for "minibuffer-special" frames. | ||
| 27 | |||
| 1 | 2013-07-14 Michael Albinus <michael.albinus@gmx.de> | 28 | 2013-07-14 Michael Albinus <michael.albinus@gmx.de> |
| 2 | 29 | ||
| 3 | * net/tramp-sh.el (tramp-sh-handle-vc-registered): Use `ignore-error'. | 30 | * net/tramp-sh.el (tramp-sh-handle-vc-registered): Use `ignore-error'. |
diff --git a/lisp/desktop.el b/lisp/desktop.el index 322b95715a2..c31cbead2b9 100644 --- a/lisp/desktop.el +++ b/lisp/desktop.el | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | ;; - the mark & mark-active | 33 | ;; - the mark & mark-active |
| 34 | ;; - buffer-read-only | 34 | ;; - buffer-read-only |
| 35 | ;; - some local variables | 35 | ;; - some local variables |
| 36 | ;; - frame and window configuration | ||
| 36 | 37 | ||
| 37 | ;; To use this, use customize to turn on desktop-save-mode or add the | 38 | ;; To use this, use customize to turn on desktop-save-mode or add the |
| 38 | ;; following line somewhere in your init file: | 39 | ;; following line somewhere in your init file: |
| @@ -127,7 +128,6 @@ | |||
| 127 | ;; --------------------------------------------------------------------------- | 128 | ;; --------------------------------------------------------------------------- |
| 128 | ;; TODO: | 129 | ;; TODO: |
| 129 | ;; | 130 | ;; |
| 130 | ;; Save window configuration. | ||
| 131 | ;; Recognize more minor modes. | 131 | ;; Recognize more minor modes. |
| 132 | ;; Save mark rings. | 132 | ;; Save mark rings. |
| 133 | 133 | ||
| @@ -369,16 +369,29 @@ modes are restored automatically; they should not be listed here." | |||
| 369 | :type '(repeat symbol) | 369 | :type '(repeat symbol) |
| 370 | :group 'desktop) | 370 | :group 'desktop) |
| 371 | 371 | ||
| 372 | (defcustom desktop-restore-frames nil | 372 | (defcustom desktop-restore-frames t |
| 373 | "When non-nil, save window/frame configuration to desktop file." | 373 | "When non-nil, save window/frame configuration to desktop file." |
| 374 | :type 'boolean | 374 | :type 'boolean |
| 375 | :group 'desktop | 375 | :group 'desktop |
| 376 | :version "24.4") | 376 | :version "24.4") |
| 377 | 377 | ||
| 378 | (defcustom desktop-restore-in-current-display nil | 378 | (defcustom desktop-restore-in-current-display nil |
| 379 | "When non-nil, frames are restored in the current display. | 379 | "If t, frames are restored in the current display. |
| 380 | Otherwise they are restored, if possible, in their original displays." | 380 | If nil, frames are restored, if possible, in their original displays. |
| 381 | :type 'boolean | 381 | If `delete', frames on other displays are deleted instead of restored." |
| 382 | :type '(choice (const :tag "Restore in current display" t) | ||
| 383 | (const :tag "Restore in original display" nil) | ||
| 384 | (const :tag "Delete frames in other displays" 'delete)) | ||
| 385 | :group 'desktop | ||
| 386 | :version "24.4") | ||
| 387 | |||
| 388 | (defcustom desktop-restoring-reuses-frames t | ||
| 389 | "If t, restoring frames reuses existing frames. | ||
| 390 | If nil, existing frames are deleted. | ||
| 391 | If `keep', existing frames are kept and not reused." | ||
| 392 | :type '(choice (const :tag "Reuse existing frames" t) | ||
| 393 | (const :tag "Delete existing frames" nil) | ||
| 394 | (const :tag "Keep existing frames" 'keep)) | ||
| 382 | :group 'desktop | 395 | :group 'desktop |
| 383 | :version "24.4") | 396 | :version "24.4") |
| 384 | 397 | ||
| @@ -566,7 +579,7 @@ DIRNAME omitted or nil means use `desktop-dirname'." | |||
| 566 | Used to avoid writing contents unchanged between auto-saves.") | 579 | Used to avoid writing contents unchanged between auto-saves.") |
| 567 | 580 | ||
| 568 | (defvar desktop--saved-states nil | 581 | (defvar desktop--saved-states nil |
| 569 | "Internal use only.") | 582 | "Saved window/frame state. Internal use only.") |
| 570 | 583 | ||
| 571 | ;; ---------------------------------------------------------------------------- | 584 | ;; ---------------------------------------------------------------------------- |
| 572 | ;; Desktop file conflict detection | 585 | ;; Desktop file conflict detection |
| @@ -869,30 +882,193 @@ DIRNAME must be the directory in which the desktop file will be saved." | |||
| 869 | 882 | ||
| 870 | 883 | ||
| 871 | ;; ---------------------------------------------------------------------------- | 884 | ;; ---------------------------------------------------------------------------- |
| 872 | (defconst desktop--excluded-frame-parameters | 885 | (defvar desktop-filter-parameters-alist |
| 873 | '(buffer-list | 886 | '((background-color . desktop--filter-*-color) |
| 874 | buffer-predicate | 887 | (buffer-list . t) |
| 875 | buried-buffer-list | 888 | (buffer-predicate . t) |
| 876 | explicit-name | 889 | (buried-buffer-list . t) |
| 877 | font | 890 | (desktop-font . desktop--filter-restore-desktop-parm) |
| 878 | font-backend | 891 | (desktop-fullscreen . desktop--filter-restore-desktop-parm) |
| 879 | minibuffer | 892 | (desktop-height . desktop--filter-restore-desktop-parm) |
| 880 | name | 893 | (desktop-width . desktop--filter-restore-desktop-parm) |
| 881 | outer-window-id | 894 | (font . desktop--filter-save-desktop-parm) |
| 882 | parent-id | 895 | (font-backend . t) |
| 883 | window-id | 896 | (foreground-color . desktop--filter-*-color) |
| 884 | window-system) | 897 | (fullscreen . desktop--filter-save-desktop-parm) |
| 885 | "Frame parameters not saved or restored.") | 898 | (height . desktop--filter-save-desktop-parm) |
| 886 | 899 | (minibuffer . desktop--filter-minibuffer) | |
| 887 | (defun desktop--filter-frame-parms (frame) | 900 | (name . t) |
| 888 | "Return frame parameters of FRAME. | 901 | (outer-window-id . t) |
| 889 | Parameters in `desktop--excluded-frame-parameters' are excluded. | 902 | (parent-id . t) |
| 903 | (tty . desktop--filter-tty*) | ||
| 904 | (tty-type . desktop--filter-tty*) | ||
| 905 | (width . desktop--filter-save-desktop-parm) | ||
| 906 | (window-id . t) | ||
| 907 | (window-system . t)) | ||
| 908 | "Alist of frame parameters and filtering functions. | ||
| 909 | |||
| 910 | Each element is a cons (PARAM . FILTER), where PARAM is a parameter | ||
| 911 | name (a symbol identifying a frame parameter), and FILTER can be t | ||
| 912 | \(meaning the parameter is removed from the parameter list on saving | ||
| 913 | and restoring), or a function that will be called with three args: | ||
| 914 | |||
| 915 | CURRENT a cons (PARAM . VALUE), where PARAM is the one being | ||
| 916 | filtered and VALUE is its current value | ||
| 917 | PARAMETERS the complete alist of parameters being filtered | ||
| 918 | SAVING non-nil if filtering before saving state, nil otherwise | ||
| 919 | |||
| 920 | The FILTER function must return: | ||
| 921 | nil CURRENT is removed from the list | ||
| 922 | t CURRENT is left as is | ||
| 923 | (PARAM' . VALUE') replace CURRENT with this | ||
| 924 | |||
| 925 | Frame parameters not on this list are passed intact.") | ||
| 926 | |||
| 927 | (defvar desktop--target-display nil | ||
| 928 | "Either (minibuffer . VALUE) or nil. | ||
| 929 | This refers to the current frame config being processed inside | ||
| 930 | `frame--restore-frames' and its auxiliary functions (like filtering). | ||
| 931 | If nil, there is no need to change the display. | ||
| 932 | If non-nil, display parameter to use when creating the frame. | ||
| 933 | Internal use only.") | ||
| 934 | |||
| 935 | (defun desktop-switch-to-gui-p (parameters) | ||
| 936 | "True when switching to a graphic display. | ||
| 937 | Return t if PARAMETERS describes a text-only terminal and | ||
| 938 | the target is a graphic display; otherwise return nil. | ||
| 939 | Only meaningful when called from a filtering function in | ||
| 940 | `desktop-filter-parameters-alist'." | ||
| 941 | (and desktop--target-display ; we're switching | ||
| 942 | (null (cdr (assq 'display parameters))) ; from a tty | ||
| 943 | (cdr desktop--target-display))) ; to a GUI display | ||
| 944 | |||
| 945 | (defun desktop-switch-to-tty-p (parameters) | ||
| 946 | "True when switching to a text-only terminal. | ||
| 947 | Return t if PARAMETERS describes a graphic display and | ||
| 948 | the target is a text-only terminal; otherwise return nil. | ||
| 949 | Only meaningful when called from a filtering function in | ||
| 950 | `desktop-filter-parameters-alist'." | ||
| 951 | (and desktop--target-display ; we're switching | ||
| 952 | (cdr (assq 'display parameters)) ; from a GUI display | ||
| 953 | (null (cdr desktop--target-display)))) ; to a tty | ||
| 954 | |||
| 955 | (defun desktop--filter-tty* (_current parameters saving) | ||
| 956 | ;; Remove tty and tty-type parameters when switching | ||
| 957 | ;; to a GUI frame. | ||
| 958 | (or saving | ||
| 959 | (not (desktop-switch-to-gui-p parameters)))) | ||
| 960 | |||
| 961 | (defun desktop--filter-*-color (current parameters saving) | ||
| 962 | ;; Remove (foreground|background)-color parameters | ||
| 963 | ;; when switching to a GUI frame if they denote an | ||
| 964 | ;; "unspecified" color. | ||
| 965 | (or saving | ||
| 966 | (not (desktop-switch-to-gui-p parameters)) | ||
| 967 | (not (stringp (cdr current))) | ||
| 968 | (not (string-match-p "^unspecified-[fb]g$" (cdr current))))) | ||
| 969 | |||
| 970 | (defun desktop--filter-minibuffer (current _parameters saving) | ||
| 971 | ;; When minibuffer is a window, save it as minibuffer . t | ||
| 972 | (or (not saving) | ||
| 973 | (if (windowp (cdr current)) | ||
| 974 | '(minibuffer . t) | ||
| 975 | t))) | ||
| 976 | |||
| 977 | (defun desktop--filter-restore-desktop-parm (current parameters saving) | ||
| 978 | ;; When switching to a GUI frame, convert desktop-XXX parameter to XXX | ||
| 979 | (or saving | ||
| 980 | (not (desktop-switch-to-gui-p parameters)) | ||
| 981 | (let ((val (cdr current))) | ||
| 982 | (if (eq val :desktop-processed) | ||
| 983 | nil | ||
| 984 | (cons (intern (substring (symbol-name (car current)) | ||
| 985 | 8)) ;; (length "desktop-") | ||
| 986 | val))))) | ||
| 987 | |||
| 988 | (defun desktop--filter-save-desktop-parm (current parameters saving) | ||
| 989 | ;; When switching to a tty frame, save parameter XXX as desktop-XXX so it | ||
| 990 | ;; can be restored in a subsequent GUI session, unless it already exists. | ||
| 991 | (cond (saving t) | ||
| 992 | ((desktop-switch-to-tty-p parameters) | ||
| 993 | (let ((sym (intern (format "desktop-%s" (car current))))) | ||
| 994 | (if (assq sym parameters) | ||
| 995 | nil | ||
| 996 | (cons sym (cdr current))))) | ||
| 997 | ((desktop-switch-to-gui-p parameters) | ||
| 998 | (let* ((dtp (assq (intern (format "desktop-%s" (car current))) | ||
| 999 | parameters)) | ||
| 1000 | (val (cdr dtp))) | ||
| 1001 | (if (eq val :desktop-processed) | ||
| 1002 | nil | ||
| 1003 | (setcdr dtp :desktop-processed) | ||
| 1004 | (cons (car current) val)))) | ||
| 1005 | (t t))) | ||
| 1006 | |||
| 1007 | (defun desktop-restore-in-original-display-p () | ||
| 1008 | "True if saved frames' displays should be honored." | ||
| 1009 | (cond ((daemonp) t) | ||
| 1010 | ((eq system-type 'windows-nt) nil) | ||
| 1011 | (t (null desktop-restore-in-current-display)))) | ||
| 1012 | |||
| 1013 | (defun desktop--filter-frame-parms (parameters saving) | ||
| 1014 | "Filter frame parameters and return filtered list. | ||
| 1015 | PARAMETERS is a parameter alist as returned by `frame-parameters'. | ||
| 1016 | If SAVING is non-nil, filtering is happening before saving frame state; | ||
| 1017 | otherwise, filtering is being done before restoring frame state. | ||
| 1018 | Parameters are filtered according to the setting of | ||
| 1019 | `desktop-filter-parameters-alist' (which see). | ||
| 890 | Internal use only." | 1020 | Internal use only." |
| 891 | (let (params) | 1021 | (let ((filtered nil)) |
| 892 | (dolist (param (frame-parameters frame)) | 1022 | (dolist (param parameters) |
| 893 | (unless (memq (car param) desktop--excluded-frame-parameters) | 1023 | (let ((filter (cdr (assq (car param) desktop-filter-parameters-alist))) |
| 894 | (push param params))) | 1024 | this) |
| 895 | params)) | 1025 | (cond (;; no filter: pass param |
| 1026 | (null filter) | ||
| 1027 | (push param filtered)) | ||
| 1028 | (;; filter = t; skip param | ||
| 1029 | (eq filter t)) | ||
| 1030 | (;; filter func returns nil: skip param | ||
| 1031 | (null (setq this (funcall filter param parameters saving)))) | ||
| 1032 | (;; filter func returns t: pass param | ||
| 1033 | (eq this t) | ||
| 1034 | (push param filtered)) | ||
| 1035 | (;; filter func returns a new param: use it | ||
| 1036 | t | ||
| 1037 | (push this filtered))))) | ||
| 1038 | ;; Set the display parameter after filtering, so that filter functions | ||
| 1039 | ;; have access to its original value. | ||
| 1040 | (when desktop--target-display | ||
| 1041 | (let ((display (assq 'display filtered))) | ||
| 1042 | (if display | ||
| 1043 | (setcdr display (cdr desktop--target-display)) | ||
| 1044 | (push desktop--target-display filtered)))) | ||
| 1045 | filtered)) | ||
| 1046 | |||
| 1047 | (defun desktop--save-minibuffer-frames () | ||
| 1048 | ;; Adds a desktop-mini parameter to frames | ||
| 1049 | ;; desktop-mini is a list (MINIBUFFER NUMBER DEFAULT?) where | ||
| 1050 | ;; MINIBUFFER t if the frame (including minibuffer-only) owns a minibuffer | ||
| 1051 | ;; NUMBER if MINIBUFFER = t, an ID for the frame; if nil, the ID of | ||
| 1052 | ;; the frame containing the minibuffer used by this frame | ||
| 1053 | ;; DEFAULT? if t, this frame is the value of default-minibuffer-frame | ||
| 1054 | ;; FIXME: What happens with multi-terminal sessions? | ||
| 1055 | (let ((frames (frame-list)) | ||
| 1056 | (count 0)) | ||
| 1057 | ;; Reset desktop-mini for all frames | ||
| 1058 | (dolist (frame frames) | ||
| 1059 | (set-frame-parameter frame 'desktop-mini nil)) | ||
| 1060 | ;; Number all frames with its own minibuffer | ||
| 1061 | (dolist (frame (minibuffer-frame-list)) | ||
| 1062 | (set-frame-parameter frame 'desktop-mini | ||
| 1063 | (list t | ||
| 1064 | (setq count (1+ count)) | ||
| 1065 | (eq frame default-minibuffer-frame)))) | ||
| 1066 | ;; Now link minibufferless frames with their minibuffer frames | ||
| 1067 | (dolist (frame frames) | ||
| 1068 | (unless (frame-parameter frame 'desktop-mini) | ||
| 1069 | (let* ((mb-frame (window-frame (minibuffer-window frame))) | ||
| 1070 | (this (cadr (frame-parameter mb-frame 'desktop-mini)))) | ||
| 1071 | (set-frame-parameter frame 'desktop-mini (list nil this nil))))))) | ||
| 896 | 1072 | ||
| 897 | (defun desktop--save-frames () | 1073 | (defun desktop--save-frames () |
| 898 | "Save window/frame state, as a global variable. | 1074 | "Save window/frame state, as a global variable. |
| @@ -900,12 +1076,14 @@ Intended to be called from `desktop-save'. | |||
| 900 | Internal use only." | 1076 | Internal use only." |
| 901 | (setq desktop--saved-states | 1077 | (setq desktop--saved-states |
| 902 | (and desktop-restore-frames | 1078 | (and desktop-restore-frames |
| 903 | (mapcar (lambda (frame) | 1079 | (progn |
| 904 | (cons (desktop--filter-frame-parms frame) | 1080 | (desktop--save-minibuffer-frames) |
| 905 | (window-state-get (frame-root-window frame) t))) | 1081 | (mapcar (lambda (frame) |
| 906 | (cons (selected-frame) | 1082 | (cons (desktop--filter-frame-parms (frame-parameters frame) t) |
| 907 | (delq (selected-frame) (frame-list)))))) | 1083 | (window-state-get (frame-root-window frame) t))) |
| 908 | (desktop-outvar 'desktop--saved-states)) | 1084 | (frame-list))))) |
| 1085 | (unless (memq 'desktop--saved-states desktop-globals-to-save) | ||
| 1086 | (desktop-outvar 'desktop--saved-states))) | ||
| 909 | 1087 | ||
| 910 | ;;;###autoload | 1088 | ;;;###autoload |
| 911 | (defun desktop-save (dirname &optional release auto-save) | 1089 | (defun desktop-save (dirname &optional release auto-save) |
| @@ -1006,71 +1184,220 @@ This function also sets `desktop-dirname' to nil." | |||
| 1006 | (defvar desktop-lazy-timer nil) | 1184 | (defvar desktop-lazy-timer nil) |
| 1007 | 1185 | ||
| 1008 | ;; ---------------------------------------------------------------------------- | 1186 | ;; ---------------------------------------------------------------------------- |
| 1009 | (defun desktop--restore-in-this-display-p () | 1187 | (defvar desktop--reuse-list nil |
| 1010 | (or desktop-restore-in-current-display | 1188 | "Internal use only.") |
| 1011 | (and (eq system-type 'windows-nt) (not (display-graphic-p))))) | 1189 | |
| 1012 | 1190 | (defun desktop--find-frame (predicate display &rest args) | |
| 1013 | (defun desktop--find-frame-in-display (frames display) | 1191 | "Find a suitable frame in `desktop--reuse-list'. |
| 1014 | (let (result) | 1192 | Look through frames whose display property matches DISPLAY and |
| 1015 | (while (and frames (not result)) | 1193 | return the first one for which (PREDICATE frame ARGS) returns t. |
| 1016 | (if (equal display (frame-parameter (car frames) 'display)) | 1194 | If PREDICATE is nil, it is always satisfied. Internal use only. |
| 1017 | (setq result (car frames)) | 1195 | This is an auxiliary function for `desktop--select-frame'." |
| 1018 | (setq frames (cdr frames)))) | 1196 | (catch :found |
| 1019 | result)) | 1197 | (dolist (frame desktop--reuse-list) |
| 1020 | 1198 | (when (and (equal (frame-parameter frame 'display) display) | |
| 1021 | (defun desktop--make-full-frame (full display config) | 1199 | (or (null predicate) |
| 1022 | (let ((width (and (eq full 'fullheight) (cdr (assq 'width config)))) | 1200 | (apply predicate frame args))) |
| 1023 | (height (and (eq full 'fullwidth) (cdr (assq 'height config)))) | 1201 | (throw :found frame))) |
| 1024 | (params '((visibility))) | 1202 | nil)) |
| 1203 | |||
| 1204 | (defun desktop--select-frame (display frame-cfg) | ||
| 1205 | "Look for an existing frame to reuse. | ||
| 1206 | DISPLAY is the display where the frame will be shown, and FRAME-CFG | ||
| 1207 | is the parameter list of the frame being restored. Internal use only." | ||
| 1208 | (if (eq desktop-restoring-reuses-frames t) | ||
| 1209 | (let ((frame nil) | ||
| 1210 | mini) | ||
| 1211 | ;; There are no fancy heuristics there. We could implement some | ||
| 1212 | ;; based on frame size and/or position, etc., but it is not clear | ||
| 1213 | ;; that any "gain" (in the sense of reduced flickering, etc.) is | ||
| 1214 | ;; worth the added complexity. In fact, the code below mainly | ||
| 1215 | ;; tries to work nicely when M-x desktop-read is used after a desktop | ||
| 1216 | ;; session has already been loaded. The other main use case, which | ||
| 1217 | ;; is the initial desktop-read upon starting Emacs, should usually | ||
| 1218 | ;; only have one, or very few, frame(s) to reuse. | ||
| 1219 | (cond (;; When the target is tty, every existing frame is reusable. | ||
| 1220 | (null display) | ||
| 1221 | (setq frame (desktop--find-frame nil display))) | ||
| 1222 | (;; If the frame has its own minibuffer, let's see whether | ||
| 1223 | ;; that frame has already been loaded (which can happen after | ||
| 1224 | ;; M-x desktop-read). | ||
| 1225 | (car (setq mini (cdr (assq 'desktop-mini frame-cfg)))) | ||
| 1226 | (setq frame (or (desktop--find-frame | ||
| 1227 | (lambda (f m) | ||
| 1228 | (equal (frame-parameter f 'desktop-mini) m)) | ||
| 1229 | display mini)))) | ||
| 1230 | (;; For minibufferless frames, check whether they already exist, | ||
| 1231 | ;; and that they are linked to the right minibuffer frame. | ||
| 1232 | mini | ||
| 1233 | (setq frame (desktop--find-frame | ||
| 1234 | (lambda (f n) | ||
| 1235 | (let ((m (frame-parameter f 'desktop-mini))) | ||
| 1236 | (and m | ||
| 1237 | (null (car m)) | ||
| 1238 | (= (cadr m) n) | ||
| 1239 | (equal (cadr (frame-parameter | ||
| 1240 | (window-frame (minibuffer-window f)) | ||
| 1241 | 'desktop-mini)) | ||
| 1242 | n)))) | ||
| 1243 | display (cadr mini)))) | ||
| 1244 | (;; Default to just finding a frame in the same display. | ||
| 1245 | t | ||
| 1246 | (setq frame (desktop--find-frame nil display)))) | ||
| 1247 | ;; If found, remove from the list. | ||
| 1248 | (when frame | ||
| 1249 | (setq desktop--reuse-list (delq frame desktop--reuse-list))) | ||
| 1025 | frame) | 1250 | frame) |
| 1026 | (when width | 1251 | nil)) |
| 1027 | (setq params (append `((user-size . t) (width . ,width)) params) | 1252 | |
| 1028 | config (assq-delete-all 'height config))) | 1253 | (defun desktop--make-frame (frame-cfg window-cfg) |
| 1029 | (when height | 1254 | "Set up a frame according to its saved state. |
| 1030 | (setq params (append `((user-size . t) (height . ,height)) params) | 1255 | That means either creating a new frame or reusing an existing one. |
| 1031 | config (assq-delete-all 'width config))) | 1256 | FRAME-CFG is the parameter list of the new frame; WINDOW-CFG is |
| 1032 | (setq frame (make-frame-on-display display params)) | 1257 | its window state. Internal use only." |
| 1033 | (modify-frame-parameters frame config) | 1258 | (let* ((fullscreen (cdr (assq 'fullscreen frame-cfg))) |
| 1259 | (lines (assq 'tool-bar-lines frame-cfg)) | ||
| 1260 | (filtered-cfg (desktop--filter-frame-parms frame-cfg nil)) | ||
| 1261 | (display (cdr (assq 'display filtered-cfg))) ;; post-filtering | ||
| 1262 | alt-cfg frame) | ||
| 1263 | |||
| 1264 | ;; This works around bug#14795 (or feature#14795, if not a bug :-) | ||
| 1265 | (setq filtered-cfg (assq-delete-all 'tool-bar-lines filtered-cfg)) | ||
| 1266 | (push '(tool-bar-lines . 0) filtered-cfg) | ||
| 1267 | |||
| 1268 | (when fullscreen | ||
| 1269 | ;; Currently Emacs has the limitation that it does not record the size | ||
| 1270 | ;; and position of a frame before maximizing it, so we cannot save & | ||
| 1271 | ;; restore that info. Instead, when restoring, we resort to creating | ||
| 1272 | ;; invisible "fullscreen" frames of default size and then maximizing them | ||
| 1273 | ;; (and making them visible) which at least is somewhat user-friendly | ||
| 1274 | ;; when these frames are later de-maximized. | ||
| 1275 | (let ((width (and (eq fullscreen 'fullheight) (cdr (assq 'width filtered-cfg)))) | ||
| 1276 | (height (and (eq fullscreen 'fullwidth) (cdr (assq 'height filtered-cfg)))) | ||
| 1277 | (visible (assq 'visibility filtered-cfg))) | ||
| 1278 | (dolist (parameter '(visibility fullscreen width height)) | ||
| 1279 | (setq filtered-cfg (assq-delete-all parameter filtered-cfg))) | ||
| 1280 | (when width | ||
| 1281 | (setq filtered-cfg (append `((user-size . t) (width . ,width)) | ||
| 1282 | filtered-cfg))) | ||
| 1283 | (when height | ||
| 1284 | (setq filtered-cfg (append `((user-size . t) (height . ,height)) | ||
| 1285 | filtered-cfg))) | ||
| 1286 | ;; These are parameters to apply after creating/setting the frame. | ||
| 1287 | (push visible alt-cfg) | ||
| 1288 | (push (cons 'fullscreen fullscreen) alt-cfg))) | ||
| 1289 | |||
| 1290 | ;; Time to select or create a frame an apply the big bunch of parameters | ||
| 1291 | (if (setq frame (desktop--select-frame display filtered-cfg)) | ||
| 1292 | (modify-frame-parameters frame filtered-cfg) | ||
| 1293 | (setq frame (make-frame-on-display display filtered-cfg))) | ||
| 1294 | |||
| 1295 | ;; Let's give the finishing touches (visibility, tool-bar, maximization). | ||
| 1296 | (when lines (push lines alt-cfg)) | ||
| 1297 | (when alt-cfg (modify-frame-parameters frame alt-cfg)) | ||
| 1298 | ;; Now restore window state. | ||
| 1299 | (window-state-put window-cfg (frame-root-window frame) 'safe) | ||
| 1034 | frame)) | 1300 | frame)) |
| 1035 | 1301 | ||
| 1302 | (defun desktop--sort-states (state1 state2) | ||
| 1303 | ;; Order: default minibuffer frame | ||
| 1304 | ;; other frames with minibuffer, ascending ID | ||
| 1305 | ;; minibufferless frames, ascending ID | ||
| 1306 | (let ((dm1 (cdr (assq 'desktop-mini (car state1)))) | ||
| 1307 | (dm2 (cdr (assq 'desktop-mini (car state2))))) | ||
| 1308 | (cond ((nth 2 dm1) t) | ||
| 1309 | ((nth 2 dm2) nil) | ||
| 1310 | ((null (car dm2)) t) | ||
| 1311 | ((null (car dm1)) nil) | ||
| 1312 | (t (< (cadr dm1) (cadr dm2)))))) | ||
| 1313 | |||
| 1036 | (defun desktop--restore-frames () | 1314 | (defun desktop--restore-frames () |
| 1037 | "Restore window/frame configuration. | 1315 | "Restore window/frame configuration. |
| 1038 | Internal use only." | 1316 | Internal use only." |
| 1039 | (when (and desktop-restore-frames desktop--saved-states) | 1317 | (when (and desktop-restore-frames desktop--saved-states) |
| 1040 | (let ((frames (frame-list)) | 1318 | (let* ((frame-mb-map nil) ;; Alist of frames with their own minibuffer |
| 1041 | (current (frame-parameter nil 'display)) | 1319 | (visible nil) |
| 1042 | (selected nil)) | 1320 | (delete-saved (eq desktop-restore-in-current-display 'delete)) |
| 1321 | (forcing (not (desktop-restore-in-original-display-p))) | ||
| 1322 | (target (and forcing (cons 'display (frame-parameter nil 'display))))) | ||
| 1323 | |||
| 1324 | ;; Sorting saved states allows us to easily restore minibuffer-owning frames | ||
| 1325 | ;; before minibufferless ones. | ||
| 1326 | (setq desktop--saved-states (sort desktop--saved-states #'desktop--sort-states)) | ||
| 1327 | ;; Potentially all existing frames are reusable. Later we will decide which ones | ||
| 1328 | ;; to reuse, and how to deal with any leftover. | ||
| 1329 | (setq desktop--reuse-list (frame-list)) | ||
| 1330 | |||
| 1043 | (dolist (state desktop--saved-states) | 1331 | (dolist (state desktop--saved-states) |
| 1044 | (condition-case err | 1332 | (condition-case err |
| 1045 | (let* ((config (car state)) | 1333 | (let* ((frame-cfg (car state)) |
| 1046 | (display (if (desktop--restore-in-this-display-p) | 1334 | (window-cfg (cdr state)) |
| 1047 | (setcdr (assq 'display config) current) | 1335 | (d-mini (cdr (assq 'desktop-mini frame-cfg))) |
| 1048 | (cdr (assq 'display config)))) | 1336 | num frame to-tty) |
| 1049 | (full (cdr (assq 'fullscreen config))) | 1337 | ;; Only set target if forcing displays and the target display is different. |
| 1050 | (frame (and (not full) | 1338 | (if (or (not forcing) |
| 1051 | (desktop--find-frame-in-display frames display)))) | 1339 | (equal target (or (assq 'display frame-cfg) '(display . nil)))) |
| 1052 | (cond (full | 1340 | (setq desktop--target-display nil) |
| 1053 | ;; treat fullscreen/maximized frames specially | 1341 | (setq desktop--target-display target |
| 1054 | (setq frame (desktop--make-full-frame full display config))) | 1342 | to-tty (null (cdr target)))) |
| 1055 | (frame | 1343 | ;; Time to restore frames and set up their minibuffers as they were. |
| 1056 | ;; found a frame in the right display -- reuse | 1344 | ;; We only skip a frame (thus deleting it) if either: |
| 1057 | (setq frames (delq frame frames)) | 1345 | ;; - we're switching displays, and the user chose the option to delete, or |
| 1058 | (modify-frame-parameters frame config)) | 1346 | ;; - we're switching to tty, and the frame to restore is minibuffer-only. |
| 1059 | (t | 1347 | (unless (and desktop--target-display |
| 1060 | ;; no frames in the display -- make a new one | 1348 | (or delete-saved |
| 1061 | (setq frame (make-frame-on-display display config)))) | 1349 | (and to-tty |
| 1062 | ;; restore windows | 1350 | (eq (cdr (assq 'minibuffer frame-cfg)) 'only)))) |
| 1063 | (window-state-put (cdr state) (frame-root-window frame) 'safe) | 1351 | |
| 1064 | (unless selected (setq selected frame))) | 1352 | ;; Restore minibuffers. Some of this stuff could be done in a filter |
| 1353 | ;; function, but it would be messy because restoring minibuffers affects | ||
| 1354 | ;; global state; it's best to do it here than add a bunch of global | ||
| 1355 | ;; variables to pass info back-and-forth to/from the filter function. | ||
| 1356 | (cond | ||
| 1357 | ((null d-mini)) ;; No desktop-mini. Process as normal frame. | ||
| 1358 | (to-tty) ;; Ignore minibuffer stuff and process as normal frame. | ||
| 1359 | ((car d-mini) ;; Frame has its own minibuffer (or it is minibuffer-only). | ||
| 1360 | (setq num (cadr d-mini)) | ||
| 1361 | (when (eq (cdr (assq 'minibuffer frame-cfg)) 'only) | ||
| 1362 | (setq frame-cfg (append '((tool-bar-lines . 0) (menu-bar-lines . 0)) | ||
| 1363 | frame-cfg)))) | ||
| 1364 | (t ;; Frame depends on other frame's minibufer window. | ||
| 1365 | (let ((mb-frame (cdr (assq (cadr d-mini) frame-mb-map)))) | ||
| 1366 | (unless (frame-live-p mb-frame) | ||
| 1367 | (error "Minibuffer frame %s not found" (cadr d-mini))) | ||
| 1368 | (let ((mb-param (assq 'minibuffer frame-cfg)) | ||
| 1369 | (mb-window (minibuffer-window mb-frame))) | ||
| 1370 | (unless (and (window-live-p mb-window) | ||
| 1371 | (window-minibuffer-p mb-window)) | ||
| 1372 | (error "Not a minibuffer window %s" mb-window)) | ||
| 1373 | (if mb-param | ||
| 1374 | (setcdr mb-param mb-window) | ||
| 1375 | (push (cons 'minibuffer mb-window) frame-cfg)))))) | ||
| 1376 | ;; OK, we're ready at last to create (or reuse) a frame and | ||
| 1377 | ;; restore the window config. | ||
| 1378 | (setq frame (desktop--make-frame frame-cfg window-cfg)) | ||
| 1379 | ;; Set default-minibuffer if required. | ||
| 1380 | (when (nth 2 d-mini) (setq default-minibuffer-frame frame)) | ||
| 1381 | ;; Store frame/NUM to assign to minibufferless frames. | ||
| 1382 | (when num (push (cons num frame) frame-mb-map)) | ||
| 1383 | ;; Try to locate at least one visible frame. | ||
| 1384 | (when (and (not visible) (frame-visible-p frame)) | ||
| 1385 | (setq visible frame)))) | ||
| 1065 | (error | 1386 | (error |
| 1066 | (message "Error restoring frame: %S" (error-message-string err))))) | 1387 | (delay-warning 'desktop (error-message-string err) :error)))) |
| 1067 | (when selected | 1388 | |
| 1068 | ;; make sure the original selected frame is visible and selected | 1389 | ;; Delete remaining frames, but do not fail if some resist being deleted. |
| 1069 | (unless (or (frame-parameter selected 'visibility) (daemonp)) | 1390 | (unless (eq desktop-restoring-reuses-frames 'keep) |
| 1070 | (modify-frame-parameters selected '((visibility . t)))) | 1391 | (dolist (frame desktop--reuse-list) |
| 1071 | (select-frame-set-input-focus selected) | 1392 | (ignore-errors (delete-frame frame)))) |
| 1072 | ;; delete any remaining frames | 1393 | (setq desktop--reuse-list nil) |
| 1073 | (mapc #'delete-frame frames))))) | 1394 | ;; Make sure there's at least one visible frame, and select it. |
| 1395 | (unless (or visible (daemonp)) | ||
| 1396 | (setq visible (if (frame-live-p default-minibuffer-frame) | ||
| 1397 | default-minibuffer-frame | ||
| 1398 | (car (frame-list)))) | ||
| 1399 | (make-frame-visible visible) | ||
| 1400 | (select-frame-set-input-focus visible))))) | ||
| 1074 | 1401 | ||
| 1075 | ;;;###autoload | 1402 | ;;;###autoload |
| 1076 | (defun desktop-read (&optional dirname) | 1403 | (defun desktop-read (&optional dirname) |