aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/frameset.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/frameset.el')
-rw-r--r--lisp/frameset.el1169
1 files changed, 859 insertions, 310 deletions
diff --git a/lisp/frameset.el b/lisp/frameset.el
index 63ee9af23fc..53ab0a5f081 100644
--- a/lisp/frameset.el
+++ b/lisp/frameset.el
@@ -41,155 +41,560 @@
41(require 'cl-lib) 41(require 'cl-lib)
42 42
43 43
44;; Framesets have two fields: 44(cl-defstruct (frameset (:type vector) :named
45;; - properties: a property list to store both frameset-specific and 45 (:constructor frameset--make)
46;; user-defined serializable data. Currently defined properties 46 ;; Copier is defined below.
47;; include: 47 (:copier nil))
48;; :version ID - Identifies the version of the frameset struct; 48
49;; this is the only property always present and 49 "A frameset encapsulates a serializable view of a set of frames and windows.
50;; must not be modified. 50
51;; :app APPINFO - Freeform. Can be used by applications and 51It contains the following slots, which can be accessed with
52;; packages to indicate the intended (but by no 52\(frameset-SLOT fs) and set with (setf (frameset-SLOT fs) VALUE):
53;; means exclusive) use of the frameset. For 53
54;; example, currently desktop.el sets :app to 54 version A read-only version number, identifying the format
55;; `(desktop . ,desktop-file-version). 55 of the frameset struct. Currently its value is 1.
56;; :name NAME - The name of the frameset instance; a string. 56 timestamp A read-only timestamp, the output of `current-time'.
57;; :desc TEXT - A description for user consumption (to choose 57 app A symbol, or a list whose first element is a symbol, which
58;; among framesets, etc.); a string. 58 identifies the creator of the frameset and related info;
59;; - states: an alist of items (FRAME-PARAMETERS . WINDOW-STATE) in 59 for example, desktop.el sets this slot to a list
60;; no particular order. Each item represents a frame to be 60 `(desktop . ,desktop-file-version).
61;; restored. 61 name A string, the name of the frameset instance.
62 62 description A string, a description for user consumption (to show in
63(cl-defstruct (frameset (:type list) :named 63 menus, messages, etc).
64 (:copier nil) 64 properties A property list, to store both frameset-specific and
65 (:predicate nil)) 65 user-defined serializable data.
66 properties ;; property list 66 states A list of items (FRAME-PARAMETERS . WINDOW-STATE), in no
67 states) ;; list of conses (frame-state . window-state) 67 particular order. Each item represents a frame to be
68 68 restored. FRAME-PARAMETERS is a frame's parameter alist,
69(defun copy-frameset (frameset) 69 extracted with (frame-parameters FRAME) and filtered
70 "Return a copy of FRAMESET. 70 through `frameset-filter-params'.
71This is a deep copy done with `copy-tree'." 71 WINDOW-STATE is the output of `window-state-get' applied
72 to the root window of the frame.
73
74To avoid collisions, it is recommended that applications wanting to add
75private serializable data to `properties' either store all info under a
76single, distinctive name, or use property names with a well-chosen prefix.
77
78A frameset is intended to be used through the following simple API:
79
80 - `frameset-save', the type's constructor, captures all or a subset of the
81 live frames, and returns a serializable snapshot of them (a frameset).
82 - `frameset-restore' takes a frameset, and restores the frames and windows
83 it describes, as faithfully as possible.
84 - `frameset-p' is the predicate for the frameset type.
85 - `frameset-valid-p' checks a frameset's validity.
86 - `frameset-copy' returns a deep copy of a frameset.
87 - `frameset-prop' is a `setf'able accessor for the contents of the
88 `properties' slot.
89 - The `frameset-SLOT' accessors described above."
90
91 (version 1 :read-only t)
92 (timestamp (current-time) :read-only t)
93 (app nil)
94 (name nil)
95 (description nil)
96 (properties nil)
97 (states nil))
98
99;; Add nicer docstrings for built-in predicate and accessors.
100(put 'frameset-p 'function-documentation
101 "Return non-nil if OBJECT is a frameset, nil otherwise.\n\n(fn OBJECT)")
102(put 'frameset-version 'function-documentation
103 "Return the version number of FRAMESET.\n
104It is an integer that identifies the format of the frameset struct.
105This slot cannot be modified.\n\n(fn FRAMESET)")
106(put 'frameset-timestamp 'function-documentation
107 "Return the creation timestamp of FRAMESET.\n
108The value is in the format returned by `current-time'.
109This slot cannot be modified.\n\n(fn FRAMESET)")
110(put 'frameset-app 'function-documentation
111 "Return the application identifier for FRAMESET.\n
112The value is either a symbol, like `my-app', or a list
113\(my-app ADDITIONAL-DATA...).\n\n(fn FRAMESET)")
114(put 'frameset-name 'function-documentation
115 "Return the name of FRAMESET (a string).\n\n(fn FRAMESET)")
116(put 'frameset-description 'function-documentation
117 "Return the description of FRAMESET (a string).\n\n(fn FRAMESET)")
118(put 'frameset-properties 'function-documentation
119 "Return the property list of FRAMESET.\n
120This list is useful to store both frameset-specific and user-defined
121serializable data. The simplest way to access and modify it is
122through `frameset-prop' (which see).\n\n(fn FRAMESET)")
123(put 'frameset-states 'function-documentation
124 "Return the list of frame states of FRAMESET.\n
125A frame state is a pair (FRAME-PARAMETERS . WINDOW-STATE), where
126FRAME-PARAMETERS is a frame's parameter alist, extracted with
127\(frame-parameters FRAME) and filtered through `frameset-filter-params',
128and WINDOW-STATE is the output of `window-state-get' applied to the
129root window of the frame.\n
130IMPORTANT: Modifying this slot may cause frameset functions to fail,
131unless the type constraints defined above are respected.\n\n(fn FRAMESET)")
132
133;; We autoloaded this for use in register.el, but now that we use registerv
134;; objects, this autoload is not useful any more.
135;; ;;;###autoload (autoload 'frameset-p "frameset"
136;; ;;;###autoload "Return non-nil if OBJECT is a frameset, nil otherwise." nil)
137
138(defun frameset-copy (frameset)
139 "Return a deep copy of FRAMESET.
140FRAMESET is copied with `copy-tree'."
72 (copy-tree frameset t)) 141 (copy-tree frameset t))
73 142
74;;;###autoload 143(defun frameset-valid-p (object)
75(defun frameset-p (frameset) 144 "Return non-nil if OBJECT is a valid frameset, nil otherwise."
76 "If FRAMESET is a frameset, return its :version. 145 (and (frameset-p object)
77Else return nil." 146 (integerp (frameset-version object))
78 (and (eq (car-safe frameset) 'frameset) 147 (consp (frameset-timestamp object))
79 (plist-get (cl-second frameset) :version))) 148 (let ((app (frameset-app object)))
149 (or (null app) ; APP is nil
150 (symbolp app) ; or a symbol
151 (and (consp app) ; or a list
152 (symbolp (car app))))) ; starting with a symbol
153 (stringp (or (frameset-name object) ""))
154 (stringp (or (frameset-description object) ""))
155 (listp (frameset-properties object))
156 (let ((states (frameset-states object)))
157 (and (listp states)
158 (cl-every #'consp (frameset-states object))))
159 (frameset-version object))) ; And VERSION is non-nil.
160
161(defun frameset--prop-setter (frameset property value)
162 "Setter function for `frameset-prop'. Internal use only."
163 (setf (frameset-properties frameset)
164 (plist-put (frameset-properties frameset) property value))
165 value)
80 166
81;; A setf'able accessor to the frameset's properties 167;; A setf'able accessor to the frameset's properties
82(defun frameset-prop (frameset prop) 168(defun frameset-prop (frameset property)
83 "Return the value of the PROP property of FRAMESET. 169 "Return the value for FRAMESET of PROPERTY.
84
85Properties other than :version can be set with
86 170
87 (setf (frameset-prop FRAMESET PROP) NEW-VALUE)" 171Properties can be set with
88 (plist-get (frameset-properties frameset) prop))
89 172
90(gv-define-setter frameset-prop (val fs prop) 173 (setf (frameset-prop FRAMESET PROPERTY) NEW-VALUE)"
91 (macroexp-let2 nil v val 174 (declare (gv-setter frameset--prop-setter))
92 `(progn 175 (plist-get (frameset-properties frameset) property))
93 (cl-assert (not (eq ,prop :version)) t ":version can not be set")
94 (setf (frameset-properties ,fs)
95 (plist-put (frameset-properties ,fs) ,prop ,v))
96 ,v)))
97 176
98 177
99;; Filtering 178;; Filtering
100 179
101(defvar frameset-filter-alist 180;; What's the deal with these "filter alists"?
102 '((background-color . frameset-filter-sanitize-color) 181;;
103 (buffer-list . t) 182;; Let's say that Emacs' frame parameters were never designed as a tool to
104 (buffer-predicate . t) 183;; precisely record (or restore) a frame's state. They grew organically,
105 (buried-buffer-list . t) 184;; and their uses and behaviors reflect their history. In using them to
106 (font . frameset-filter-save-parm) 185;; implement framesets, the unwary implementer, or the prospective package
107 (foreground-color . frameset-filter-sanitize-color) 186;; writer willing to use framesets in their code, might fall victim of some
108 (fullscreen . frameset-filter-save-parm) 187;; unexpected... oddities.
109 (GUI:font . frameset-filter-restore-parm) 188;;
110 (GUI:fullscreen . frameset-filter-restore-parm) 189;; You can find frame parameters that:
111 (GUI:height . frameset-filter-restore-parm) 190;;
112 (GUI:width . frameset-filter-restore-parm) 191;; - can be used to get and set some data from the frame's current state
113 (height . frameset-filter-save-parm) 192;; (`height', `width')
114 (left . frameset-filter-iconified) 193;; - can be set at creation time, and setting them afterwards has no effect
115 (minibuffer . frameset-filter-minibuffer) 194;; (`window-state', `minibuffer')
116 (top . frameset-filter-iconified) 195;; - can be set at creation time, and setting them afterwards will fail with
117 (width . frameset-filter-save-parm)) 196;; an error, *unless* you set it to the same value, a noop (`border-width')
197;; - act differently when passed at frame creation time, and when set
198;; afterwards (`height')
199;; - affect the value of other parameters (`name', `visibility')
200;; - can be ignored by window managers (most positional args, like `height',
201;; `width', `left' and `top', and others, like `auto-raise', `auto-lower')
202;; - can be set externally in X resources or Window registry (again, most
203;; positional parameters, and also `toolbar-lines', `menu-bar-lines' etc.)
204;, - can contain references to live objects (`buffer-list', `minibuffer') or
205;; code (`buffer-predicate')
206;; - are set automatically, and cannot be changed (`window-id', `parent-id'),
207;; but setting them produces no error
208;; - have a noticeable effect in some window managers, and are ignored in
209;; others (`menu-bar-lines')
210;; - can not be safely set in a tty session and then copied back to a GUI
211;; session (`font', `background-color', `foreground-color')
212;;
213;; etc etc.
214;;
215;; Which means that, in order to save a parameter alist to disk and read it
216;; back later to reconstruct a frame, some processing must be done. That's
217;; what `frameset-filter-params' and the `frameset-*-filter-alist' variables
218;; are for.
219;;
220;; First, a clarification. The word "filter" in these names refers to both
221;; common meanings of filter: to filter out (i.e., to remove), and to pass
222;; through a transformation function (think `filter-buffer-substring').
223;;
224;; `frameset-filter-params' takes a parameter alist PARAMETERS, a filtering
225;; alist FILTER-ALIST, and a flag SAVING to indicate whether we are filtering
226;; parameters with the intent of saving a frame or restoring it. It then
227;; accumulates an output alist, FILTERED, by checking each parameter in
228;; PARAMETERS against FILTER-ALIST and obeying any rule found there. The
229;; absence of a rule just means the parameter/value pair (called CURRENT in
230;; filtering functions) is copied to FILTERED as is. Keyword values :save,
231;; :restore and :never tell the function to copy CURRENT to FILTERED in the
232;; respective situations, that is, when saving, restoring, or never at all.
233;; Values :save and :restore are not used in this package, because usually if
234;; you don't want to save a parameter, you don't want to restore it either.
235;; But they can be useful, for example, if you already have a saved frameset
236;; created with some intent, and want to reuse it for a different objective
237;; where the expected parameter list has different requirements.
238;;
239;; Finally, the value can also be a filtering function, or a filtering
240;; function plus some arguments. The function is called for each matching
241;; parameter, and receives CURRENT (the parameter/value pair being processed),
242;; FILTERED (the output alist so far), PARAMETERS (the full parameter alist),
243;; SAVING (the save/restore flag), plus any additional ARGS set along the
244;; function in the `frameset-*-filter-alist' entry. The filtering function
245;; then has the possibility to pass along CURRENT, or reject it altogether,
246;; or pass back a (NEW-PARAM . NEW-VALUE) pair, which does not even need to
247;; refer to the same parameter (so you can filter `width' and return `height'
248;; and vice versa, if you're feeling silly and want to mess with the user's
249;; mind). As a help in deciding what to do, the filtering function has
250;; access to PARAMETERS, but must not change it in any way. It also has
251;; access to FILTERED, which can be modified at will. This allows two or
252;; more filters to coordinate themselves, because in general there's no way
253;; to predict the order in which they will be run.
254;;
255;; So, which parameters are filtered by default, and why? Let's see.
256;;
257;; - `buffer-list', `buried-buffer-list', `buffer-predicate': They contain
258;; references to live objects, or in the case of `buffer-predicate', it
259;; could also contain an fbound symbol (a predicate function) that could
260;; not be defined in a later session.
261;;
262;; - `window-id', `outer-window-id', `parent-id': They are assigned
263;; automatically and cannot be set, so keeping them is harmless, but they
264;; add clutter. `window-system' is similar: it's assigned at frame
265;; creation, and does not serve any useful purpose later.
266;;
267;; - `left', `top': Only problematic when saving an iconified frame, because
268;; when the frame is iconified they are set to (- 32000), which doesn't
269;; really help in restoring the frame. Better to remove them and let the
270;; window manager choose a default position for the frame.
271;;
272;; - `background-color', `foreground-color': In tty frames they can be set
273;; to "unspecified-bg" and "unspecified-fg", which aren't understood on
274;; GUI sessions. They have to be filtered out when switching from tty to
275;; a graphical display.
276;;
277;; - `tty', `tty-type': These are tty-specific. When switching to a GUI
278;; display they do no harm, but they clutter the parameter alist.
279;;
280;; - `minibuffer': It can contain a reference to a live window, which cannot
281;; be serialized. Because of Emacs' idiosyncratic treatment of this
282;; parameter, frames created with (minibuffer . t) have a parameter
283;; (minibuffer . #<window...>), while frames created with
284;; (minibuffer . #<window...>) have (minibuffer . nil), which is madness
285;; but helps to differentiate between minibufferless and "normal" frames.
286;; So, changing (minibuffer . #<window...>) to (minibuffer . t) allows
287;; Emacs to set up the new frame correctly. Nice, uh?
288;;
289;; - `name': If this parameter is directly set, `explicit-name' is
290;; automatically set to t, and then `name' no longer changes dynamically.
291;; So, in general, not saving `name' is the right thing to do, though
292;; surely there are applications that will want to override this filter.
293;;
294;; - `font', `fullscreen', `height' and `width': These parameters suffer
295;; from the fact that they are badly mangled when going through a
296;; tty session, though not all in the same way. When saving a GUI frame
297;; and restoring it in a tty, the height and width of the new frame are
298;; those of the tty screen (let's say 80x25, for example); going back
299;; to a GUI session means getting frames of the tty screen size (so all
300;; your frames are 80 cols x 25 rows). For `fullscreen' there's a
301;; similar problem, because a tty frame cannot really be fullscreen or
302;; maximized, so the state is lost. The problem with `font' is a bit
303;; different, because a valid GUI font spec in `font' turns into
304;; (font . "tty") in a tty frame, and when read back into a GUI session
305;; it fails because `font's value is no longer a valid font spec.
306;;
307;; In most cases, the filtering functions just do the obvious thing: remove
308;; CURRENT when it is meaningless to keep it, or pass a modified copy if
309;; that helps (as in the case of `minibuffer').
310;;
311;; The exception are the parameters in the last set, which should survive
312;; the roundtrip though tty-land. The answer is to add "stashing
313;; parameters", working in pairs, to shelve the GUI-specific contents and
314;; restore it once we're back in pixel country. That's what functions
315;; `frameset-filter-shelve-param' and `frameset-filter-unshelve-param' do.
316;;
317;; Basically, if you set `frameset-filter-shelve-param' as the filter for
318;; a parameter P, it will detect when it is restoring a GUI frame into a
319;; tty session, and save P's value in the custom parameter X:P, but only
320;; if X:P does not exist already (so it is not overwritten if you enter
321;; the tty session more than once). If you're not switching to a tty
322;; frame, the filter just passes CURRENT along.
323;;
324;; The parameter X:P, on the other hand, must have been setup to be
325;; filtered by `frameset-filter-unshelve-param', which unshelves the
326;; value: if we're entering a GUI session, returns P instead of CURRENT,
327;; while in other cases it just passes it along.
328;;
329;; The only additional trick is that `frameset-filter-shelve-param' does
330;; not set P if switching back to GUI and P already has a value, because
331;; it assumes that `frameset-filter-unshelve-param' did set it up. And
332;; `frameset-filter-unshelve-param', when unshelving P, must look into
333;; FILTERED to determine if P has already been set and if so, modify it;
334;; else just returns P.
335;;
336;; Currently, the value of X in X:P is `GUI', but you can use any prefix,
337;; by passing its symbol as argument in the filter:
338;;
339;; (my-parameter frameset-filter-shelve-param MYPREFIX)
340;;
341;; instead of
342;;
343;; (my-parameter . frameset-filter-shelve-param)
344;;
345;; Note that `frameset-filter-unshelve-param' does not need MYPREFIX
346;; because it is available from the parameter name in CURRENT. Also note
347;; that the colon between the prefix and the parameter name is hardcoded.
348;; The reason is that X:P is quite readable, and that the colon is a
349;; very unusual character in symbol names, other than in initial position
350;; in keywords (emacs -Q has only two such symbols, and one of them is a
351;; URL). So the probability of a collision with existing or future
352;; symbols is quite insignificant.
353;;
354;; Now, what about the filter alist variables? There are three of them,
355;; though only two sets of parameters:
356;;
357;; - `frameset-session-filter-alist' contains these filters that allow to
358;; save and restore framesets in-session, without the need to serialize
359;; the frameset or save it to disk (for example, to save a frameset in a
360;; register and restore it later). Filters in this list do not remove
361;; live objects, except in `minibuffer', which is dealt especially by
362;; `frameset-save' / `frameset-restore'.
363;;
364;; - `frameset-persistent-filter-alist' is the whole deal. It does all
365;; the filtering described above, and the result is ready to be saved on
366;; disk without loss of information. That's the format used by the
367;; desktop.el package, for example.
368;;
369;; IMPORTANT: These variables share structure and should NEVER be modified.
370;;
371;; - `frameset-filter-alist': The value of this variable is the default
372;; value for the FILTERS arguments of `frameset-save' and
373;; `frameset-restore'. It is set to `frameset-persistent-filter-alist',
374;; though it can be changed by specific applications.
375;;
376;; How to use them?
377;;
378;; The simplest way is just do nothing. The default should work
379;; reasonably and sensibly enough. But, what if you really need a
380;; customized filter alist? Then you can create your own variable
381;;
382;; (defvar my-filter-alist
383;; '((my-param1 . :never)
384;; (my-param2 . :save)
385;; (my-param3 . :restore)
386;; (my-param4 . my-filtering-function-without-args)
387;; (my-param5 my-filtering-function-with arg1 arg2)
388;; ;;; many other parameters
389;; )
390;; "My customized parameter filter alist.")
391;;
392;; or, if you're only changing a few items,
393;;
394;; (defvar my-filter-alist
395;; (nconc '((my-param1 . :never)
396;; (my-param2 . my-filtering-function))
397;; frameset-filter-alist)
398;; "My brief customized parameter filter alist.")
399;;
400;; and pass it to the FILTER arg of the save/restore functions,
401;; ALWAYS taking care of not modifying the original lists; if you're
402;; going to do any modifying of my-filter-alist, please use
403;;
404;; (nconc '((my-param1 . :never) ...)
405;; (copy-sequence frameset-filter-alist))
406;;
407;; One thing you shouldn't forget is that they are alists, so searching
408;; in them is sequential. If you just want to change the default of
409;; `name' to allow it to be saved, you can set (name . nil) in your
410;; customized filter alist; it will take precedence over the latter
411;; setting. In case you decide that you *always* want to save `name',
412;; you can add it to `frameset-filter-alist':
413;;
414;; (push '(name . nil) frameset-filter-alist)
415;;
416;; In certain applications, having a parameter filtering function like
417;; `frameset-filter-params' can be useful, even if you're not using
418;; framesets. The interface of `frameset-filter-params' is generic
419;; and does not depend of global state, with one exception: it uses
420;; the internal variable `frameset--target-display' to decide if, and
421;; how, to modify the `display' parameter of FILTERED. But that
422;; should not represent any problem, because it's only meaningful
423;; when restoring, and customized uses of `frameset-filter-params'
424;; are likely to use their own filter alist and just call
425;;
426;; (setq my-filtered (frameset-filter-params my-params my-filters t))
427;;
428;; In case you want to use it with the standard filters, you can
429;; wrap the call to `frameset-filter-params' in a let form to bind
430;; `frameset--target-display' to nil or the desired value.
431;;
432
433;;;###autoload
434(defvar frameset-session-filter-alist
435 '((name . :never)
436 (left . frameset-filter-iconified)
437 (minibuffer . frameset-filter-minibuffer)
438 (top . frameset-filter-iconified))
439 "Minimum set of parameters to filter for live (on-session) framesets.
440DO NOT MODIFY. See `frameset-filter-alist' for a full description.")
441
442;;;###autoload
443(defvar frameset-persistent-filter-alist
444 (nconc
445 '((background-color . frameset-filter-sanitize-color)
446 (buffer-list . :never)
447 (buffer-predicate . :never)
448 (buried-buffer-list . :never)
449 (font . frameset-filter-shelve-param)
450 (foreground-color . frameset-filter-sanitize-color)
451 (fullscreen . frameset-filter-shelve-param)
452 (GUI:font . frameset-filter-unshelve-param)
453 (GUI:fullscreen . frameset-filter-unshelve-param)
454 (GUI:height . frameset-filter-unshelve-param)
455 (GUI:width . frameset-filter-unshelve-param)
456 (height . frameset-filter-shelve-param)
457 (outer-window-id . :never)
458 (parent-id . :never)
459 (tty . frameset-filter-tty-to-GUI)
460 (tty-type . frameset-filter-tty-to-GUI)
461 (width . frameset-filter-shelve-param)
462 (window-id . :never)
463 (window-system . :never))
464 frameset-session-filter-alist)
465 "Parameters to filter for persistent framesets.
466DO NOT MODIFY. See `frameset-filter-alist' for a full description.")
467
468;;;###autoload
469(defvar frameset-filter-alist frameset-persistent-filter-alist
118 "Alist of frame parameters and filtering functions. 470 "Alist of frame parameters and filtering functions.
119 471
120Each element is a cons (PARAM . ACTION), where PARAM is a parameter 472This alist is the default value of the FILTERS argument of
121name (a symbol identifying a frame parameter), and ACTION can be: 473`frameset-save' and `frameset-restore' (which see).
474
475Initially, `frameset-filter-alist' is set to, and shares the value of,
476`frameset-persistent-filter-alist'. You can override any item in
477this alist by `push'ing a new item onto it. If, for some reason, you
478intend to modify existing values, do
122 479
123 t The parameter is always removed from the parameter list. 480 (setq frameset-filter-alist (copy-tree frameset-filter-alist))
124 :save The parameter is removed when saving the frame. 481
125 :restore The parameter is removed when restoring the frame. 482before changing anything.
483
484On saving, PARAMETERS is the parameter alist of each frame processed,
485and FILTERED is the parameter alist that gets saved to the frameset.
486
487On restoring, PARAMETERS is the parameter alist extracted from the
488frameset, and FILTERED is the resulting frame parameter alist used
489to restore the frame.
490
491Elements of `frameset-filter-alist' are conses (PARAM . ACTION),
492where PARAM is a parameter name (a symbol identifying a frame
493parameter), and ACTION can be:
494
495 nil The parameter is copied to FILTERED.
496 :never The parameter is never copied to FILTERED.
497 :save The parameter is copied only when saving the frame.
498 :restore The parameter is copied only when restoring the frame.
126 FILTER A filter function. 499 FILTER A filter function.
127 500
128FILTER can be a symbol FILTER-FUN, or a list (FILTER-FUN ARGS...). 501FILTER can be a symbol FILTER-FUN, or a list (FILTER-FUN ARGS...).
129It will be called with four arguments CURRENT, FILTERED, PARAMETERS 502FILTER-FUN is invoked with
130and SAVING, plus any additional ARGS: 503
504 (apply FILTER-FUN CURRENT FILTERED PARAMETERS SAVING ARGS)
505
506where
131 507
132 CURRENT A cons (PARAM . VALUE), where PARAM is the one being 508 CURRENT A cons (PARAM . VALUE), where PARAM is the one being
133 filtered and VALUE is its current value. 509 filtered and VALUE is its current value.
134 FILTERED The alist of parameters filtered so far. 510 FILTERED The resulting alist (so far).
135 PARAMETERS The complete alist of parameters being filtered, 511 PARAMETERS The complete alist of parameters being filtered,
136 SAVING Non-nil if filtering before saving state, nil otherwise. 512 SAVING Non-nil if filtering before saving state, nil if filtering
513 before restoring it.
514 ARGS Any additional arguments specified in the ACTION.
515
516FILTER-FUN is allowed to modify items in FILTERED, but no other arguments.
517It must return:
518 nil Skip CURRENT (do not add it to FILTERED).
519 t Add CURRENT to FILTERED as is.
520 (NEW-PARAM . NEW-VALUE) Add this to FILTERED instead of CURRENT.
137 521
138The FILTER-FUN function must return: 522Frame parameters not on this alist are passed intact, as if they were
139 nil CURRENT is removed from the list. 523defined with ACTION = nil.")
140 t CURRENT is left as is.
141 (PARAM' . VALUE') Replace CURRENT with this.
142 524
143Frame parameters not on this list are passed intact.")
144 525
145(defvar frameset--target-display nil 526(defvar frameset--target-display nil
146 ;; Either (minibuffer . VALUE) or nil. 527 ;; Either (minibuffer . VALUE) or nil.
147 ;; This refers to the current frame config being processed inside 528 ;; This refers to the current frame config being processed inside
148 ;; `frame--restore-frames' and its auxiliary functions (like filtering). 529 ;; `frameset-restore' and its auxiliary functions (like filtering).
149 ;; If nil, there is no need to change the display. 530 ;; If nil, there is no need to change the display.
150 ;; If non-nil, display parameter to use when creating the frame. 531 ;; If non-nil, display parameter to use when creating the frame.
151 "Internal use only.") 532 "Internal use only.")
152 533
153(defun frameset-switch-to-gui-p (parameters) 534(defun frameset-switch-to-gui-p (parameters)
154 "True when switching to a graphic display. 535 "True when switching to a graphic display.
155Return t if PARAMETERS describes a text-only terminal and 536Return non-nil if the parameter alist PARAMETERS describes a frame on a
156the target is a graphic display; otherwise return nil. 537text-only terminal, and the frame is being restored on a graphic display;
157Only meaningful when called from a filtering function in 538otherwise return nil. Only meaningful when called from a filtering
158`frameset-filter-alist'." 539function in `frameset-filter-alist'."
159 (and frameset--target-display ; we're switching 540 (and frameset--target-display ; we're switching
160 (null (cdr (assq 'display parameters))) ; from a tty 541 (null (cdr (assq 'display parameters))) ; from a tty
161 (cdr frameset--target-display))) ; to a GUI display 542 (cdr frameset--target-display))) ; to a GUI display
162 543
163(defun frameset-switch-to-tty-p (parameters) 544(defun frameset-switch-to-tty-p (parameters)
164 "True when switching to a text-only terminal. 545 "True when switching to a text-only terminal.
165Return t if PARAMETERS describes a graphic display and 546Return non-nil if the parameter alist PARAMETERS describes a frame on a
166the target is a text-only terminal; otherwise return nil. 547graphic display, and the frame is being restored on a text-only terminal;
167Only meaningful when called from a filtering function in 548otherwise return nil. Only meaningful when called from a filtering
168`frameset-filter-alist'." 549function in `frameset-filter-alist'."
169 (and frameset--target-display ; we're switching 550 (and frameset--target-display ; we're switching
170 (cdr (assq 'display parameters)) ; from a GUI display 551 (cdr (assq 'display parameters)) ; from a GUI display
171 (null (cdr frameset--target-display)))) ; to a tty 552 (null (cdr frameset--target-display)))) ; to a tty
553
554(defun frameset-filter-tty-to-GUI (_current _filtered parameters saving)
555 "Remove CURRENT when switching from tty to a graphic display.
556
557For the meaning of CURRENT, FILTERED, PARAMETERS and SAVING,
558see `frameset-filter-alist'."
559 (or saving
560 (not (frameset-switch-to-gui-p parameters))))
172 561
173(defun frameset-filter-sanitize-color (current _filtered parameters saving) 562(defun frameset-filter-sanitize-color (current _filtered parameters saving)
174 "When switching to a GUI frame, remove \"unspecified\" colors. 563 "When switching to a GUI frame, remove \"unspecified\" colors.
175Useful as a filter function for tty-specific parameters." 564Useful as a filter function for tty-specific parameters.
565
566For the meaning of CURRENT, FILTERED, PARAMETERS and SAVING,
567see `frameset-filter-alist'."
176 (or saving 568 (or saving
177 (not (frameset-switch-to-gui-p parameters)) 569 (not (frameset-switch-to-gui-p parameters))
178 (not (stringp (cdr current))) 570 (not (stringp (cdr current)))
179 (not (string-match-p "^unspecified-[fb]g$" (cdr current))))) 571 (not (string-match-p "^unspecified-[fb]g$" (cdr current)))))
180 572
181(defun frameset-filter-minibuffer (current _filtered _parameters saving) 573(defun frameset-filter-minibuffer (current filtered _parameters saving)
182 "When saving, convert (minibuffer . #<window>) parameter to (minibuffer . t)." 574 "Force the minibuffer parameter to have a sensible value.
183 (or (not saving) 575
184 (if (windowp (cdr current)) 576When saving, convert (minibuffer . #<window>) to (minibuffer . t).
185 '(minibuffer . t) 577When restoring, if there are two copies, keep the one pointing to
186 t))) 578a live window.
579
580For the meaning of CURRENT, FILTERED, PARAMETERS and SAVING,
581see `frameset-filter-alist'."
582 (let ((value (cdr current)) mini)
583 (cond (saving
584 (if (windowp value) '(minibuffer . t) t))
585 ((setq mini (assq 'minibuffer filtered))
586 (when (windowp value) (setcdr mini value))
587 nil)
588 (t t))))
187 589
188(defun frameset-filter-save-parm (current _filtered parameters saving 590(defun frameset-filter-shelve-param (current _filtered parameters saving
189 &optional prefix) 591 &optional prefix)
190 "When switching to a tty frame, save parameter P as PREFIX:P. 592 "When switching to a tty frame, save parameter P as PREFIX:P.
191The parameter can be later restored with `frameset-filter-restore-parm'. 593The parameter can be later restored with `frameset-filter-unshelve-param'.
192PREFIX defaults to `GUI'." 594PREFIX defaults to `GUI'.
595
596For the meaning of CURRENT, FILTERED, PARAMETERS and SAVING,
597see `frameset-filter-alist'."
193 (unless prefix (setq prefix 'GUI)) 598 (unless prefix (setq prefix 'GUI))
194 (cond (saving t) 599 (cond (saving t)
195 ((frameset-switch-to-tty-p parameters) 600 ((frameset-switch-to-tty-p parameters)
@@ -201,9 +606,12 @@ PREFIX defaults to `GUI'."
201 (not (assq (intern (format "%s:%s" prefix (car current))) parameters))) 606 (not (assq (intern (format "%s:%s" prefix (car current))) parameters)))
202 (t t))) 607 (t t)))
203 608
204(defun frameset-filter-restore-parm (current filtered parameters saving) 609(defun frameset-filter-unshelve-param (current filtered parameters saving)
205 "When switching to a GUI frame, restore PREFIX:P parameter as P. 610 "When switching to a GUI frame, restore PREFIX:P parameter as P.
206CURRENT must be of the form (PREFIX:P . value)." 611CURRENT must be of the form (PREFIX:P . value).
612
613For the meaning of CURRENT, FILTERED, PARAMETERS and SAVING,
614see `frameset-filter-alist'."
207 (or saving 615 (or saving
208 (not (frameset-switch-to-gui-p parameters)) 616 (not (frameset-switch-to-gui-p parameters))
209 (let* ((prefix:p (symbol-name (car current))) 617 (let* ((prefix:p (symbol-name (car current)))
@@ -218,38 +626,40 @@ CURRENT must be of the form (PREFIX:P . value)."
218 626
219(defun frameset-filter-iconified (_current _filtered parameters saving) 627(defun frameset-filter-iconified (_current _filtered parameters saving)
220 "Remove CURRENT when saving an iconified frame. 628 "Remove CURRENT when saving an iconified frame.
221This is used for positions parameters `left' and `top', which are 629This is used for positional parameters `left' and `top', which are
222meaningless in an iconified frame, so the frame is restored in a 630meaningless in an iconified frame, so the frame is restored in a
223default position." 631default position.
224 (not (and saving (eq (cdr (assq 'visibility parameters)) 'icon))))
225 632
226(defun frameset-keep-original-display-p (force-display) 633For the meaning of CURRENT, FILTERED, PARAMETERS and SAVING,
227 "True if saved frames' displays should be honored." 634see `frameset-filter-alist'."
228 (cond ((daemonp) t) 635 (not (and saving (eq (cdr (assq 'visibility parameters)) 'icon))))
229 ((eq system-type 'windows-nt) nil)
230 (t (null force-display))))
231 636
232(defun frameset-filter-params (parameters filter-alist saving) 637(defun frameset-filter-params (parameters filter-alist saving)
233 "Filter parameter list PARAMETERS and return a filtered list. 638 "Filter parameter alist PARAMETERS and return a filtered alist.
234FILTER-ALIST is an alist of parameter filters, in the format of 639FILTER-ALIST is an alist of parameter filters, in the format of
235`frameset-filter-alist' (which see). 640`frameset-filter-alist' (which see).
236SAVING is non-nil while filtering parameters to save a frameset, 641SAVING is non-nil while filtering parameters to save a frameset,
237nil while the filtering is done to restore it." 642nil while the filtering is done to restore it."
238 (let ((filtered nil)) 643 (let ((filtered nil))
239 (dolist (current parameters) 644 (dolist (current parameters)
645 ;; When saving, the parameter alist is temporary, so modifying it
646 ;; is not a problem. When restoring, the parameter alist is part
647 ;; of a frameset, so we must copy parameters to avoid inadvertent
648 ;; modifications.
240 (pcase (cdr (assq (car current) filter-alist)) 649 (pcase (cdr (assq (car current) filter-alist))
241 (`nil 650 (`nil
242 (push current filtered)) 651 (push (if saving current (copy-tree current)) filtered))
243 (`t 652 (:never
244 nil) 653 nil)
245 (:save
246 (unless saving (push current filtered)))
247 (:restore 654 (:restore
655 (unless saving (push (copy-tree current) filtered)))
656 (:save
248 (when saving (push current filtered))) 657 (when saving (push current filtered)))
249 ((or `(,fun . ,args) (and fun (pred fboundp))) 658 ((or `(,fun . ,args) (and fun (pred fboundp)))
250 (let ((this (apply fun current filtered parameters saving args))) 659 (let* ((this (apply fun current filtered parameters saving args))
251 (when this 660 (val (if (eq this t) current this)))
252 (push (if (eq this t) current this) filtered)))) 661 (when val
662 (push (if saving val (copy-tree val)) filtered))))
253 (other 663 (other
254 (delay-warning 'frameset (format "Unknown filter %S" other) :error)))) 664 (delay-warning 'frameset (format "Unknown filter %S" other) :error))))
255 ;; Set the display parameter after filtering, so that filter functions 665 ;; Set the display parameter after filtering, so that filter functions
@@ -262,21 +672,48 @@ nil while the filtering is done to restore it."
262 filtered)) 672 filtered))
263 673
264 674
265;; Saving framesets 675;; Frame ids
266 676
267(defun frameset--set-id (frame) 677(defun frameset--set-id (frame)
268 "Set FRAME's `frameset-id' if not yet set. 678 "Set FRAME's id if not yet set.
269Internal use only." 679Internal use only."
270 (unless (frame-parameter frame 'frameset-id) 680 (unless (frame-parameter frame 'frameset--id)
271 (set-frame-parameter frame 681 (set-frame-parameter frame
272 'frameset-id 682 'frameset--id
273 (mapconcat (lambda (n) (format "%04X" n)) 683 (mapconcat (lambda (n) (format "%04X" n))
274 (cl-loop repeat 4 collect (random 65536)) 684 (cl-loop repeat 4 collect (random 65536))
275 "-")))) 685 "-"))))
686;;;###autoload
687(defun frameset-frame-id (frame)
688 "Return the frame id of FRAME, if it has one; else, return nil.
689A frame id is a string that uniquely identifies a frame.
690It is persistent across `frameset-save' / `frameset-restore'
691invocations, and once assigned is never changed unless the same
692frame is duplicated (via `frameset-restore'), in which case the
693newest frame keeps the id and the old frame's is set to nil."
694 (frame-parameter frame 'frameset--id))
695
696;;;###autoload
697(defun frameset-frame-id-equal-p (frame id)
698 "Return non-nil if FRAME's id matches ID."
699 (string= (frameset-frame-id frame) id))
700
701;;;###autoload
702(defun frameset-frame-with-id (id &optional frame-list)
703 "Return the live frame with id ID, if exists; else nil.
704If FRAME-LIST is a list of frames, check these frames only.
705If nil, check all live frames."
706 (cl-find-if (lambda (f)
707 (and (frame-live-p f)
708 (frameset-frame-id-equal-p f id)))
709 (or frame-list (frame-list))))
710
711
712;; Saving framesets
276 713
277(defun frameset--process-minibuffer-frames (frame-list) 714(defun frameset--record-minibuffer-relationships (frame-list)
278 "Process FRAME-LIST and record minibuffer relationships. 715 "Process FRAME-LIST and record minibuffer relationships.
279FRAME-LIST is a list of frames." 716FRAME-LIST is a list of frames. Internal use only."
280 ;; Record frames with their own minibuffer 717 ;; Record frames with their own minibuffer
281 (dolist (frame (minibuffer-frame-list)) 718 (dolist (frame (minibuffer-frame-list))
282 (when (memq frame frame-list) 719 (when (memq frame frame-list)
@@ -291,73 +728,106 @@ FRAME-LIST is a list of frames."
291 (dolist (frame frame-list) 728 (dolist (frame frame-list)
292 (unless (frame-parameter frame 'frameset--mini) 729 (unless (frame-parameter frame 'frameset--mini)
293 (frameset--set-id frame) 730 (frameset--set-id frame)
294 (let* ((mb-frame (window-frame (minibuffer-window frame))) 731 (let ((mb-frame (window-frame (minibuffer-window frame))))
295 (id (and mb-frame (frame-parameter mb-frame 'frameset-id)))) 732 ;; For minibufferless frames, frameset--mini is a cons
296 (if (null id) 733 ;; (nil . FRAME-ID), where FRAME-ID is the frameset--id of
297 (error "Minibuffer frame %S for %S is excluded" mb-frame frame) 734 ;; the frame containing its minibuffer window.
298 ;; For minibufferless frames, frameset--mini is a cons 735 ;; FRAME-ID can be set to nil, if FRAME-LIST doesn't contain
299 ;; (nil . FRAME-ID), where FRAME-ID is the frameset-id of 736 ;; the minibuffer frame of a minibufferless frame; we allow
300 ;; the frame containing its minibuffer window. 737 ;; it without trying to second-guess the user.
301 (set-frame-parameter frame 738 (set-frame-parameter frame
302 'frameset--mini 739 'frameset--mini
303 (cons nil id))))))) 740 (cons nil
741 (and mb-frame
742 (frameset-frame-id mb-frame))))))))
304 743
305;;;###autoload 744;;;###autoload
306(cl-defun frameset-save (frame-list &key filters predicate properties) 745(cl-defun frameset-save (frame-list
307 "Return the frameset of FRAME-LIST, a list of frames. 746 &key app name description
308If nil, FRAME-LIST defaults to all live frames. 747 filters predicate properties)
309FILTERS is an alist of parameter filters; defaults to `frameset-filter-alist'. 748 "Return a frameset for FRAME-LIST, a list of frames.
749Dead frames and non-frame objects are silently removed from the list.
750If nil, FRAME-LIST defaults to the output of `frame-list' (all live frames).
751APP, NAME and DESCRIPTION are optional data; see the docstring of the
752`frameset' defstruct for details.
753FILTERS is an alist of parameter filters; if nil, the value of the variable
754`frameset-filter-alist' is used instead.
310PREDICATE is a predicate function, which must return non-nil for frames that 755PREDICATE is a predicate function, which must return non-nil for frames that
311should be saved; it defaults to saving all frames from FRAME-LIST. 756should be saved; if PREDICATE is nil, all frames from FRAME-LIST are saved.
312PROPERTIES is a user-defined property list to add to the frameset." 757PROPERTIES is a user-defined property list to add to the frameset."
313 (let ((frames (cl-delete-if-not #'frame-live-p 758 (let* ((list (or (copy-sequence frame-list) (frame-list)))
314 (cl-delete-if-not (or predicate #'framep) 759 (frames (cl-delete-if-not #'frame-live-p
315 (or (copy-sequence frame-list) 760 (if predicate
316 (frame-list)))))) 761 (cl-delete-if-not predicate list)
317 (frameset--process-minibuffer-frames frames) 762 list)))
318 (make-frameset :properties (append '(:version 1) properties) 763 fs)
319 :states (mapcar 764 (frameset--record-minibuffer-relationships frames)
320 (lambda (frame) 765 (setq fs (frameset--make
321 (cons 766 :app app
322 (frameset-filter-params (frame-parameters frame) 767 :name name
323 (or filters 768 :description description
324 frameset-filter-alist) 769 :properties properties
325 t) 770 :states (mapcar
326 (window-state-get (frame-root-window frame) t))) 771 (lambda (frame)
327 frames)))) 772 (cons
773 (frameset-filter-params (frame-parameters frame)
774 (or filters
775 frameset-filter-alist)
776 t)
777 (window-state-get (frame-root-window frame) t)))
778 frames)))
779 (cl-assert (frameset-valid-p fs))
780 fs))
328 781
329 782
330;; Restoring framesets 783;; Restoring framesets
331 784
332(defvar frameset--reuse-list nil 785(defvar frameset--reuse-list nil
333 "Internal use only.") 786 "The list of frames potentially reusable.
334 787Its value is only meaningful during execution of `frameset-restore'.
335(defun frameset--compute-pos (value left/top right/bottom) 788Internal use only.")
789
790(defun frameset-compute-pos (value left/top right/bottom)
791 "Return an absolute positioning value for a frame.
792VALUE is the value of a positional frame parameter (`left' or `top').
793If VALUE is relative to the screen edges (like (+ -35) or (-200), it is
794converted to absolute by adding it to the corresponding edge; if it is
795an absolute position, it is returned unmodified.
796LEFT/TOP and RIGHT/BOTTOM indicate the dimensions of the screen in
797pixels along the relevant direction: either the position of the left
798and right edges for a `left' positional parameter, or the position of
799the top and bottom edges for a `top' parameter."
336 (pcase value 800 (pcase value
337 (`(+ ,val) (+ left/top val)) 801 (`(+ ,val) (+ left/top val))
338 (`(- ,val) (+ right/bottom val)) 802 (`(- ,val) (+ right/bottom val))
339 (val val))) 803 (val val)))
340 804
341(defun frameset--move-onscreen (frame force-onscreen) 805(defun frameset-move-onscreen (frame force-onscreen)
342 "If FRAME is offscreen, move it back onscreen and, if necessary, resize it. 806 "If FRAME is offscreen, move it back onscreen and, if necessary, resize it.
343For the description of FORCE-ONSCREEN, see `frameset-restore'. 807For the description of FORCE-ONSCREEN, see `frameset-restore'.
344When forced onscreen, frames wider than the monitor's workarea are converted 808When forced onscreen, frames wider than the monitor's workarea are converted
345to fullwidth, and frames taller than the workarea are converted to fullheight. 809to fullwidth, and frames taller than the workarea are converted to fullheight.
346NOTE: This only works for non-iconified frames. Internal use only." 810NOTE: This only works for non-iconified frames."
347 (pcase-let* ((`(,left ,top ,width ,height) (cl-cdadr (frame-monitor-attributes frame))) 811 (pcase-let* ((`(,left ,top ,width ,height) (cl-cdadr (frame-monitor-attributes frame)))
348 (right (+ left width -1)) 812 (right (+ left width -1))
349 (bottom (+ top height -1)) 813 (bottom (+ top height -1))
350 (fr-left (frameset--compute-pos (frame-parameter frame 'left) left right)) 814 (fr-left (frameset-compute-pos (frame-parameter frame 'left) left right))
351 (fr-top (frameset--compute-pos (frame-parameter frame 'top) top bottom)) 815 (fr-top (frameset-compute-pos (frame-parameter frame 'top) top bottom))
352 (ch-width (frame-char-width frame)) 816 (ch-width (frame-char-width frame))
353 (ch-height (frame-char-height frame)) 817 (ch-height (frame-char-height frame))
354 (fr-width (max (frame-pixel-width frame) (* ch-width (frame-width frame)))) 818 (fr-width (max (frame-pixel-width frame) (* ch-width (frame-width frame))))
355 (fr-height (max (frame-pixel-height frame) (* ch-height (frame-height frame)))) 819 (fr-height (max (frame-pixel-height frame) (* ch-height (frame-height frame))))
356 (fr-right (+ fr-left fr-width -1)) 820 (fr-right (+ fr-left fr-width -1))
357 (fr-bottom (+ fr-top fr-height -1))) 821 (fr-bottom (+ fr-top fr-height -1)))
358 (when (pcase force-onscreen 822 (when (pcase force-onscreen
823 ;; A predicate.
824 ((pred functionp)
825 (funcall force-onscreen
826 frame
827 (list fr-left fr-top fr-width fr-height)
828 (list left top width height)))
359 ;; Any corner is outside the screen. 829 ;; Any corner is outside the screen.
360 (`all (or (< fr-bottom top) (> fr-bottom bottom) 830 (:all (or (< fr-bottom top) (> fr-bottom bottom)
361 (< fr-left left) (> fr-left right) 831 (< fr-left left) (> fr-left right)
362 (< fr-right left) (> fr-right right) 832 (< fr-right left) (> fr-right right)
363 (< fr-top top) (> fr-top bottom))) 833 (< fr-top top) (> fr-top bottom)))
@@ -396,7 +866,7 @@ NOTE: This only works for non-iconified frames. Internal use only."
396 (when params 866 (when params
397 (modify-frame-parameters frame params)))))) 867 (modify-frame-parameters frame params))))))
398 868
399(defun frameset--find-frame (predicate display &rest args) 869(defun frameset--find-frame-if (predicate display &rest args)
400 "Find a frame in `frameset--reuse-list' satisfying PREDICATE. 870 "Find a frame in `frameset--reuse-list' satisfying PREDICATE.
401Look through available frames whose display property matches DISPLAY 871Look through available frames whose display property matches DISPLAY
402and return the first one for which (PREDICATE frame ARGS) returns t. 872and return the first one for which (PREDICATE frame ARGS) returns t.
@@ -407,10 +877,10 @@ If PREDICATE is nil, it is always satisfied. Internal use only."
407 (apply predicate frame args)))) 877 (apply predicate frame args))))
408 frameset--reuse-list)) 878 frameset--reuse-list))
409 879
410(defun frameset--reuse-frame (display frame-cfg) 880(defun frameset--reuse-frame (display parameters)
411 "Look for an existing frame to reuse. 881 "Return an existing frame to reuse, or nil if none found.
412DISPLAY is the display where the frame will be shown, and FRAME-CFG 882DISPLAY is the display where the frame will be shown, and PARAMETERS
413is the parameter list of the frame being restored. Internal use only." 883is the parameter alist of the frame being restored. Internal use only."
414 (let ((frame nil) 884 (let ((frame nil)
415 mini) 885 mini)
416 ;; There are no fancy heuristics there. We could implement some 886 ;; There are no fancy heuristics there. We could implement some
@@ -423,19 +893,19 @@ is the parameter list of the frame being restored. Internal use only."
423 ;; will usually have only one frame, and should already work. 893 ;; will usually have only one frame, and should already work.
424 (cond ((null display) 894 (cond ((null display)
425 ;; When the target is tty, every existing frame is reusable. 895 ;; When the target is tty, every existing frame is reusable.
426 (setq frame (frameset--find-frame nil display))) 896 (setq frame (frameset--find-frame-if nil display)))
427 ((car (setq mini (cdr (assq 'frameset--mini frame-cfg)))) 897 ((car (setq mini (cdr (assq 'frameset--mini parameters))))
428 ;; If the frame has its own minibuffer, let's see whether 898 ;; If the frame has its own minibuffer, let's see whether
429 ;; that frame has already been loaded (which can happen after 899 ;; that frame has already been loaded (which can happen after
430 ;; M-x desktop-read). 900 ;; M-x desktop-read).
431 (setq frame (frameset--find-frame 901 (setq frame (frameset--find-frame-if
432 (lambda (f id) 902 (lambda (f id)
433 (string= (frame-parameter f 'frameset-id) id)) 903 (frameset-frame-id-equal-p f id))
434 display (cdr mini))) 904 display (cdr (assq 'frameset--id parameters))))
435 ;; If it has not been loaded, and it is not a minibuffer-only frame, 905 ;; If it has not been loaded, and it is not a minibuffer-only frame,
436 ;; let's look for an existing non-minibuffer-only frame to reuse. 906 ;; let's look for an existing non-minibuffer-only frame to reuse.
437 (unless (or frame (eq (cdr (assq 'minibuffer frame-cfg)) 'only)) 907 (unless (or frame (eq (cdr (assq 'minibuffer parameters)) 'only))
438 (setq frame (frameset--find-frame 908 (setq frame (frameset--find-frame-if
439 (lambda (f) 909 (lambda (f)
440 (let ((w (frame-parameter f 'minibuffer))) 910 (let ((w (frame-parameter f 'minibuffer)))
441 (and (window-live-p w) 911 (and (window-live-p w)
@@ -445,29 +915,40 @@ is the parameter list of the frame being restored. Internal use only."
445 (mini 915 (mini
446 ;; For minibufferless frames, check whether they already exist, 916 ;; For minibufferless frames, check whether they already exist,
447 ;; and that they are linked to the right minibuffer frame. 917 ;; and that they are linked to the right minibuffer frame.
448 (setq frame (frameset--find-frame 918 (setq frame (frameset--find-frame-if
449 (lambda (f id mini-id) 919 (lambda (f id mini-id)
450 (and (string= (frame-parameter f 'frameset-id) id) 920 (and (frameset-frame-id-equal-p f id)
451 (string= (frame-parameter (window-frame (minibuffer-window f)) 921 (or (null mini-id) ; minibuffer frame not saved
452 'frameset-id) 922 (frameset-frame-id-equal-p
453 mini-id))) 923 (window-frame (minibuffer-window f))
454 display (cdr (assq 'frameset-id frame-cfg)) (cdr mini)))) 924 mini-id))))
925 display
926 (cdr (assq 'frameset--id parameters)) (cdr mini))))
455 (t 927 (t
456 ;; Default to just finding a frame in the same display. 928 ;; Default to just finding a frame in the same display.
457 (setq frame (frameset--find-frame nil display)))) 929 (setq frame (frameset--find-frame-if nil display))))
458 ;; If found, remove from the list. 930 ;; If found, remove from the list.
459 (when frame 931 (when frame
460 (setq frameset--reuse-list (delq frame frameset--reuse-list))) 932 (setq frameset--reuse-list (delq frame frameset--reuse-list)))
461 frame)) 933 frame))
462 934
463(defun frameset--get-frame (frame-cfg window-cfg filters force-onscreen) 935(defun frameset--initial-params (parameters)
936 "Return a list of PARAMETERS that must be set when creating the frame.
937Setting position and size parameters as soon as possible helps reducing
938flickering; other parameters, like `minibuffer' and `border-width', can
939not be changed once the frame has been created. Internal use only."
940 (cl-loop for param in '(left top with height border-width minibuffer)
941 collect (assq param parameters)))
942
943(defun frameset--restore-frame (parameters window-state filters force-onscreen)
464 "Set up and return a frame according to its saved state. 944 "Set up and return a frame according to its saved state.
465That means either reusing an existing frame or creating one anew. 945That means either reusing an existing frame or creating one anew.
466FRAME-CFG is the frame's parameter list; WINDOW-CFG is its window state. 946PARAMETERS is the frame's parameter alist; WINDOW-STATE is its window state.
467For the meaning of FORCE-ONSCREEN, see `frameset-restore'." 947For the meaning of FILTERS and FORCE-ONSCREEN, see `frameset-restore'.
468 (let* ((fullscreen (cdr (assq 'fullscreen frame-cfg))) 948Internal use only."
469 (lines (assq 'tool-bar-lines frame-cfg)) 949 (let* ((fullscreen (cdr (assq 'fullscreen parameters)))
470 (filtered-cfg (frameset-filter-params frame-cfg filters nil)) 950 (lines (assq 'tool-bar-lines parameters))
951 (filtered-cfg (frameset-filter-params parameters filters nil))
471 (display (cdr (assq 'display filtered-cfg))) ;; post-filtering 952 (display (cdr (assq 'display filtered-cfg))) ;; post-filtering
472 alt-cfg frame) 953 alt-cfg frame)
473 954
@@ -502,14 +983,12 @@ For the meaning of FORCE-ONSCREEN, see `frameset-restore'."
502 ;; If a frame needs to be created and it falls partially or fully offscreen, 983 ;; If a frame needs to be created and it falls partially or fully offscreen,
503 ;; sometimes it gets "pushed back" onscreen; however, moving it afterwards is 984 ;; sometimes it gets "pushed back" onscreen; however, moving it afterwards is
504 ;; allowed. So we create the frame as invisible and then reapply the full 985 ;; allowed. So we create the frame as invisible and then reapply the full
505 ;; parameter list (including position and size parameters). 986 ;; parameter alist (including position and size parameters).
506 (setq frame (or (and frameset--reuse-list 987 (setq frame (or (and frameset--reuse-list
507 (frameset--reuse-frame display filtered-cfg)) 988 (frameset--reuse-frame display filtered-cfg))
508 (make-frame-on-display display 989 (make-frame-on-display display
509 (cons '(visibility) 990 (cons '(visibility)
510 (cl-loop 991 (frameset--initial-params filtered-cfg)))))
511 for param in '(left top width height minibuffer)
512 collect (assq param filtered-cfg))))))
513 (modify-frame-parameters frame 992 (modify-frame-parameters frame
514 (if (eq (frame-parameter frame 'fullscreen) fullscreen) 993 (if (eq (frame-parameter frame 'fullscreen) fullscreen)
515 ;; Workaround for bug#14949 994 ;; Workaround for bug#14949
@@ -521,76 +1000,102 @@ For the meaning of FORCE-ONSCREEN, see `frameset-restore'."
521 ;; FIXME: iconified frames should be checked too, 1000 ;; FIXME: iconified frames should be checked too,
522 ;; but it is impossible without deiconifying them. 1001 ;; but it is impossible without deiconifying them.
523 (not (eq (frame-parameter frame 'visibility) 'icon))) 1002 (not (eq (frame-parameter frame 'visibility) 'icon)))
524 (frameset--move-onscreen frame force-onscreen)) 1003 (frameset-move-onscreen frame force-onscreen))
525 1004
526 ;; Let's give the finishing touches (visibility, tool-bar, maximization). 1005 ;; Let's give the finishing touches (visibility, tool-bar, maximization).
527 (when lines (push lines alt-cfg)) 1006 (when lines (push lines alt-cfg))
528 (when alt-cfg (modify-frame-parameters frame alt-cfg)) 1007 (when alt-cfg (modify-frame-parameters frame alt-cfg))
529 ;; Now restore window state. 1008 ;; Now restore window state.
530 (window-state-put window-cfg (frame-root-window frame) 'safe) 1009 (window-state-put window-state (frame-root-window frame) 'safe)
531 frame)) 1010 frame))
532 1011
533(defun frameset--sort-states (state1 state2) 1012(defun frameset--minibufferless-last-p (state1 state2)
534 "Predicate to sort frame states in a suitable order to be created. 1013 "Predicate to sort frame states in an order suitable for creating frames.
535It sorts minibuffer-owning frames before minibufferless ones." 1014It sorts minibuffer-owning frames before minibufferless ones.
1015Internal use only."
536 (pcase-let ((`(,hasmini1 ,id-def1) (assq 'frameset--mini (car state1))) 1016 (pcase-let ((`(,hasmini1 ,id-def1) (assq 'frameset--mini (car state1)))
537 (`(,hasmini2 ,id-def2) (assq 'frameset--mini (car state2)))) 1017 (`(,hasmini2 ,id-def2) (assq 'frameset--mini (car state2))))
538 (cond ((eq id-def1 t) t) 1018 (cond ((eq id-def1 t) t)
539 ((eq id-def2 t) nil) 1019 ((eq id-def2 t) nil)
540 ((not (eq hasmini1 hasmini2)) (eq hasmini1 t)) 1020 ((not (eq hasmini1 hasmini2)) (eq hasmini1 t))
541 ((eq hasmini1 nil) (string< id-def1 id-def2)) 1021 ((eq hasmini1 nil) (or id-def1 id-def2))
542 (t t)))) 1022 (t t))))
543 1023
544(defun frameset-sort-frames-for-deletion (frame1 _frame2) 1024(defun frameset-keep-original-display-p (force-display)
545 "Predicate to sort live frames for deletion. 1025 "True if saved frames' displays should be honored.
546Minibufferless frames must go first to avoid errors when attempting 1026For the meaning of FORCE-DISPLAY, see `frameset-restore'."
547to delete a frame whose minibuffer window is used by another frame." 1027 (cond ((daemonp) t)
1028 ((eq system-type 'windows-nt) nil) ;; Does ns support more than one display?
1029 (t (not force-display))))
1030
1031(defun frameset-minibufferless-first-p (frame1 _frame2)
1032 "Predicate to sort minibufferless frames before other frames."
548 (not (frame-parameter frame1 'minibuffer))) 1033 (not (frame-parameter frame1 'minibuffer)))
549 1034
550;;;###autoload 1035;;;###autoload
551(cl-defun frameset-restore (frameset &key filters reuse-frames force-display force-onscreen) 1036(cl-defun frameset-restore (frameset
1037 &key predicate filters reuse-frames
1038 force-display force-onscreen)
552 "Restore a FRAMESET into the current display(s). 1039 "Restore a FRAMESET into the current display(s).
553 1040
554FILTERS is an alist of parameter filters; defaults to `frameset-filter-alist'. 1041PREDICATE is a function called with two arguments, the parameter alist
1042and the window-state of the frame being restored, in that order (see
1043the docstring of the `frameset' defstruct for additional details).
1044If PREDICATE returns nil, the frame described by that parameter alist
1045and window-state is not restored.
555 1046
556REUSE-FRAMES describes how to reuse existing frames while restoring a frameset: 1047FILTERS is an alist of parameter filters; if nil, the value of
557 t Reuse any existing frame if possible; delete leftover frames. 1048`frameset-filter-alist' is used instead.
558 nil Restore frameset in new frames and delete existing frames. 1049
559 keep Restore frameset in new frames and keep the existing ones. 1050REUSE-FRAMES selects the policy to use to reuse frames when restoring:
560 LIST A list of frames to reuse; only these will be reused, if possible, 1051 t Reuse existing frames if possible, and delete those not reused.
561 and any leftover one will be deleted; other frames not on this 1052 nil Restore frameset in new frames and delete existing frames.
562 list will be kept. 1053 :keep Restore frameset in new frames and keep the existing ones.
1054 LIST A list of frames to reuse; only these are reused (if possible).
1055 Remaining frames in this list are deleted; other frames not
1056 included on the list are left untouched.
563 1057
564FORCE-DISPLAY can be: 1058FORCE-DISPLAY can be:
565 t Frames will be restored in the current display. 1059 t Frames are restored in the current display.
566 nil Frames will be restored, if possible, in their original displays. 1060 nil Frames are restored, if possible, in their original displays.
567 delete Frames in other displays will be deleted instead of restored. 1061 :delete Frames in other displays are deleted instead of restored.
1062 PRED A function called with two arguments, the parameter alist and
1063 the window state (in that order). It must return t, nil or
1064 `:delete', as above but affecting only the frame that will
1065 be created from that parameter alist.
568 1066
569FORCE-ONSCREEN can be: 1067FORCE-ONSCREEN can be:
570 all Force onscreen any frame fully or partially offscreen. 1068 t Force onscreen only those frames that are fully offscreen.
571 t Force onscreen only those frames that are fully offscreen. 1069 nil Do not force any frame back onscreen.
572 nil Do not force any frame back onscreen. 1070 :all Force onscreen any frame fully or partially offscreen.
1071 PRED A function called with three arguments,
1072 - the live frame just restored,
1073 - a list (LEFT TOP WIDTH HEIGHT), describing the frame,
1074 - a list (LEFT TOP WIDTH HEIGHT), describing the workarea.
1075 It must return non-nil to force the frame onscreen, nil otherwise.
1076
1077Note the timing and scope of the operations described above: REUSE-FRAMES
1078affects existing frames; PREDICATE, FILTERS and FORCE-DISPLAY affect the frame
1079being restored before that happens; and FORCE-ONSCREEN affects the frame once
1080it has been restored.
573 1081
574All keywords default to nil." 1082All keyword parameters default to nil."
575 1083
576 (cl-assert (frameset-p frameset)) 1084 (cl-assert (frameset-valid-p frameset))
577 1085
578 (let* ((delete-saved (eq force-display 'delete)) 1086 (let (other-frames)
579 (forcing (not (frameset-keep-original-display-p force-display)))
580 (target (and forcing (cons 'display (frame-parameter nil 'display))))
581 other-frames)
582 1087
583 ;; frameset--reuse-list is a list of frames potentially reusable. Later we 1088 ;; frameset--reuse-list is a list of frames potentially reusable. Later we
584 ;; will decide which ones can be reused, and how to deal with any leftover. 1089 ;; will decide which ones can be reused, and how to deal with any leftover.
585 (pcase reuse-frames 1090 (pcase reuse-frames
586 ((or `nil `keep) 1091 ((or `nil `:keep)
587 (setq frameset--reuse-list nil 1092 (setq frameset--reuse-list nil
588 other-frames (frame-list))) 1093 other-frames (frame-list)))
589 ((pred consp) 1094 ((pred consp)
590 (setq frameset--reuse-list (copy-sequence reuse-frames) 1095 (setq frameset--reuse-list (copy-sequence reuse-frames)
591 other-frames (cl-delete-if (lambda (frame) 1096 other-frames (cl-delete-if (lambda (frame)
592 (memq frame frameset--reuse-list)) 1097 (memq frame frameset--reuse-list))
593 (frame-list)))) 1098 (frame-list))))
594 (_ 1099 (_
595 (setq frameset--reuse-list (frame-list) 1100 (setq frameset--reuse-list (frame-list)
596 other-frames nil))) 1101 other-frames nil)))
@@ -598,96 +1103,140 @@ All keywords default to nil."
598 ;; Sort saved states to guarantee that minibufferless frames will be created 1103 ;; Sort saved states to guarantee that minibufferless frames will be created
599 ;; after the frames that contain their minibuffer windows. 1104 ;; after the frames that contain their minibuffer windows.
600 (dolist (state (sort (copy-sequence (frameset-states frameset)) 1105 (dolist (state (sort (copy-sequence (frameset-states frameset))
601 #'frameset--sort-states)) 1106 #'frameset--minibufferless-last-p))
602 (condition-case-unless-debug err 1107 (pcase-let ((`(,frame-cfg . ,window-cfg) state))
603 (pcase-let* ((`(,frame-cfg . ,window-cfg) state) 1108 (when (or (null predicate) (funcall predicate frame-cfg window-cfg))
604 ((and d-mini `(,hasmini . ,mb-id)) 1109 (condition-case-unless-debug err
605 (cdr (assq 'frameset--mini frame-cfg))) 1110 (let* ((d-mini (cdr (assq 'frameset--mini frame-cfg)))
606 (default (and (booleanp mb-id) mb-id)) 1111 (mb-id (cdr d-mini))
607 (frame nil) (to-tty nil)) 1112 (default (and (car d-mini) mb-id))
608 ;; Only set target if forcing displays and the target display is different. 1113 (force-display (if (functionp force-display)
609 (if (or (not forcing) 1114 (funcall force-display frame-cfg window-cfg)
610 (equal target (or (assq 'display frame-cfg) '(display . nil)))) 1115 force-display))
611 (setq frameset--target-display nil) 1116 frame to-tty)
612 (setq frameset--target-display target 1117 ;; Only set target if forcing displays and the target display is different.
613 to-tty (null (cdr target)))) 1118 (cond ((frameset-keep-original-display-p force-display)
614 ;; If keeping non-reusable frames, and the frame-id of one of them 1119 (setq frameset--target-display nil))
615 ;; matches the frame-id of a frame being restored (because, for example, 1120 ((eq (frame-parameter nil 'display) (cdr (assq 'display frame-cfg)))
616 ;; the frameset has already been read in the same session), remove the 1121 (setq frameset--target-display nil))
617 ;; frame-id from the non-reusable frame, which is not useful anymore. 1122 (t
618 (when (and other-frames 1123 (setq frameset--target-display (cons 'display
619 (or (eq reuse-frames 'keep) (consp reuse-frames))) 1124 (frame-parameter nil 'display))
620 (let ((dup (cl-find (cdr (assq 'frameset-frame-id frame-cfg)) 1125 to-tty (null (cdr frameset--target-display)))))
621 other-frames 1126 ;; Time to restore frames and set up their minibuffers as they were.
622 :key (lambda (frame) 1127 ;; We only skip a frame (thus deleting it) if either:
623 (frame-parameter frame 'frameset-frame-id)) 1128 ;; - we're switching displays, and the user chose the option to delete, or
624 :test #'string=))) 1129 ;; - we're switching to tty, and the frame to restore is minibuffer-only.
625 (when dup 1130 (unless (and frameset--target-display
626 (set-frame-parameter dup 'frameset-frame-id nil)))) 1131 (or (eq force-display :delete)
627 ;; Time to restore frames and set up their minibuffers as they were. 1132 (and to-tty
628 ;; We only skip a frame (thus deleting it) if either: 1133 (eq (cdr (assq 'minibuffer frame-cfg)) 'only))))
629 ;; - we're switching displays, and the user chose the option to delete, or 1134 ;; If keeping non-reusable frames, and the frameset--id of one of them
630 ;; - we're switching to tty, and the frame to restore is minibuffer-only. 1135 ;; matches the id of a frame being restored (because, for example, the
631 (unless (and frameset--target-display 1136 ;; frameset has already been read in the same session), remove the
632 (or delete-saved 1137 ;; frameset--id from the non-reusable frame, which is not useful anymore.
633 (and to-tty 1138 (when (and other-frames
634 (eq (cdr (assq 'minibuffer frame-cfg)) 'only)))) 1139 (or (eq reuse-frames :keep) (consp reuse-frames)))
635 1140 (let ((dup (frameset-frame-with-id (cdr (assq 'frameset--id frame-cfg))
636 ;; Restore minibuffers. Some of this stuff could be done in a filter 1141 other-frames)))
637 ;; function, but it would be messy because restoring minibuffers affects 1142 (when dup
638 ;; global state; it's best to do it here than add a bunch of global 1143 (set-frame-parameter dup 'frameset--id nil))))
639 ;; variables to pass info back-and-forth to/from the filter function. 1144 ;; Restore minibuffers. Some of this stuff could be done in a filter
640 (cond 1145 ;; function, but it would be messy because restoring minibuffers affects
641 ((null d-mini)) ;; No frameset--mini. Process as normal frame. 1146 ;; global state; it's best to do it here than add a bunch of global
642 (to-tty) ;; Ignore minibuffer stuff and process as normal frame. 1147 ;; variables to pass info back-and-forth to/from the filter function.
643 (hasmini ;; Frame has minibuffer (or it is minibuffer-only). 1148 (cond
644 (when (eq (cdr (assq 'minibuffer frame-cfg)) 'only) 1149 ((null d-mini)) ;; No frameset--mini. Process as normal frame.
645 (setq frame-cfg (append '((tool-bar-lines . 0) (menu-bar-lines . 0)) 1150 (to-tty) ;; Ignore minibuffer stuff and process as normal frame.
646 frame-cfg)))) 1151 ((car d-mini) ;; Frame has minibuffer (or it is minibuffer-only).
647 (t ;; Frame depends on other frame's minibuffer window. 1152 (when (eq (cdr (assq 'minibuffer frame-cfg)) 'only)
648 (let* ((mb-frame (or (cl-find-if 1153 (setq frame-cfg (append '((tool-bar-lines . 0) (menu-bar-lines . 0))
649 (lambda (f) 1154 frame-cfg))))
650 (string= (frame-parameter f 'frameset-id) 1155 (t ;; Frame depends on other frame's minibuffer window.
651 mb-id)) 1156 (when mb-id
652 (frame-list)) 1157 (let ((mb-frame (frameset-frame-with-id mb-id))
653 (error "Minibuffer frame %S not found" mb-id))) 1158 (mb-window nil))
654 (mb-param (assq 'minibuffer frame-cfg)) 1159 (if (not mb-frame)
655 (mb-window (minibuffer-window mb-frame))) 1160 (delay-warning 'frameset
656 (unless (and (window-live-p mb-window) 1161 (format "Minibuffer frame %S not found" mb-id)
657 (window-minibuffer-p mb-window)) 1162 :warning)
658 (error "Not a minibuffer window %s" mb-window)) 1163 (setq mb-window (minibuffer-window mb-frame))
659 (if mb-param 1164 (unless (and (window-live-p mb-window)
660 (setcdr mb-param mb-window) 1165 (window-minibuffer-p mb-window))
661 (push (cons 'minibuffer mb-window) frame-cfg)))))) 1166 (delay-warning 'frameset
662 ;; OK, we're ready at last to create (or reuse) a frame and 1167 (format "Not a minibuffer window %s" mb-window)
663 ;; restore the window config. 1168 :warning)
664 (setq frame (frameset--get-frame frame-cfg window-cfg 1169 (setq mb-window nil)))
665 (or filters frameset-filter-alist) 1170 (when mb-window
666 force-onscreen)) 1171 (push (cons 'minibuffer mb-window) frame-cfg))))))
667 ;; Set default-minibuffer if required. 1172 ;; OK, we're ready at last to create (or reuse) a frame and
668 (when default (setq default-minibuffer-frame frame))) 1173 ;; restore the window config.
669 (error 1174 (setq frame (frameset--restore-frame frame-cfg window-cfg
670 (delay-warning 'frameset (error-message-string err) :error)))) 1175 (or filters frameset-filter-alist)
1176 force-onscreen))
1177 ;; Set default-minibuffer if required.
1178 (when default (setq default-minibuffer-frame frame))))
1179 (error
1180 (delay-warning 'frameset (error-message-string err) :error))))))
671 1181
672 ;; In case we try to delete the initial frame, we want to make sure that 1182 ;; In case we try to delete the initial frame, we want to make sure that
673 ;; other frames are already visible (discussed in thread for bug#14841). 1183 ;; other frames are already visible (discussed in thread for bug#14841).
674 (sit-for 0 t) 1184 (sit-for 0 t)
675 1185
676 ;; Delete remaining frames, but do not fail if some resist being deleted. 1186 ;; Delete remaining frames, but do not fail if some resist being deleted.
677 (unless (eq reuse-frames 'keep) 1187 (unless (eq reuse-frames :keep)
678 (dolist (frame (sort (nconc (if (listp reuse-frames) nil other-frames) 1188 (dolist (frame (sort (nconc (if (listp reuse-frames) nil other-frames)
679 frameset--reuse-list) 1189 frameset--reuse-list)
680 #'frameset-sort-frames-for-deletion)) 1190 ;; Minibufferless frames must go first to avoid
1191 ;; errors when attempting to delete a frame whose
1192 ;; minibuffer window is used by another frame.
1193 #'frameset-minibufferless-first-p))
681 (condition-case err 1194 (condition-case err
682 (delete-frame frame) 1195 (delete-frame frame)
683 (error 1196 (error
684 (delay-warning 'frameset (error-message-string err)))))) 1197 (delay-warning 'frameset (error-message-string err))))))
685 (setq frameset--reuse-list nil) 1198 (setq frameset--reuse-list nil
1199 frameset--target-display nil)
686 1200
687 ;; Make sure there's at least one visible frame. 1201 ;; Make sure there's at least one visible frame.
688 (unless (or (daemonp) (visible-frame-list)) 1202 (unless (or (daemonp) (visible-frame-list))
689 (make-frame-visible (car (frame-list)))))) 1203 (make-frame-visible (car (frame-list))))))
690 1204
1205
1206;; Register support
1207
1208(defun frameset--jump-to-register (data)
1209 "Restore frameset from DATA stored in register.
1210Called from `jump-to-register'. Internal use only."
1211 (let* ((delete (and current-prefix-arg t))
1212 (iconify-list (if delete nil (frame-list))))
1213 (frameset-restore (aref data 0)
1214 :filters frameset-session-filter-alist
1215 :reuse-frames (if delete t :keep))
1216 (mapc #'iconify-frame iconify-list)
1217 (let ((frame (frameset-frame-with-id (aref data 1))))
1218 (when frame
1219 (select-frame-set-input-focus frame)
1220 (goto-char (aref data 2))))))
1221
1222;;;###autoload
1223(defun frameset-to-register (register &optional _arg)
1224 "Store the current frameset in register REGISTER.
1225Use \\[jump-to-register] to restore the frameset.
1226Argument is a character, naming the register."
1227 (interactive "cFrameset to register: \nP")
1228 (set-register register
1229 (registerv-make
1230 (vector (frameset-save nil
1231 :app 'register
1232 :filters frameset-session-filter-alist)
1233 ;; frameset-save does not include the value of point
1234 ;; in the current buffer, so record that separately.
1235 (frameset-frame-id nil)
1236 (point-marker))
1237 :print-func (lambda (_data) (princ "a frameset."))
1238 :jump-func #'frameset--jump-to-register)))
1239
691(provide 'frameset) 1240(provide 'frameset)
692 1241
693;;; frameset.el ends here 1242;;; frameset.el ends here