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

From: Juanma Barranquero <[email-masked]>
Date: Mon, 29 Jul 2013 04:14:04 +0200

On Sun, Jul 28, 2013 at 8:56 PM, Drew Adams <[email-masked]> wrote:

> In brief:
>
>  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).
- Not flexible (they have basically two ops,
current-frame-configuration and set-window-configuration;
frame-configuration-to-register seems an afterthought).
- 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).
- Opaque; not supposed to be modified or played with other than from
their get/set functions.
- Non-serializable; they are intended for short-term use in the same
session they were created (they contain references to live objects).
- Relatively oriented to one frame or simple frame setups (they are
unable to recreate deleted frames, for example).

Frame states:
- Are high level (coded in Elisp)
- Designed to be flexible: hooks, filters, etc. to modify what is saved and how.
- Intended as a UI tool for humans (to save the desktop).
- Not very well documented yet, but not opaque (again: filters, hooks, etc.)
- Serializable by definition; their very purpose is to be used in a new session.
- Able to recreate and manipulate complex frame configurations
(including deleted frames).

There are a few more differences, of course. For example,
frame-configurations have a clear API, while frame-states currently do
not (I have fixing that as a short term goal).

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. They are not going to be
used in the same way, or by the same functions. It does not make sense
to convert a frame-state into a frame-configuration and feed it back
to set-frame-configuration. If you want to use frame-states, just use
them.

> 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.
- 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.

> 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.

> 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. OTOH, though frame-states support recording a partial
list of frames, they are a global operation. They are not designed for
mix&match of different sets. Saving a frame-state is inherently
global; 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). 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. 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.

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 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.

> In any case, some code
> factoring in desktop.el would be helpful anyway, as we've discussed.
> It is not the first priority for desktop.el, perhaps, but it will be
> good if it is done at some point.

Agreed.

> 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).

> Not explicitly, IIUC.  The desktop code does not use, and does not
> provide for, readable frame configurations.

Yes. They will, if possible.

> 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

> 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.

> Well *I* don't see my little trick.  What do you mean?
> I just tried to write down more or less what I see in an existing
> frame config: it is just that form, no?  Let me know if I'm missing
> something here.

Yes, sorry. I missed the ... after FRAME+WINDOW-CONFIG :-)

> 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.

> Sorry, I don't understand that at all.  Why introduce another list
> level?  It would be just what I wrote, I think:
> (frame-configuration FRAME+WINDOW-CONFIG...)

Yes, sorry (again).

> 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 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. 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).

> 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 added desktop--mini to all existing frames. Then you delete a
frame, call readable-frame again, and doing so renumber all frames.
Then try to restore both frames and likely get conflicts or errors.
frame-states partially depend on global state, because their goal is
to save and restore *desktops*, and I've done everything I could to
make that operation (saving & restoring the desktop) as seamless and
painless as possible, even at the cost of making some things less
generic.

> Probably if we did that then we would want to let you specify the
> frame parameters to record via one or more parameters to the function
> rather than using `desktop--filter-frame-parms' inside the function body.

Yes, that's an example of what I was talking about not being very
generic. But that gets messy quite fast, I think.