On Wed, Jul 10, 2019 at 10:28:15PM +0200 I heard the voice of
Rhialto, and lo! it spake thus:
>
> [...] None of the patches attempting to fix these bugs.
Hm. I read this as "none of the patches fixed these bugs", but now I
think it means "I didn't try it patched". Is that right?
> #4 OtpRestackWindow (twm_win=twm_win@entry=0x74ba18b40c00)
> at /mnt/vol1/rhialto/cvs/other/ctwm/bzr/trunk/otp.c:1626
> #5 0x000000000042f705 in SetFocus (tmp_win=0x74ba18b40800, tim=<optimized
> out>)
> at /mnt/vol1/rhialto/cvs/other/ctwm/bzr/trunk/win_ops.c:168
is definitely a non-patched backtrace, since SetFocus() doesn't call
Restack with the patch. I can reproduce that crash with xine and
roughly your steps (just mousing, not warp'ing, since that doesn't
quite work right in xnest/xephyr) with trunk, but with a patched tree
I can't.
Looking into it did turn up a few more cases; we need to make similar
changes in the FocusIn/FocusOut handlers. As far as I can tell from
reading and thinking, anyway. It's extremely hard to actually trigger
them, since with ClickToFocus we just call SetFocus(), and without we
get EnterNotify -> SetFocus. So, I don't have a good before/after
test on it, but it should be right.
So, updated patch attached; as far as I can tell, this (and the
previous variant) should fix that crash, and if that's also your
historic one, should cleanse the world.
> Now I use the window ring to bring focus to W which comes up
> partially on top of P. Now I move the mouse directly into P. (To my
> surprise, W did not disappear behind a rising F).
Correct; in this case, F doesn't rise, only P does. That's what I've
tentatively classified as a cosmetic-but-not-correctness bug. Should
fix that in trunk, but if we can't use it to crash things I'd like to
skip for backporting, for minimalism's sake.
> Sometimes I notice another weirdness: despite using f.warpring to go
> to W, it does not always come to the top as it should (since F isn't
> focused any more, it has no reason to stay on top of W).
When F loses focus, it will drop back down to its base OTP layer,
which would be the same layer as W. But it'll wind up as the _top_
thing on that layer, so it'll still be above W.
--
Matthew Fuller (MF4839) | [email protected]
Systems/Network Administrator | http://www.over-yonder.net/~fullermd/
On the Internet, nobody can hear you scream.
=== modified file 'event_handlers.c'
--- event_handlers.c 2018-11-19 06:15:52 +0000
+++ event_handlers.c 2019-07-11 02:13:12 +0000
@@ -367,35 +367,36 @@
static void
HandleFocusIn(void)
{
-#ifdef EWMH
- TwmWindow *old_focus = Scr->Focus;
-#endif
-
if(! Tmp_win->wmhints->input) {
return;
}
if(Scr->Focus == Tmp_win) {
return;
}
+
+#ifdef EWMH
+ // Handle focus-dependent re-stacking of what we're moving out of.
+ if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) {
+ OtpUnfocusWindow(Scr->Focus);
+ // NULL's Scr->Focus
+ }
+#endif
+
if(Tmp_win->AutoSqueeze && Tmp_win->squeezed) {
AutoSqueeze(Tmp_win);
}
SetFocusVisualAttributes(Tmp_win, true);
+#ifdef EWMH
+ // Handle focus-dependent re-stacking of what we're moving in to.
+ if(Tmp_win && OtpIsFocusDependent(Tmp_win)) {
+ OtpFocusWindow(Tmp_win);
+ // Sets Scr->Focus
+ }
+#endif
+
+ // Redundant in EWMH case
Scr->Focus = Tmp_win;
-
-#ifdef EWMH
- /*
- * Some EWMH flags may affect stacking, so after we change
- * Scr->Focus...
- */
- if(old_focus && OtpIsFocusDependent(old_focus)) {
- OtpRestackWindow(old_focus);
- }
- if(Tmp_win && OtpIsFocusDependent(Tmp_win)) {
- OtpRestackWindow(Tmp_win);
- }
-#endif
}
@@ -413,18 +414,20 @@
}
SetFocusVisualAttributes(Tmp_win, false);
- Scr->Focus = NULL;
-
#ifdef EWMH
/*
* X-ref HandleFocusIn() comment. FocusOut is only leaving a window,
* not entering a new one, so there's only one we may need to
* restack.
*/
- if(Tmp_win && OtpIsFocusDependent(Tmp_win)) {
- OtpRestackWindow(Tmp_win);
+ if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) {
+ OtpUnfocusWindow(Scr->Focus);
+ // NULL's Scr->Focus
}
#endif
+
+ // Redundant in EWMH case
+ Scr->Focus = NULL;
}
=== modified file 'otp.c'
--- otp.c 2018-08-19 22:50:13 +0000
+++ otp.c 2019-07-07 04:44:45 +0000
@@ -1579,6 +1579,99 @@
}
+
+/**
+ * Focus/unfocus backend. This is used on windows whose stacking is
+ * focus-dependent (e.g., EWMH fullscreen), to move them and their
+ * transients around. For these windows, getting/losing focus is
+ * practically the same as a f.setpriority, except it's on the calculated
+ * rather than the base parts. And it's hard to re-use our existing
+ * functions to do it because we have to move Scr->Focus before the main
+ * window changes, but then it's too late to see where all the transients
+ * were.
+ *
+ * There are a number of unpleasant assumptions in here relating to where
+ * the transients are, and IWBNI we could be smarter and quicker about
+ * dealing with them. But this gets us past the simple to cause
+ * assertion failures, anyway...
+ */
+static void
+OtpFocusWindowBE(TwmWindow *twm_win, int oldprio)
+{
+ OtpWinList *owl = twm_win->otp;
+
+ // This one comes off the list, and goes back in its new place.
+ RemoveOwl(owl);
+ InsertOwl(owl, Above);
+
+ // Now root around for any transients of it, and
+ // nudge them into the new location. The whole Above/Below thing is
+ // kinda a heavy-handed guess, but...
+ //
+ // This is nearly a reimplementation of TryToMoveTransientsOfTo(),
+ // but the assumption that we can find the transients by starting
+ // from where the old priority was in the list turns out to be deeply
+ // broken. So just walk the whole thing. Which isn't ideal, but...
+ //
+ // XXX It should not be this freakin' hard to find a window's
+ // transients. We should fix that more globally.
+ OtpWinList *trans = Scr->bottomOwl;
+ while((trans != NULL)) {
+ // Gotta pre-stash, since we're sometimes about to move trans.
+ OtpWinList *next = trans->above;
+
+ if((trans->type == WinWin)
+ && isTransientOf(trans->twm_win, twm_win)) {
+ // Bounce this one
+ RemoveOwl(trans);
+ InsertOwl(trans, Above);
+ }
+
+ trans = next;
+ }
+
+ OtpCheckConsistency();
+}
+
+/**
+ * Unfocus a window. This needs to know internals of OTP because of
+ * focus-dependent stacking of it and its transients.
+ */
+void
+OtpUnfocusWindow(TwmWindow *twm_win)
+{
+ // Stash where it currently appears to be. We assume all its
+ // transients currently have the same effective priority. See also
+ // TryToMoveTransientsOfTo() which makes the same assumption. I'm
+ // not sure that's entirely warranted...
+ int oldprio = PRI(twm_win->otp);
+
+ // Now tell ourselves it's unfocused
+ assert(Scr->Focus == twm_win);
+ Scr->Focus = NULL;
+
+ // And do the work
+ OtpFocusWindowBE(twm_win, oldprio);
+}
+
+/**
+ * Focus a window. This needs to know internals of OTP because of
+ * focus-dependent stacking of it and its transients.
+ */
+void
+OtpFocusWindow(TwmWindow *twm_win)
+{
+ // X-ref OtoUnfocusWindow() comments.
+ int oldprio = PRI(twm_win->otp);
+
+ assert(Scr->Focus != twm_win);
+ Scr->Focus = twm_win;
+
+ OtpFocusWindowBE(twm_win, oldprio);
+}
+
+
+
/*
* Calculating effective priority. Take the base priority (what gets
* set/altered by various OTP config and functions), and then tack on
=== modified file 'otp.h'
--- otp.h 2017-06-01 15:51:31 +0000
+++ otp.h 2019-07-07 04:42:48 +0000
@@ -61,6 +61,9 @@
void OtpStashAflagsFirstTime(TwmWindow *twm_win);
void OtpRestackWindow(TwmWindow *twm_win);
+void OtpUnfocusWindow(TwmWindow *twm_win);
+void OtpFocusWindow(TwmWindow *twm_win);
+
/* functions to manage the preferences. The second arg specifies icon prefs */
void OtpScrInitData(ScreenInfo *);
name_list **OtpScrSwitchingL(ScreenInfo *, WinType);
=== modified file 'win_ops.c'
--- win_ops.c 2018-11-18 21:09:02 +0000
+++ win_ops.c 2019-07-07 04:42:48 +0000
@@ -129,9 +129,6 @@
{
Window w = (tmp_win ? tmp_win->w : PointerRoot);
bool f_iconmgr = false;
-#ifdef EWMH
- TwmWindow *old_focus = Scr->Focus;
-#endif
if(Scr->Focus && (Scr->Focus->isiconmgr)) {
f_iconmgr = true;
@@ -153,24 +150,34 @@
AutoSqueeze(Scr->Focus);
}
SetFocusVisualAttributes(Scr->Focus, false);
+#ifdef EWMH
+ // Priority may change when focus does
+ if(OtpIsFocusDependent(Scr->Focus)) {
+ OtpUnfocusWindow(Scr->Focus);
+ // That Scr->Focus = NULL's internally for us, but we don't
+ // care, since we're about to reset it if we need to.
+ }
+#endif
}
- if(tmp_win) {
+
+ if(tmp_win) {
if(tmp_win->AutoSqueeze && tmp_win->squeezed) {
AutoSqueeze(tmp_win);
}
SetFocusVisualAttributes(tmp_win, true);
+#ifdef EWMH
+ // Priority may change when focus does
+ if(OtpIsFocusDependent(tmp_win)) {
+ OtpFocusWindow(tmp_win);
+ // Pre-sets Scr->Focus
+ }
+#endif
}
+
+ // in the EWMH cases, this was already done.
Scr->Focus = tmp_win;
-#ifdef EWMH
- /* Priority may change when focus does */
- if(old_focus && OtpIsFocusDependent(old_focus)) {
- OtpRestackWindow(old_focus);
- }
- if(tmp_win && OtpIsFocusDependent(tmp_win)) {
- OtpRestackWindow(tmp_win);
- }
-#endif
+ return;
}