After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 647706 - do better tracking of focus_window
do better tracking of focus_window
Status: RESOLVED FIXED
Product: mutter
Classification: Core
Component: general
git master
Other Linux
: Normal normal
: ---
Assigned To: Owen Taylor
mutter-maint
steam
: 597352 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2011-04-13 20:21 UTC by Dan Winship
Modified: 2013-05-22 17:46 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
display: clean up focus_window vs expected_focus_window (32.50 KB, patch)
2011-04-29 21:33 UTC, Dan Winship
none Details | Review
display: clean up focus_window vs expected_focus_window (32.58 KB, patch)
2011-04-29 21:44 UTC, Dan Winship
none Details | Review
display: clean up focus_window vs expected_focus_window (32.68 KB, patch)
2011-04-29 21:48 UTC, Dan Winship
needs-work Details | Review
display: clean up focus_window vs expected_focus_window (34.10 KB, patch)
2011-07-15 23:53 UTC, Dan Winship
needs-work Details | Review
test-focus: test program for focus window management (12.00 KB, patch)
2011-09-02 00:11 UTC, Dan Winship
none Details | Review
display: clean up focus_window vs expected_focus_window (35.21 KB, patch)
2011-09-02 00:24 UTC, Dan Winship
none Details | Review
test-focus: test program for focus window management (12.00 KB, patch)
2012-07-13 22:38 UTC, Jasper St. Pierre (not reading bugmail)
none Details | Review
display: clean up focus_window vs expected_focus_window (35.46 KB, patch)
2012-07-13 22:38 UTC, Jasper St. Pierre (not reading bugmail)
none Details | Review
workspace: Respect the not_this_one parameter passed in (947 bytes, patch)
2012-07-13 22:38 UTC, Jasper St. Pierre (not reading bugmail)
none Details | Review
display: clean up focus_window vs expected_focus_window (35.27 KB, patch)
2012-07-23 05:50 UTC, Jasper St. Pierre (not reading bugmail)
none Details | Review
test-focus: test program for focus window management (12.00 KB, patch)
2013-05-18 20:25 UTC, Jasper St. Pierre (not reading bugmail)
reviewed Details | Review
window: Refactor "got focus" code (6.91 KB, patch)
2013-05-18 20:25 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review
window: Merge got_focus/lost_focus to a new function (10.27 KB, patch)
2013-05-18 20:25 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review
window: Clean up the set_focused_internal function (8.70 KB, patch)
2013-05-18 20:25 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review
display: clean up focus_window vs expected_focus_window (28.77 KB, patch)
2013-05-18 20:25 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review
display: Use XI2 constants for mode/detail focus event values (1.45 KB, patch)
2013-05-18 20:26 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review
window: Properly handle focusing override redirect windows (1.52 KB, patch)
2013-05-18 20:26 UTC, Jasper St. Pierre (not reading bugmail)
needs-work Details | Review
test-focus: test program for focus window management (14.22 KB, patch)
2013-05-22 16:46 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review
window: Properly handle focusing override redirect windows (2.06 KB, patch)
2013-05-22 16:47 UTC, Jasper St. Pierre (not reading bugmail)
committed Details | Review

Description Dan Winship 2011-04-13 20:21:24 UTC
MetaDisplay currently has two "focused window" concepts: display->focus_window, which is the last window that it received a FocusIn event for, and display->expected_focus_window, which is the last window it sent an XSetInputFocus for.

Neither of these is entirely right/useful, and there are various bits of code that use one but should be using the other, and various other problems (qv bug 647613 comment 3).

We should get rid of expected_focus_window, and have focus_window be our "best guess" focus window, based on whichever of FocusIn or XSetInputFocus is more relevant at the time, and track X serial numbers to figure out if our focus requests have been denied, in basically the same way MetaStackTracker works (except vastly simpler because we're only keeping track of a single piece of information).
Comment 1 Dan Winship 2011-04-20 17:41:01 UTC
*** Bug 597352 has been marked as a duplicate of this bug. ***
Comment 2 Dan Winship 2011-04-29 21:33:44 UTC
Created attachment 186908 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).
Comment 3 Dan Winship 2011-04-29 21:44:41 UTC
Created attachment 186909 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).
Comment 4 Dan Winship 2011-04-29 21:45:34 UTC
Comment on attachment 186909 [details] [review]
display: clean up focus_window vs expected_focus_window

mrh
Comment 5 Dan Winship 2011-04-29 21:48:37 UTC
Created attachment 186910 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).
Comment 6 Dan Winship 2011-04-29 21:53:21 UTC
OK, this implements basically what's in bug 597352 comment 1. It passes at least basic testing.

Examining all remaining uses of focus_window and window->has_focus suggests that the new semantics should be as good or better than the old semantics in most places.

One slight change: if we try to focus a WM_TAKE_FOCUS window, and then that window closes without taking focus, we will be left with the no_focus_window focused, instead of having focus revert back to the previously-focused window as in current mutter/metacity. This is in fact what expected_focus_window was originally added for (bug 84564, though the bigger problem there was that None was getting focused rather than no_focus_window). So... we could bring expected_focus_window back and use it in just that case, if we wanted...
Comment 7 Dan Winship 2011-05-16 10:35:13 UTC
poke
Comment 8 Owen Taylor 2011-07-15 15:46:14 UTC
Review of attachment 186910 [details] [review]:

Mostly looks good as far as I can tell. Few concerns about particular aspects.

::: src/core/display.c
@@ +1591,3 @@
+handle_window_focus_event (MetaDisplay *display,
+                           MetaWindow  *window,
+                           XEvent      *event)

This function looks like it was written to handle UnmapNotify events, but it doesn't appear to be ever called for UnmapNotify events. (Meaning that server_focus_window can end up pointing to an unmanaged window at least briefly)

@@ +2195,3 @@
                               "Unsetting focus from %s due to mouse entering "
                               "the DESKTOP window\n",
+                              display->focus_window->desc);

You removed (not replaced) the display->expected_focus_window != NULL check, but couldn't display->focus_window be NULL here in some weird corner case?

@@ +5526,3 @@
+  if (window != display->focus_window)
+    {
+      /* We can't just set display->focus_window to @window, since we

I think it might be good to start this off with:
 
 This is the rare "globally active" case.

to clarify the scope of the problem.

@@ +5528,3 @@
+      /* We can't just set display->focus_window to @window, since we
+       * we don't know when (or even if) the window will actually take
+       * focus, so we could end up being wrong for arbitrarily long.

I think the way you've done it here has the right user behavior:

 - As soon as you click away, the window that you clicked away from loses the focus indication and stops getting keystrokes
 - If the app you've clicked on is slow to take care of it's "globally active" responsibilities it doesn't get focus indication until it actually gets focused and starts getting keystrokes.

I don't think dealing with the case where globally active windows handle WM_TAKE_FOCUS  by closing is worth extra code unless it leaves things entirely broken.

@@ +5635,3 @@
+ * Get the window that we currently consider to be focused. This is
+ * the window for which we have most recently either received a
+ * FocusIn or sent an XSetInputFocus.

mixture of Xlib and protocol terminology - "sent a SetInputFocus request"

::: src/core/window.c
@@ +1514,3 @@
     }
 
+  g_assert (window->display->focus_window != window);

Do you also want to assert the same about ->server_focus_window here as well? Or should you rather have clearing logic for server_focus_window in case there are some cases where it woudn't get unset? (Does it get unset if a window is destroyed without being withdrawn first?)

@@ +6413,3 @@
+void
+meta_window_set_focused (MetaWindow *window,
+                         gboolean    focused)

This name seems a little too simple for something that's an implementation detail and doesn't take care of the display-> properties and system state. Yes, it's not in the public header, so it shouldn't confuse users of the library, but I'd back happier with _internal tacked on to the name.
Comment 9 Dan Winship 2011-07-15 22:05:18 UTC
(In reply to comment #8)
> +  g_assert (window->display->focus_window != window);
> 
> Do you also want to assert the same about ->server_focus_window here as well?
> Or should you rather have clearing logic for server_focus_window in case there
> are some cases where it woudn't get unset? (Does it get unset if a window is
> destroyed without being withdrawn first?)

XDestroyWindow(3) says "If the window specified by the w argument is mapped, it is unmapped automatically" which seems to suggest that that can't happen.

However, there was at least one broken case: if a call to XSetInputFocus() fails due to someone else having already called it with a newer timestamp, then once we notice this, we revert focus_window back to server_focus_window. So if server_focus_window had been freed, then, boom. So now, when a window is unmanaged, if it's the server_focus_window, we set that to NULL. This means that in the case above, we'd end up setting focus_window to NULL as well, which is correct, since we have no clue what window is focused at that point, until we receive another FocusIn. (And we know that there has to be a FocusIn coming, or else our XSetInputFocus() wouldn't have failed, so we don't want to call focus_the_no_focus_window() or anything.)
Comment 10 Dan Winship 2011-07-15 23:53:33 UTC
Created attachment 192062 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).
Comment 11 Owen Taylor 2011-07-18 19:46:16 UTC
(In reply to comment #9)
> (In reply to comment #8)
> > +  g_assert (window->display->focus_window != window);
> > 
> > Do you also want to assert the same about ->server_focus_window here as well?
> > Or should you rather have clearing logic for server_focus_window in case there
> > are some cases where it woudn't get unset? (Does it get unset if a window is
> > destroyed without being withdrawn first?)
> 
> XDestroyWindow(3) says "If the window specified by the w argument is mapped, it
> is unmapped automatically" which seems to suggest that that can't happen.
> 
> However, there was at least one broken case: if a call to XSetInputFocus()
> fails due to someone else having already called it with a newer timestamp, then
> once we notice this, we revert focus_window back to server_focus_window. So if
> server_focus_window had been freed, then, boom. So now, when a window is
> unmanaged, if it's the server_focus_window, we set that to NULL. This means
> that in the case above, we'd end up setting focus_window to NULL as well, which
> is correct, since we have no clue what window is focused at that point, until
> we receive another FocusIn. (And we know that there has to be a FocusIn coming,
> or else our XSetInputFocus() wouldn't have failed, so we don't want to call
> focus_the_no_focus_window() or anything.)

I don't think it's true about having to be a FocusIn coming for the failed XSetInputFocus case - we can get a FocusIn *before* we call XSetInputFocus and still try to XSetInputFocus from an older timestamp from a button press or something that will get denied. (And we can't even fix this because FocusIn doesn't contain the focus timestap.)

But more generically, an UnmapNotify for the server focus window is never the final thing we see - even if that causes the focus to go None, we'll then get a FocusIn on the root window with  NotifyDetailNone and that will cause us to focus the no focus window. So, I think we're OK.
Comment 12 Owen Taylor 2011-07-18 19:53:47 UTC
Review of attachment 192062 [details] [review]:

Little uncertain about a couple aspects of how the call to handle_window_focus_event was added for Unmap events

::: src/core/display.c
@@ +2383,3 @@
                               "Received pending unmap, %d now pending\n",
                               window->unmaps_pending);
+                  handle_window_focus_event (display, window, event);

Is it right to add this in the !frame_was_receiver block? Under certain circumstances we focus a window by focusing the frame (shaded windows, windows set not to accept keyboard input); in that case, unmapping the frame implies a focus-out. And in fact, in all case, unmapping the frame implies a focus-out. (if live_hidden_windows is true, we never unmap frames, so it's not normal, but it's at least accurate)

Should we only call handle_widow_focus_event if the window is the server focus window? The code seems to produce confuse debug spew at a minimum if called for unfocused windows on unmap.
Comment 13 Dan Winship 2011-08-03 02:00:58 UTC
(In reply to comment #8)
> I think it might be good to start this off with:
> 
>  This is the rare "globally active" case.

As it happens, this is not actually rare: Gdk sets the Input hint on a window according to the accept-focus property, but includes WM_TAKE_FOCUS in WM_PROTOCOLS unconditionally. And when it receives a WM_TAKE_FOCUS request on a window, it ignores it if accept-focus is FALSE. So, every time you click on an accept-focus=FALSE GtkWindow, you hit this "rare" "edge case". Good thing we decided to make it work. :)

(In reply to comment #12)
> Little uncertain about a couple aspects of how the call to
> handle_window_focus_event was added for Unmap events

> +                  handle_window_focus_event (display, window, event);
> 
> Is it right to add this in the !frame_was_receiver block?

No

> Should we only call handle_widow_focus_event if the window is the server focus
> window? The code seems to produce confuse debug spew at a minimum if called for
> unfocused windows on unmap.

Indeed, although that was a pre-existing bug.
Comment 14 Dan Winship 2011-09-02 00:11:08 UTC
Created attachment 195450 [details] [review]
test-focus: test program for focus window management
Comment 15 Dan Winship 2011-09-02 00:24:22 UTC
Created attachment 195451 [details] [review]
display: clean up focus_window vs expected_focus_window

====

new-and-improved. We now keep server_focus_window as a Window rather
than as a MetaWindow, and so we can track all focus events, not just
those with an associated MetaWindow, and so we don't need to pretend
that UnmapNotify events are FocusOut events.
Comment 16 Jasper St. Pierre (not reading bugmail) 2012-07-13 22:38:38 UTC
Created attachment 218766 [details] [review]
test-focus: test program for focus window management
Comment 17 Jasper St. Pierre (not reading bugmail) 2012-07-13 22:38:54 UTC
Created attachment 218767 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).



Rebased onto master. I'm picking this up now :)
Comment 18 Jasper St. Pierre (not reading bugmail) 2012-07-13 22:38:59 UTC
Created attachment 218768 [details] [review]
workspace: Respect the not_this_one parameter passed in

This fixes a crash when closing a window with the previous cleanup
Comment 19 Jasper St. Pierre (not reading bugmail) 2012-07-23 05:50:14 UTC
Created attachment 219461 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).



Obsoleting workspace patch because it's been pushed.

Attaching new patch that corrects a bug where a new window will not appear to have focus, even if it does.
Comment 20 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:25:40 UTC
Created attachment 244662 [details] [review]
test-focus: test program for focus window management
Comment 21 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:25:47 UTC
Created attachment 244663 [details] [review]
window: Refactor "got focus" code

Just move this out to a separate function.
Comment 22 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:25:50 UTC
Created attachment 244664 [details] [review]
window: Merge got_focus/lost_focus to a new function

Make it a static function for now, but this will be a private
function soon, replacing meta_window_lost_focus. This should
contain no functional changes, only cosmetic indentation changes,
so best viewed with ignorews=1 or -w or -b, you know the drill.
Comment 23 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:25:53 UTC
Created attachment 244665 [details] [review]
window: Clean up the set_focused_internal function

Move things out of an indentation layer, and reshuffle
things around.
Comment 24 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:25:57 UTC
Created attachment 244666 [details] [review]
display: clean up focus_window vs expected_focus_window

Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).
Comment 25 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:26:00 UTC
Created attachment 244667 [details] [review]
display: Use XI2 constants for mode/detail focus event values

This makes no functional difference, except conceptual clarity.
Comment 26 Jasper St. Pierre (not reading bugmail) 2013-05-18 20:26:03 UTC
Created attachment 244668 [details] [review]
window: Properly handle focusing override redirect windows

If an app pops up an OR window and sets input focus to it, like
Steam does, we'll think the focus window is null, causing us to
think the app is not focused.

OR windows should not be special if they get input focus, where
the input focus would be set to NULL. Instead, the window should
be marked as focused.
Comment 27 Owen Taylor 2013-05-21 22:55:34 UTC
Review of attachment 244666 [details] [review]:

Mostly looks good, few comments and comments about comments, and one thing about the code (the change to meta_window_unmanage()) where it seems like we might get into trouble in some edge cases.

::: src/core/display.c
@@ +1874,3 @@
+      /* Make sure that signals handlers invoked by
+       * meta_window_set_focused_internal() don't see
+       * display->focus_window->has_focus == FALSE

Is the "don't" here stray, meaning that in the handlers, we want  window->has_focus iff. display->focus_window == window to hold? (Text would still need some improvement other than remove "don't")

And then again, the main signal handler is the property notification which is in this function? Overall, I'm just confused :-)

@@ +1980,3 @@
+    {
+      display->server_focus_window = event->event;
+      display->server_focus_serial = serial;

Just a note from our conversation about short-circuiting, since we don't exclude NotifyAncestor, we *can* certainly get a FocusIn on a window which already has focus. In which case, because we do have the short-circuiting in set_focus_window, we'll update the display->focus_serial, and do nothing more, which is fine.

@@ +1983,3 @@
+
+      if (window && window->override_redirect)
+        focus_window = NULL;

I'm confused why I don't see this removed in the later O-R patch

@@ +2054,3 @@
   display->monitor_cache_invalidated = TRUE;
+
+  if (event->xany.serial > display->focus_serial &&

Hmm, the > here is a little unexpected - events with a specified serial refer to only the request we made and *later* events. So we'll have to make another request before we found out that the focus failed, and simply seeing incoming events won't do that. But thinking about it some more, I think that's fine, since using >= would be tricky - we have to wait for *all* focus events generated  by our SetInputFocus to be seen before a >= serial indicates failure - and the complexity of 

 event->xany.serial > display->focus_serial ||
 (<event is not a core or XI focus event> && event->xany.serial >= display->focus_serial)

seems unwarranted. So leave as is.

@@ -2377,3 @@
-
-              meta_topic (META_DEBUG_FOCUS,
-                          "Focus %s event received on root window 0x%lx "

This log was lost, but I guess that's fine, since we do log when we actually do something for root window focus events. If someone is debugging a problem with the code paths, they'll probably have to add it back...

::: src/core/window.c
@@ -1796,3 @@
-  if (window->display->focus_window == window)
-    {
-      window->display->focus_window = NULL;

I'm confused by this removal - we can meta_window_unmanage() a window before the X window is destroyed in a few cases (like having the parent dialog of an attached transient window destroyed), in which I *think* we'll free the MetaWindow and leave window->display->focus_window pointing to junk.
Comment 28 Owen Taylor 2013-05-21 23:13:12 UTC
Review of attachment 244662 [details] [review]:

Looking at this test case, I have two thoughts:

 - This is pretty dependent on the details of GTK+ (not much we can do about that)
 - How would we actually know if Mutter got confused? The main thing that you'll see is the "backdrop" theme state, but that comes from Mutter's idea of what window is focused. Maybe we should pack some widget into all the windows (including the toplevel) that is tied to notify::active on the toplevel and shows "FOCUSED" or "NOT FOCUSED" ?

::: src/wm-tester/test-focus.c
@@ +35,3 @@
+      if (protocols[i] == wm_take_focus)
+	{
+	  protocols[i] = protocols[--n_protocols];

Just test code, but this still deserves to be called out as pointlessly tricky code. The compression of:

 protocols[i] = protocols[n_protocols - 1];
 n_protocols--;

To this is in no way actually meaningful, it's just trying to save writing some characters.

@@ +48,3 @@
+clear_input_hint (GtkWidget *window)
+{
+  GdkWindow *gdkwindow = gtk_widget_get_window (window);

I think this is different from disable_take_focus() and doens't realize because it has to be called after show() or GTK+ will overwrite it? If so it would be helpful to have a comment.

@@ +56,3 @@
+  XSetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
+	       GDK_WINDOW_XID (gdkwindow),
+	       &wm_hints);

Is it intentional that you are wiping out the rest of WMHints? The flags field doesn't mean "change only this" it means "only this is set"

@@ +157,3 @@
+
+static guint32
+get_current_time_roundtrip (void)

Why not use gdk_x11_get_server_time() ?
Comment 29 Owen Taylor 2013-05-21 23:13:50 UTC
Review of attachment 244663 [details] [review]:

Going to assume this is OK
Comment 30 Owen Taylor 2013-05-21 23:14:30 UTC
Review of attachment 244664 [details] [review]:

Going to assume this is OK
Comment 31 Owen Taylor 2013-05-21 23:15:18 UTC
Review of attachment 244665 [details] [review]:

This removes the short-circuits which is wrong, but doesn't matter a lot, since they *should* be removed in a subsequent patch. Otherwise seems OK.
Comment 32 Owen Taylor 2013-05-21 23:16:19 UTC
Review of attachment 244667 [details] [review]:

Looks good
Comment 33 Owen Taylor 2013-05-21 23:18:38 UTC
Review of attachment 244668 [details] [review]:

As mentioned in a earlier review, it looks like this patch doesn't actually change things to include O-R windows in tracking focus, it just makes the code in window.c handle that. Can we also extend the test case to test this, so we don't have to test with Steam or whatever? The blinking of the gnome-shell App menu is probably stil visible even for a no-.desktop-file test program.
Comment 34 Dan Winship 2013-05-22 12:16:16 UTC
(Answering the questions about the test case since I wrote that code.)

(In reply to comment #28)
> +clear_input_hint (GtkWidget *window)
> +{
> +  GdkWindow *gdkwindow = gtk_widget_get_window (window);
> 
> I think this is different from disable_take_focus() and doens't realize because
> it has to be called after show() or GTK+ will overwrite it? If so it would be
> helpful to have a comment.

I think that's correct, yes.

> +  XSetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)),
> +           GDK_WINDOW_XID (gdkwindow),
> +           &wm_hints);
> 
> Is it intentional that you are wiping out the rest of WMHints?

No, but it doesn't affect the behavior of the tests either way.

> +static guint32
> +get_current_time_roundtrip (void)
> 
> Why not use gdk_x11_get_server_time() ?

I didn't know that existed and so just copied the code from mutter.
Comment 35 Jasper St. Pierre (not reading bugmail) 2013-05-22 16:36:10 UTC
Attachment 244663 [details] pushed as f6dd081 - window: Refactor "got focus" code
Attachment 244664 [details] pushed as 696d9d2 - window: Merge got_focus/lost_focus to a new function
Attachment 244665 [details] pushed as e430e05 - window: Clean up the set_focused_internal function


Gonna push these preliminary cleanups for now, with short-circuit
guards re-added.
Comment 36 Jasper St. Pierre (not reading bugmail) 2013-05-22 16:46:31 UTC
(In reply to comment #27)
> Review of attachment 244666 [details] [review]:
> 
> Mostly looks good, few comments and comments about comments, and one thing
> about the code (the change to meta_window_unmanage()) where it seems like we
> might get into trouble in some edge cases.
> 
> ::: src/core/display.c
> @@ +1874,3 @@
> +      /* Make sure that signals handlers invoked by
> +       * meta_window_set_focused_internal() don't see
> +       * display->focus_window->has_focus == FALSE
> 
> Is the "don't" here stray, meaning that in the handlers, we want 
> window->has_focus iff. display->focus_window == window to hold? (Text would
> still need some improvement other than remove "don't")
> 
> And then again, the main signal handler is the property notification which is
> in this function? Overall, I'm just confused :-)

It reads well to me. From a preliminary reading, the only signal handler that can be invoked by meta_window_set_focused_internal(FALSE); is notify::appears-focused, but it's possible that we want more to be.

What it's saying is that the signal handler may want to check display->focus_window, which without the NULL set, will get set to the window, so we'll have this weird state of display->focus_window->has_focus being FALSE. Setting focus_window to NULL before calling the signal handler makes it so that those signal handlers believe that there is no focused window, which is correct at that point.

> @@ +1980,3 @@
> +    {
> +      display->server_focus_window = event->event;
> +      display->server_focus_serial = serial;
> 
> Just a note from our conversation about short-circuiting, since we don't
> exclude NotifyAncestor, we *can* certainly get a FocusIn on a window which
> already has focus. In which case, because we do have the short-circuiting in
> set_focus_window, we'll update the display->focus_serial, and do nothing more,
> which is fine.

Ah, OK, that makes sense.

> @@ +1983,3 @@
> +
> +      if (window && window->override_redirect)
> +        focus_window = NULL;
> 
> I'm confused why I don't see this removed in the later O-R patch

I do remember removing it, and I tested the patch a lot, so I'm going to take the convenient "blame-it-on-rebase" excuse.

> ::: src/core/window.c
> @@ -1796,3 @@
> -  if (window->display->focus_window == window)
> -    {
> -      window->display->focus_window = NULL;
> 
> I'm confused by this removal - we can meta_window_unmanage() a window before
> the X window is destroyed in a few cases (like having the parent dialog of an
> attached transient window destroyed), in which I *think* we'll free the
> MetaWindow and leave window->display->focus_window pointing to junk.

Immediately before that is:

+  g_assert (window->display->focus_window != window);

Which implements the same guarantee, but more strictly. The reason being is that directly above *that* is:

  if (window->has_focus)
    {
      meta_topic (META_DEBUG_FOCUS,
                  "Focusing default window since we're unmanaging %s\n",
                  window->desc);
      meta_workspace_focus_default_window (window->screen->active_workspace,
                                           window,
                                           timestamp);
    }
  else
    {
      meta_topic (META_DEBUG_FOCUS,
                  "Unmanaging window %s which doesn't currently have focus\n",
                  window->desc);
    }

Which I'm quite sure will select the right window, or none, in that case.
Comment 37 Jasper St. Pierre (not reading bugmail) 2013-05-22 16:46:56 UTC
Created attachment 245067 [details] [review]
test-focus: test program for focus window management
Comment 38 Jasper St. Pierre (not reading bugmail) 2013-05-22 16:47:42 UTC
Created attachment 245068 [details] [review]
window: Properly handle focusing override redirect windows

If an app pops up an OR window and sets input focus to it, like
Steam does, we'll think the focus window is null, causing us to
think the app is not focused.

OR windows should not be special if they get input focus, where
the input focus would be set to NULL. Instead, the window should
be marked as focused.



Properly remove the obviously broken override-redirect case.
Comment 39 Owen Taylor 2013-05-22 17:21:47 UTC
Review of attachment 245067 [details] [review]:

Looks good
Comment 40 Owen Taylor 2013-05-22 17:22:20 UTC
Review of attachment 245068 [details] [review]:

OK
Comment 41 Owen Taylor 2013-05-22 17:32:55 UTC
(In reply to comment #36)
> (In reply to comment #27)
> > Review of attachment 244666 [details] [review] [details]:
> > 
> > Mostly looks good, few comments and comments about comments, and one thing
> > about the code (the change to meta_window_unmanage()) where it seems like we
> > might get into trouble in some edge cases.
> > 
> > ::: src/core/display.c
> > @@ +1874,3 @@
> > +      /* Make sure that signals handlers invoked by
> > +       * meta_window_set_focused_internal() don't see
> > +       * display->focus_window->has_focus == FALSE
> > 
> > Is the "don't" here stray, meaning that in the handlers, we want 
> > window->has_focus iff. display->focus_window == window to hold? (Text would
> > still need some improvement other than remove "don't")
> > 
> > And then again, the main signal handler is the property notification which is
> > in this function? Overall, I'm just confused :-)
> 
> It reads well to me. From a preliminary reading, the only signal handler that
> can be invoked by meta_window_set_focused_internal(FALSE); is
> notify::appears-focused, but it's possible that we want more to be.
> 
> What it's saying is that the signal handler may want to check
> display->focus_window, which without the NULL set, will get set to the window,
> so we'll have this weird state of display->focus_window->has_focus being FALSE.
> Setting focus_window to NULL before calling the signal handler makes it so that
> those signal handlers believe that there is no focused window, which is correct
> at that point.

Ah, OK, I missread it as 'window->has_focus == FALSE' rather than display->focus_window->has_focus == FALSE. Good as is.

> > ::: src/core/window.c
> > @@ -1796,3 @@
> > -  if (window->display->focus_window == window)
> > -    {
> > -      window->display->focus_window = NULL;
> > 
> > I'm confused by this removal - we can meta_window_unmanage() a window before
> > the X window is destroyed in a few cases (like having the parent dialog of an
> > attached transient window destroyed), in which I *think* we'll free the
> > MetaWindow and leave window->display->focus_window pointing to junk.
> 
> Immediately before that is:
> 
> +  g_assert (window->display->focus_window != window);
> 
> Which implements the same guarantee, but more strictly. The reason being is
> that directly above *that* is:
> 
>   if (window->has_focus)
>     {
>       meta_topic (META_DEBUG_FOCUS,
>                   "Focusing default window since we're unmanaging %s\n",
>                   window->desc);
>       meta_workspace_focus_default_window (window->screen->active_workspace,
>                                            window,
>                                            timestamp);
>     }
>   else
>     {
>       meta_topic (META_DEBUG_FOCUS,
>                   "Unmanaging window %s which doesn't currently have focus\n",
>                   window->desc);
>     }
> 
> Which I'm quite sure will select the right window, or none, in that case.

OK, make sense.
Comment 42 Jasper St. Pierre (not reading bugmail) 2013-05-22 17:46:27 UTC
Attachment 244666 [details] pushed as 7a4c808 - display: clean up focus_window vs expected_focus_window
Attachment 244667 [details] pushed as d03ffd8 - display: Use XI2 constants for mode/detail focus event values
Attachment 245067 [details] pushed as 4f1d621 - test-focus: test program for focus window management
Attachment 245068 [details] pushed as df8234c - window: Properly handle focusing override redirect windows