Subject: bug#14964: 24.3.50; doc of `compare-window-configurations'

From: Drew Adams <[email-masked]>
Date: Mon, 29 Jul 2013 16:56:48 -0700 (PDT)

> >  Tighten it up.  Harmonize the new readable frame & window data
> >  structures with the existing non-readable frame and window
> >  configuration structures.  Enable code to use them the same way.
> >
> >  Offer explicit readable frame and window configs whose contents and
> >  structure are as compatible as possible with the current, non-readable
> >  ones.  Some existing code that uses such configs would then just work,
> >  and future code too would use structures that say what they are.
> 
> The more I think about the whole issue, the less I agree with this point.
> 
> I think the superficial similarities between frame configurations and
> desktop's frame-states hide the fact that their goals are deeply
> different.
> 
> Frame configurations:
> - Are low level (coded in C).

Code them in Lisp.

> - Not flexible (they have basically two ops,
> current-frame-configuration and set-window-configuration;
> frame-configuration-to-register seems an afterthought).

Add more ops.  Make them general.

> - Intended as an API for programmers, to wrap changes to window and
> frame configs that will be undone (as in a unwind-protect, for
> example).

They are just structures.  You can use them for anything their properties
permit.  And I am not proposing that they need to stay the same as they
are now, in any case, if you feel they are limited.

> - Opaque; not supposed to be modified or played with other than from
> their get/set functions.

Supposed to?  Who says so?  And just because something is a list or a
defstruct, and so you *can* modify it, that does not mean that you *need*
to modify it.  The uses that Desktop or `frame-configuration-to-register'
make of a (new, unified) frame configuration would be different, but they
could both use the same structure.

And even with just get/set, that is something useful.  If the structures
were Lisp-readable they would be even more useful (a lot more).  Even now
you can modify or construct a frame configuration by hand and set the
current state to use that configuration using that constructed config.

(let* ((f1  (get-a-frame "iii.el"))
       (p1  (frame-parameters f1))
       (w1  (current-window-configuration f1))
       (f2  (get-a-frame "my-directory"))
       (p2  (frame-parameters f2))
       (w2  (current-window-configuration f2))
       (f3  (get-a-frame "My Emacs Minibuffer"))
       (p3  (frame-parameters f3))
       (w3  (current-window-configuration f3)))
  (setq new `(frame-configuration (,f1 ,p1 ,w1)
                                  (,f2 ,p2 ,w2)
                                  (,f3 ,p3 ,w3))))

(set-frame-configuration new)

Frame configurations today are not completely opaque.  Admittedly, the
embedded window-config part is opaque.

> - Non-serializable; they are intended for short-term use in the same
> session they were created (they contain references to live objects).

You are just repeating what exists today for these things.  That's my
point: let them be (optionally) serializable.  I'm asking that the
existing frame and window config structures be made transparent (not
opaque) and serializable, and that the Desktop and window-state saving
code use them.

It makes no sense to counter that they are not (yet) serializable and
are opaque.  Or that the Desktop frame-saving and the `window-state-*'
window-saving are not (yet) recognizable, component-accessible
structures.  That's the whole point of my request: to do just that,
bring them together, giving the advantages of each to both.

> - Relatively oriented to one frame or simple frame setups (they are
> unable to recreate deleted frames, for example).

Not to one frame, unless you are referring to a window config, in which
case the same applies to `window-state-*' AFAIK.  And yes, being able
to restore  dead frames is an advantage to your code.  Let's give it
to frame configurations, perhaps optionally.

> Frame states:
> - Are high level (coded in Elisp)

Good.  Same would be good for frame configs.  They could be based on
(parts of) your existing code, for instance.  Certainly things should
be factored and reused rather than duplicated.

> - Designed to be flexible: hooks, filters, etc. to modify what is saved and
> how.

Again, good.  Now add those advantages to frame configs, and add the frame
config advantages to Desktop parts.

> - Intended as a UI tool for humans (to save the desktop).

So are frame configs and window configs used as UI tools.  Why do you
think we have `frame-configuration-to-register' and then jumping to a
frame-config register?

> - Not very well documented yet, but not opaque (again: filters, hooks, etc.)

Transparent is good.  Should be the same for frame & window configs.

> - Serializable by definition; their very purpose is to be used in a new
> session.

Should be added to frame and window configs too.  That's a main suggestion
I am making.  Whether there will still be some need for a C-code version or
a non Lisp-readable version of a frame or window config is TBD.  I doubt
it would be needed.  But if someone thinks that access to the actual frame
is needed, instead of just sufficient info to re-create it, then we can
consider keeping that representation as an alternative one.

I, for one, would I think vote for tossing it and moving to only Lisp and
only a Lisp-readable representation - essentially the code you've written.
Use it as (the basis of) the new implementation of frame configs.  Use
Martin's `window-state-*' code as (the basis of) the new implementation of
window configs.

I'm not at all rejecting what you've done.  I'm asking that you take it
one step further and replace the existing frame & window configs with
your implementation.

And at that same time, make your code use a standard, recognizable
structure with defined accessor functions for its components.  IOW, make
it usable for frame-config and window-config uses too.

> - Able to recreate and manipulate complex frame configurations
> (including deleted frames).

Also a plus.  Also perhaps useful for frame configs.  If not, can be
ignored or omitted for a frame config.

(For purposes of discussion, let's not refer to your frame-set state info
as a "frame configuration"; that just makes discussion confusing, since
we are discussing also Emacs frame configurations per se.)

> There are a few more differences, of course.

If there were no differences there would be no need to unify the two.

> For example, frame-configurations have a clear API, while frame-states
> currently do not (I have fixing that as a short term goal).

That's a big part of what my request is.  While doing that, if you do it
in such a way that we can replace the existing frame-config structures
and their APIs, then my request will be fulfilled.

I don't really care so much what the final structure looks like or what
info it contains.  As long as it can be used (together with other
state-saving code perhaps) for saving desktops AND it can be used in
place of an Emacs frame config (e.g. get a config from the current
set of states and set the current set of states from a frame config, as
now), I'll be happy.

That, and hopefully (a) a recognizing predicate (e.g.
`frame-configuration-p') and component accessor functions (for whatever
components you end up having that serve both generally (e.g. for
frame-config commands/functions) and for desktops.

> I don't really see the point of trying to make frame-states more
> similar to frame-configurations, or to make a future serializable
> frame-configuration similar to frame-states.

I can tell you don't.

> They are not going to be used in the same way, or by the same
> functions.

They will hold the same info.  Any differences in content today would
be removed: any purely desktop-specific stuff would not be part of the
frame config.  It would be something that can serve for both uses (and
other uses as well).

> It does not make sense to convert a frame-state into a
> frame-configuration and feed it back to set-frame-configuration.

Right.  That's precisely what I want to avoid.  Today, if I want to take
the info saved in a frame-config register and persist it and use it in
another session, I would need to convert that info to your frame-state
form.  If I want to put your frame-state info into a register so it can
be used with `jump-to-register' to restore/apply it, I would need to
convert it to a frame config.

And if I want to test whether some list in fact represents frame-state
info then what do I do?  There is no identifying predicate, such as
`frame-configuration-p'.  And there is no identifying tag (AFAIK), such
as a `frame-config' wrapper or an identifying attribute/keyword.
Give us a way to recognize the beast, please.

And if I want the equivalent of function `window-configuration-frame'
for a `window-state-*' saved state, what do I do?  Accessor functions,
please.

> If you want to use frame-states, just use them.

See above.  I want to use them where today I would use frame and window
configs.  And yes, I want them to be persistent or persitable (i.e.,
optional).

> > I don't know which frame parameters are included in ALIST.  What I
> > would like is that, if possible, those that are included currently in
> > a non-readable frame config are also included in a readable one.
> 
> Most are. Of those that do not:
> 
> - parent-id, window-id: does not make sense to save them, you cannot
> restore them.

Then they would be removed.  Or if we keep both live-object configs when
providing also persistable configs, then they would be ignored for
operations of restoring from persisted data.  IOW, they should not be
a bother.

> - buried-buffer-list, buffer-list: perhaps, but that's a bit more
> complex and it's not clear yet that it is very useful.
> - name: it could be saved, but then assigning it sets explicit-name to
> t and it stops being dynamic. A workaround could be found if someone
> presents a convincing use case.
> - font-backend: I'm removing it because it's rare setting a font
> backend explicitly for a frame, but it would be harmless to allow it
> to be saved.

Any impedance mismatch can be handled by just using a lowest common
denominator for the common structure.  If additional things are needed
for a given side (e.g. Desktop or in-memory/same-session uses) then
such things can either be included and ignored or excluded and handled
separately by the different using software.

> > I think you mean that it is not very different from a desktop SET of
> > frame stateS (plural) - or a state that represents multiple frames.
> 
> Yes, I'm calling what desktop saves a "frame-state", though it is the
> state of a set of  frames. Like a frame-configuration is the state of
> several frames, not just one.

Not a great name, but the name is not the most important thing now.
 
> > Yes, of course.  It's all about factoring and generalizing: the focus
> > is on representing an arbitrary set of frames, not on saving a desktop.
> 
> I partially agree; a frame-state can be useful even if not saved
> persistently.

Well, there's one thing we agree on.  And perhaps we agree that the state
of a set of frames can be useful even if it is saved. ;-)  Now just what
needs to be in such a state in each case is open and likely to be somewhat
different.  That's not the end of the world.  What is needed for set/get
during a session might well be different from what is needed for
save/restore across sessions.  And perhaps the set of frames that is a
reasonable unit would be different as well.

The point is that there is some info, in fact a lot, I suspect, that is
in common.  And there are some uses/operations that are also in common.
That should be plenty to serve as a basis for finding common ground and
factoring it out to serve both use cases.

> OTOH, though frame-states support recording a partial
> list of frames, they are a global operation. 

I can see that.  But they could also serve a need that did not have that
additional requirement.  The fact that they can save more and do more
does not preclude the ability to use them for something less as well.

> They are not designed for mix&match of different sets.

Maybe they should be, at some point.  Dunno.

But that is likely beside the point now.  Frame configurations are also
not mix&match.  The set of all current frames is saved and restored.
OK, not dead frames etc.  But that's a limitation that could be fixed,
e.g., using some of your code.

> Saving a frame-state is inherently global;

(Ouch; that is such a bad name.  It misleads me each time I read it
into thinking you are talking about the state of a frame, instead of the
state of all of the frames.)

> it requires information about all frames, to be able to keep
> minibuffer relationships and to set the default minibuffer frame
> (which is required to be able to correctly restore minibufferless
> frames).

See above.  I said I can understand that.  But as the French like to say,
"That which can do more can also do less."

> The more you slice & dice the existing frames into different
> frame-states and try to restore them one upon another, the more likely
> is you'll hit trouble.

So let's not do that.

> Also, I have yet so see a use case that does
> not involve saving all (or almost all) existing frames; going from
> frame state A to B to C to E and back to any one of them looks useful.
> Fragmenting A into A1, A2, A3, A4 and then wanting to restore just A1
> and A3 seems less useful to me.

That sounds OK to me.  That is aligned with frame configs too, AFAIK.

I can imagine wanting to be able to set a single frame's state to some
saved state, modulo loss of info about some of things you mentioned.
IOW, you can do some things that could be useful, even if you might not
be able to do everything imaginable.

I already have code that lets you pick up properties from a given frame
interactively and "paste" them onto another frame.  This would be an
extension of that.

But if you don't imagine such a possibility that's OK too.  Let's
limit things to a frame config (or frame-set state or whatever)
representing a snapshot of the global state: all frames (dead and alive,
whatever).

> Also, frame-states *do* have some focus on saving a desktop. They are
> designed to survive as faithfully as possible being restored in a tty
> session and back into a GUI session, for example.

That use would be kept, of course.  If some of the info necessary for
that is not useful for other uses such as what we use frame configs for
today, then that info could alternatively be kept in a separate data
structure (for desktop only, if there is no other use for it).

I don't mean that we need to limit what a "new-style" frame config can
do or be used for to what today's frame configs can do or be used for.
I mean only that if, as you suggest, there are some things that Desktop
needs to save for its use of frames that you think will never have any
use outside Desktop, then fine, let's leave those things out of the 
new-style frame config but keep them for Desktop, separately.

> That means that they can sometimes contain information (in the form
> of desktop--X parameters) that does not reflect the current state of
> any frame, but the future state of a frame if it is ever again restored
> into a GUI session.

OK.  Dunno whether that fits into the description of my last paragraph
or not.  If you think it is stuff that will never serve outside Desktop
then it does.  If not, perhaps it should be included in a new-style
frame config.

> > E.g. (current-frame-configuration t) would return a writable &
> > readable frame configuration.
> 
> That could be useful, but, as discussed above, I just think that its
> result is just not a frame-state (not implementation-wise, of course,
> but goal-wise).

I don't argue that it is.  I don't argue that you can just take the
existing thing you call a frame-state (a state representing the state
of all frames) and use it as a new-style frame config.  I expect that
there will some stuff to exclude from the frame config and keep only
in Desktop.

> > Not explicitly, IIUC.  The desktop code does not use, and does not
> > provide for, readable frame configurations.
> 
> Yes. They will, if possible.

Ah, well maybe we will converge.  Let's aim to have it replace the
existing frame configs then.  And let's please make sure such a beast
can be easily recognized and its components easily accessed (e.g. by name).

If we do that then I'll be a happy camper.  (Likewise for window configs.)

> > And the `window-state-*' functions do not explicitly provide for
> > readable window configurations.  Consider something like this, for
> > example:
> >
> > (defun readable-window-config (window-configuration)
> >   "Return a Lisp-readable representation of WINDOW-CONFIGURATION.
> > The form is (window-configuration FRAME-NAME . ROOT-WINDOW-STATE)."
> >   ;; Record the name of the frame and its root window state.
> >   (let ((winfr  (window-configuration-frame window-configuration)))
> >     `(window-configuration
> >       ,(frame-parameter winfr 'name)
> >       . ,(window-state-get (frame-root-window winfr) 'WRITABLE))))
> 
> What if the frame is dead?
>   (frame-parameter winfr 'name) => nil
>   (frame-root-window winfr) => error

It was an illustration of the kind of thing that's needed, not a
proposed implementation.  I think (hope) you can see that and appreciate
that gross level of communication as well.

> > Using a defstruct would also be OK, but then the form would be even
> > more different from a (current) non-readable frame config, for
> > instance.  I would not oppose using a defstruct for both readable
> > and non-readable.  A defstruct has some advantages.
> 
> Yes, but it is currently a bit of overkill.

I'm not sure it is.  We should have a type predicate and accessor (i.e.,
slot functions).  Those are the main things a defstruct offers.  What
would be wrong with using a defstruct, concretely ("overkill" doesn't
explain much)?

> > Yes, but more correctly, of any set of frames.  Yes, it is created by
> > `current-frame-configuration', which records all existing frames at
> > the time of invocation.  But it does not necessarily continue to
> > represent all of the existing frames.
> 
> The problem is that it doesn't even continue to represent all existing
> frames at the time of invocation. Only these that are still alive. As
> I said above, the frame configuration interface doesn't seem oriented
> to slice & dice, but to save & restore full sets, and particularly to
> relatively short term
> save/do-something-that-can-affect-windows&frames/restore operations.

Agreed.  But it can be made to be more flexible.  Your code already
proves that.  It can be made to (e.g., optionally) include dead frames
etc.

> > Granted, but it is sugar that would let code use the result as it
> > now uses a non-readable frame config.  That's the point: use frame
> > configs.
> 
> What kind of code do you think that it is currently using frame
> configurations and would want to use frame configurations and
> frame-states in the future?

See above.  Both code and interactively, saving and restoring the states
of the current set of frames can be useful.  We can discuss that more if
necessary.

> > See above for some more info.  In sum, provide aalternative,
> > Lisp-readable representations of both frame configs and window configs,
> > and update the functions that use/create such configs to also use/create
> > the readable form (creating a readable config would be optional via an
> > optional `WRITABLE parameter).
> 
> I think that's a worthwhile goal, but I see it as very different of
> what I need and I'm trying to do.

It's different from what you've done so far.  But it's related.
And it's especially related to your longer-term intention to factor the
desktop.el ball of wax into useful and more modular pieces.

> Even if Martin or Stefan or someone
> else added these readable frame and window configs, they wouldn't
> substitute frame-states (though I could use them to simplify part of
> what I'm doing right now, I suppose).

Yes, you could, I think.  That's the upside for desktop.el.  The upside
more generally is for users (code and people) of today's frame and window
configs, which would become better in several ways (persistent, able to
restore more, smarter, transparent, Lisp-level, etc.)

> > I did not mention serializing individual frames here, AFAIK.  But I'm
> > actually in favor of that as well, like we do for windows with
> > `window-state-get' + WRITABLE.  Why not?
> >
> > (defun readable-frame (frame)
> >   "Return a Lisp-readable representation of FRAME.
> > Form is (frame . FRAME-PARAMETERS)."
> >     `(frame . ,(desktop--filter-frame-parms (frame-parameters frame) t)))
> 
> You just... [lots of specific description of why this is not the right
> implmentation of a `readable-frame' function]

See above - it was an illustration of the idea, not an implementation
proposal.

Gotta go.  HTH.  Hoping we will end up at least understanding each other.
Hoping beyond that that we might find some common ground.  And of course
thanks again for your work on this stuff (frame support in Desktop,
Lisp-level for frame save/restore, etc.