Preface: I think I'm reasonably happy with the resolutions come up
with in this mail.  At least, I don't think they're horrific, and I
don't have a lot of faith there are alternatives that balance the
competing players better.  So, I'll call this a proposed
implementation plan.


On Thu, Mar 16, 2017 at 03:48:35PM -0400 I heard the voice of
Stefan Monnier, and lo! it spake thus:
> > So that just leaves the _STATE_{ABOVE,BELOW} pieces.
> 
> Can these be removed (i.e. do we need to keep track of the earlier
> state to return to it, as is the case for _FULLSCREEN)?
> [ The answers below assume that there's no such need.  ]

Why, yes they can.  Welcome to funtimes    :)

    Atoms present in the list MUST be considered set, atoms not
    present in the list MUST be considered not set. [...] A Client
    wishing to change the state of a window MUST send a _NET_WM_STATE
    client message to the root window (see below). The Window Manager
    MUST keep this property updated to reflect the current state of
    the window.
    [...]
    [ later talk about the message semantics I won't bother pasting ]
      
<https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472615568>

Mental tracking of what you're thinking about with this stuff is
irritatingly complicated because there's a _NET_WM_STATE property on
the window, with the various _NET_WM_STATE_* atoms potentially
present/missing, and there's ALSO a _NET_WM_STATE _message_ that we
listen to receive from the client that can contain one or two of those
atoms.  With the additional fun that the message version can specify
add, remove, or toggle on them (which means we have to keep track of
what's set/not, or we don't know WTF to do when we get a toggle)[2].
So any time you're thinking of one name, you have to keep track of
which _meaning_ of that name you're thinking of.  Whee.

And as I think I've mentioned before (but repeated here for
one-stop-ness), there's extra fun in interpreting the window property,
because it serves as both (a) a way for the client to communicate its
desired initial state to the WM, and (b) a way for the WM to
communicate to client(s)[0] what it thinks of them.  Which are then
impossible to distinguish when the WM restarts.  Maybe EWMH didn't
contemplate the radical notion of a WM restarting in the middle of a
session?  But it does seem like unless we chose a route where the
meanings we assign to things are unaffected by the difference, we'll
need to put up extra properties of our own to keep track.


It seems like no matter what we do, we're going to have to bite one
bullet or another; there's gonna be some Weird or Wrong behavior, and
we'll just have to choose which set we'll redefine as a feature.


> I'd take _ABOVE to mean "raise until _ABOVE" and _BELOW to mean "lower
> until _BELOW" (so _ABOVE will never lower and _BELOW will never raise).

So, something like "if(above) effective = min(base+1, 2); if(below)
effective = max(base-1, -2);".  Reasonable.  The drawback to that is
that there's now no way[1] to get a window to below/above those levels
when the flags are set.

The alternative is that we interpret them to just mean raise/lower,
but without the floors; then we contradict the spec's stacking order
in that _ABOVE on a given window could well mean it winds up below the
default layer.  Either way, blah.  Well, I'm going to say let's take
the alternative.

The best reading I can make of the spec is that as far as it's
concerned (a little license is required as it doesn't specify a few
things),

- _DESKTOP always goes on the bottom.  We can go with that unless the
  user specifically gives a window a different OTP, in which case the
  user knows what they're doing and we go with that.

- _FULLSCREEN/focused is on top while that's set.  OK, we can just go
  with that.

- _DOCK always goes a bit above the middle.  Handle that just like
  _DESKTOP.

- _ABOVE / _BELOW cause _DOCK to be ignored, and then shift
  above/below the middle.

So I'm thinking that the smallest break we can make of user
expectations + the spec is the second choice above; we don't guarantee
that _ABOVE/_BELOW are actually above/below the middle if the user has
otherwise set the OTP (either via config or runtime funcs).  In which
case, like desktop and dock, the user Knows What They're Doing and
should win.


That leaves the question of what/if _we_ set the property back at the
window.  The spec is pretty strongly worded.

    The Window Manager MUST keep this property updated to reflect the
    current state of the window.

Well, we don't ourselves have a desktop/dock status (I would say just
being in the OTP layers we define those to be isn't the same thing).
So that's easy; we don't have those concepts ourselves, so we never
set the properties.  FULLSCREEN we set/clear[3] when we
f.fullscreenzoom; that's easy enough to handle, because it's purely a
binary "temporarily treat this as X" flag.

So we apparently should (in the RFC2119 sense, MUST) set the
_ABOVE/_BELOW flags back into the window properties whenever a window
isn't _DESKTOP or _FULLSCREEN, and the effective OTP is non-zero.
This also leads to the wacky case of "app sends us _ABOVE message,
combined with user config the window is still at negative something,
so we mark that _we're_ applying _ABOVE in our own state/prop, but
set/keep the _BELOW in the window's EWMH property".  Well, least bad
choice[4].

So our last bit of trickery is how we distinguish "the client set this
from the start, so it's telling us where to put it" from "we set this
sometime, so it's telling the client where it is" at times like
restart.

On Fri, Mar 17, 2017 at 10:46:53PM +0100 I heard the voice of
Rhialto, and lo! it spake thus:
>
> I suppose we could solve that by setting yet another property on the
> window: a ctwm-specific one that reflects the layer where the window
> eventually winds up.  When later restoring after a restart, we just
> need to look there. (Like there aren't enough properties already...)

Which I s'pose is the only reasonable way.  We should probably do this
not in a "I set the _ABOVE/_BELOW in the EWMH prop on the window"
sense, but rather "I'm currently applying _ABOVE/_BELOW in my internal
state"; that way we can know on restore whether we should be applying
a flag, or whether it's just already ending up somewhere for other
reasons.  That way we get the right answer in all cases, including
"We're using _ABOVE flag to ++ the priority, which still winds up
negative so _BELOW is set in the EWMH prop".


Now, there's one thing left uncovered; we don't preserve the effects
of f.{set,change,switch}priority over restarts.  OTOH, if we did stash
the current value and restore, it would mean that changes you make to
OnTopPriority specs in the config wouldn't take effect on restarts.
Which of those behaviors is worse?  Well, that's pretty purely
definitional, and since Stefan (intentionally or not) defined it in
the initial implementation, I don't see a good reason to argue the
opposite.


    There is also the slight possibility of the "screw you guys, I'm
    going home" solution, whereby we completely ignore _ABOVE/_BELOW
    properties/messages, and never set them ourselves either.  I don't
    like that; it's contradictory to the point of doing EWMH.  And I
    think a halfway like "we accept but never set" or vice versa is
    probably worse.  Thus, all the preceding babbling about how we
    can define things to work the best   :)


> We have similar problems, potentially, with other window details
> too.  For instance "borderless" or "titlebar-less" can be set [...]
> Fortunately, most of those attributes don't usually change over the
> lifetime of a window [...] But because these are binary [...]

Yeah.  Those two properties taken together, especially combined with
the lack of confusing multi-meaning channels like we have with EWMH
properties, mean we get to be really dumb about how we handle them and
still get the right (or at least easily define as such) answers  :)




[0] EWMH roughly intends to provide 3 communication channels:
    1) Client -> WM, to provide enhanced hinting and control.  Sorta
       an extended version of stuff like the Motif hints.
    2) WM -> Client so the client can know what we're doing with it.
    3) WM -> [other clients], so things like e.g. the WorkSpaceManager
       could be implemented by an external program instead of by the
       WM itself.
    The various ways in which the 3 step on each other is apparently
    part of the fun of experience.  Like standing in line for 6 hours
    at Disneyland.

[1] Well, aside from e.g. 'EWMHIgnore { "STATE_ABOVE" }' or the like.
    Which _does_ work, but is global.  Can't do it just to a single
    window, and can't change at runtime.

[2] This is part of the reason behind my idea of "base + (interpret
    current flags) = effective" for the tracking, instead of just
    trying to track the final answer; we'll have to do a bunch of
    magic to keep track of which of these flags have already been
    applied anyway so we know what and when to add/remove and avoid
    double-applying, so we might as well just have it explicit in the
    process.  That way we can just blindly un/set and trigger recalc,
    and for the low low cost of a few occasionally unnecessary
    additions be sure to get the right answer.

[3] Actually, we currently don't; the _FULLSCREEN and
    _MAXIMIZED_{VERT,HORZ} are a bit mixed up in our zooming.  But
    that works out to be reasonable straightforward bugfixes.

[4] Well, least bad unless the client thinks it means we didn't hear
    it, so keeps sending "unset _BELOW, set _ABOVE" messages.  That
    would be amusing (maybe only if it's not you it's happening to).
    Well, that's probably a stupid thing for the client to do; even
    EWMH says
      The window manager is supposed to be in charge of window
      management policy[...]
    so the client should damn well accept that it's making
    _suggestions_, and we (or rather, the user acting through us) get
    to make the final calls.


-- 
Matthew Fuller     (MF4839)   |  [email protected]
Systems/Network Administrator |  http://www.over-yonder.net/~fullermd/
           On the Internet, nobody can hear you scream.

Reply via email to