GNOME Bugzilla – Bug 162980
focus stealing prevention and window frames (in particular, deletes)
Last modified: 2011-02-04 16:17:22 UTC
Sebastien pointed this one out to me in IRC: 1. Open gedit 2. Type some stuff (but don't save your changes) 3. Click on the close button on the upper right of the window frame Gedit will open a dialog asking whether to save the document; this window will not be focused. Reasons for this bug, in excruciating detail: 1. Whenever the user presses keys or clicks in client area, gtk+ updates _NET_WM_USER_TIME of window. Let's say the timestamp of the last keypress is x, so that gedit's _NET_WM_USER_TIME is x. 2. Whenever the user clicks on a window (client area or the frame), Metacity updates it's user_time variable for the window (reasons: legacy app support and because clients don't receive events that occur on the frame, yet they really should count as user interaction). 3. Metacity doesn't update the _NET_WM_USER_TIME of gedit (reason: the idea of _NET_WM_USER_TIME was so that the window manager would know all the correct timestamps, and Metacity already has the timestamp). 4. From (2) and (3), metacity's user_time variable for gedit is y, where y > x. 5. Gedit opens a new window when it receives the delete_event. Gtk+ default is to set the new window's _NET_WM_USER_TIME to the most recent user interaction of the user with the app. As far as gtk+ knows, that time is x. 6. Metacity sees the new window mapped, but with _NET_WM_USER_TIME x, which is less than the user_time of the focus window (gedit), i.e. is less than y. Proposed solution: gtk+ already gets a WM_DELETE_WINDOW from the window manager that includes a timestamp, so use it. (Question: Can an app get a WM_DELETE_WINDOW that doesn't come from user interaction? I don't see how, but that might be a problem with this solution.) Alternate solutions that seem to have problems: 1) Don't have metacity update it's internal user_time for any clicks if the app is known to support _NET_WM_USER_TIME. This means that clicking on a frame (e.g. to raise a window) doesn't count as user interaction and so other windows can take focus when the user doesn't expect it. 2) Have metacity update _NET_WM_USER_TIME x property of the window when it receives frame clicks. Make gtk+ monitor these so that it's variables are updated appropriately. This would introduce a race condition--which message gets to the app first; the _NET_WM_USER_TIME update, or the WM_DELETE_WINDOW? I'll attach a one-line patch in a minute that uses the proposed solution and which I've verified fixes the observed bug.
Created attachment 35458 [details] [review] Use timestamp from WM_DELETE_WINDOW to update user interaction time
Sounds like a correct analysis and a reasonable fix. It doesn't seem to solve the fundamental problem that in the sequence 1) edit text in gedit 2) fiddle with wm frame 3) gedit opens a dialog without further user interaction the dialog will not get focus. Correct ?
Um, I need to split your summary into three cases and discuss each individually, because the explanation depends a lot on what you mean by "gedit opens a dialog without further user interaction". Case 1: gedit opens a dialog not spawned from any user interaction This is by definition a "surprise popup". Applications are supposed to mark any such dialogs with a do-not-focus-on-map request (because focusing surprise dialogs can result in users accidentally dismissing the window or accidentally choosing a destructive action without even getting a chance to read and realize what action they chose). This case presents no problems Case 2: gedit opens a dialog due to a user interaction with the frame I can't think of any user interactions with the frame that will cause an application to open a window, except, of course, for the close button on the frame. Can you? As far as I can tell, my patch fixes the only problem this case presents. Case 3: gedit opens a dialog from a previous interaction with the app This is much more involved to explain. However, it is a lot like the following focus case we have to worry about for browsers: a) Open a URL in a web browser for a site which happens to be down b) wait for the browser to respond (by loading the page or showing an error) OR a) same as above b) open another URL in a different tab while waiting In the first of these two cases, the error dialog that says "the connection couldn't be made" should be focused; in the latter, it shouldn't be (the user has interacted with the application--and may still be interacting with it--after the event that caused the window to be opened; focusing it could also result in accidental dismissal) Now, to make it easier to catch the similarity, let me go back to the gedit case but introduce a slight change: 1) The user clicks on Edit->Preferences in order to get a preferences dialog to appear. 2) Pretend this preferences dialog makes Open Office startup look fast, and doesn't appear for a long time. 3) Pretend further that the user has somehow become accustomed to this, and just clicks in his document to continue typing while waiting. 4) the preference dialog finally appears The correct behavior in this case is to not focus the preference dialog because the user has continued on with their work. (Note that they might accidentally dismiss the dialog or give an erroneous action if you were to focus it.) Now, clicking on the frame when the application is trying to open a window from a previous user interaction is just like the above case, meaning that the new window should not be focused. This means that this case presents no problems either. In summary, I think we only have a problem if a frame interaction can cause the app to open a window. The only case I'm aware of that this can happen is when trying to close the window via clicking on the frame's close button. My patch above handles this case. Now, it's possible in that long list of cases to miss something. Can anyone see anything that I missed?
No, I think you are right, fiddling with the frame shouldn't trigger app popups, exect for the one case we are discussing here (WM_DELETE) - unless someone want to code a "Really accept focus" dialog in response to WM_TAKE_FOCUS... I was mainly thinking about dialogs which could come up after a timeout, but I think you are correct that not focusing the dialog in this case is likely more correct wm behaviour, and the user can easily correct it by clicking on the dialog frame in case he really needs the focus in the dialog.
2005-01-05 Matthias Clasen <mclasen@redhat.com> * gdk/x11/gdkevents-x11.c (gdk_wm_protocols_filter): Update the user time when receiving a WM_DELETE message. (#162980, Elijah Newren)