[enhancement] Need a mechanism to prevent focus stealing

Bug #1398852 reported by desrt on 2014-12-03
14
This bug affects 2 people
Affects Status Importance Assigned to Milestone
Mir
Triaged
Wishlist
Unassigned
Ubuntu UX
Medium
Matthew Paul Thomas
mir (Ubuntu)
Wishlist
Unassigned

Bug Description

We need a mechanism to prevent focus stealing.

This could take the form of a timestamp/cookie in the style of X, or something else.

kevin gunn (kgunn72) on 2014-12-03
Changed in mir:
importance: Undecided → High
assignee: nobody → Alberto Aguirre (albaguirre)
Daniel van Vugt (vanvugt) wrote :

We have designs documented specifically to solve this problem. I'm not sure the current design reflects the X-approach suggested here.

Changed in mir:
importance: High → Wishlist
tags: added: enhancement
Changed in mir:
status: New → Incomplete
desrt (desrt) wrote :

Can you tell me where I can see the spec? Last time we talked about this (about a year ago) I recall mpt and I convincing people that the design at the time failed to deal with certain important usecases. Was it changed since then?

Daniel van Vugt (vanvugt) wrote :

It's certainly possible your suggestion is superior. I don't know. Needs investigation...
https://docs.google.com/a/canonical.com/document/d/1IxzLIpe8fJBaffOUrwI6tb-lb-Iq8Kd4ivSAzuxiYwY/edit#

Ideally if the design is wrong then we'd get that fixed instead of ignoring it and doing something else.

desrt (desrt) wrote :
Download full text (6.9 KiB)

I've read the document and was able to find the proposed approach (around page 46), quoted here for convenience:

"""
For example, suppose that you click on a PDF attachment in a mail window. The mail program saves the attachment in a temporary directory, then asks Ubuntu to open it. Ubuntu launches the default PDF viewer, which opens a window to display the document. Ideally, if you did nothing else after the click, the PDF viewer should be focused. But if you did something else, in the same window or another window, the PDF viewer should not be focused.

Unfortunately, in this example, there is no part of the system that “knows” that the reason the PDF viewer window opened was your clicking in the mail window. The mail program doesn’t know that the attachment opened in a PDF viewer. The PDF viewer doesn’t know that the file it’s opening is a mail attachment. And if neither of them know, there’s no way for the window manager to know whether to focus the window. Previously window managers have tackled this using heuristics, guessing based on how recently you did anything in any window. But the faster a program launches, the worse this works.

To minimise this problem, Mir introduces the idea of a focus grant. This is a flag that can be set by the focused application, to say “the next application that requests focus for a surface should be given it”. In the previous example, when you click on the attachment in the mail window, the mail application would set the focus grant: it knows that the attachment is likely to open in another application’s window, even if it doesn’t know which application. So when the PDF viewer opens and implicitly requests focus, Mir focuses it.

But if you interact with any surface while the focus grant is set, Mir should revoke the grant. For example, if you click on the attachment, but then do anything else before the PDF viewer opens — navigate to the next message, click Reply to open a composition window, or launch a different application — the PDF viewer should not be focused when it opens.

This means that there is only ever one focus grant set at a time. If any other surface is focused, it is either because it was granted focus, or because you focused it yourself.
It is still possible for the wrong application to sneak in and take focus before the right one does. But this system should make focus stealing much less common.
"""

The proposed solution is workable, but as noted in the document itself, it can sometimes focus the wrong window.

It also suffers from some possible races, depending on how exactly the "grant" is set. Unless the setting of the grant is a synchronous call (with the application waiting for a reply from Mir to confirm that the grant was successfully set) then there is no guarantee that the grant request was received by Mir before the second application pops up its window. It could easily happen in a multi-tasking environment (particularly in the case that the second application was already running) that the request to open a new window is handled before Mir has a chance to process the grant request. We could try to work around that by saying things like "on receiving a request from an app...

Read more...

summary: - need a mechanism to create/map surface with timestamp/cookie
+ Need a mechanism to prevent focus stealing
description: updated
Changed in mir:
status: Incomplete → Triaged

Thanks for your analysis Ryan...seems correct w.r.t. races etc and that we should use the timestamp over the focus grant system in the doc.

Matthew Paul Thomas (mpt) wrote :

I've just come across this bug report, thanks to Christopher's comment in the spec.

1. Why would checking each apps' queue for focus grants, before opening a window, be "exceedingly unreasonable"? Would it be too slow? The focus decision algorithm wasn't yet written at the time of Ryan's review, but at the moment, checking for a focus grant is step 4 of a 6-step process. A decent fraction of focus requests (for example, all tips, and all children of the previously-focused window) would have exited at earlier steps, so would not require the check.

2. I don't know where "Presumably an app is allowed to focus new windows of its own without going through the grant mechanism" comes from. Apart from the case of a child of the currently-focused window (which should always be focused), requests are treated the same regardless of whether they are from the same app or a different app.

3. I understood that the timestamp mechanism worked only in simple cases in some toolkits. I see how it works in gdk_app_launch_context_set_timestamp(), for example, but where do you put the timestamp in Qt's activateWindow() function? <http://doc.qt.io/qt-5/qwidget.html#activateWindow> Or in window.open() as called from XUL? <https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Tutorial/Features_of_a_Window> Or in LibreOffice's Window::ImplGrabFocus()? <http://docs.libreoffice.org/vcl/html/mouse_8cxx_source.html#l00198>

Of course, those toolkits don't do focus grants today either. I'm not particularly attached to the focus grant mechanism; if timestamps can work better, then great! But for timestamps to work, both the trigger app *and* the opening app need to handle them, right? (Even if it's the same app, it needs to do two different things with the timestamp, creating it at the moment of input, and then sending it when requesting focus.) Whereas for the focus grant system to work, only the trigger app would need to handle it. If so, the timestamp system would work less often.

desrt (desrt) wrote :

> 1. Why would checking each apps' queue for focus grants, before opening a window, be "exceedingly unreasonable"?

It's not because it would be a massive amount of work to do this, but rather because of how awkward it would be to do it from the place that it needed to be done. I imagine the Mir server to be structured in a way where incoming requests from a client result in some code being run to handle those requests. You'd now be in a situation where that code would have to go and actively check for pending incoming requests from all other clients (while in the middle of handling a request), potentially selectively dequeuing some things from those other clients (ie: grant requests) from a place other than where they are normally dequeued. It's not just a simple matter of checking the queue -- you'd have to actively interact with the kernel. It's not that this is computationally complex or slow -- it's just that it would be extremely awkward.

> 2. I don't know where "Presumably an app is allowed to focus new windows of its own without going through the grant mechanism" comes from.

Sure. This is a presumption. It wouldn't have to be done this way, but if you didn't do it this way, you could get a worse evil: opening a new window in my web browser would then give a chance for an arbitrary popup from another app to sneak in.

> 3. I understood that the timestamp mechanism worked only in simple cases in some toolkits. I see how it works in gdk_app_launch_context_set_timestamp(), for example, but where do you put the timestamp in Qt's activateWindow() function? [...]

The timestamp is not typically treated explicitly by most toolkits, but rather implicitly. They record the timestamp of the most recent incoming event, internally, and when making requests to the X server, they give the most-recent-timestamp automatically. The APIs that allow you to explicitly specify the timestamp are only required in cases where you are "doing something strange", such as opening a window on behalf of a request from another program (rather than a direct input event to your program). The most common cases of this (application launching, and using the shell to create new windows in existing apps) are even handled automatically via the _TIME field in the startup notification ID.

This might seem a bit "evil", but the truth is that 99% of the time, you get something that 'just works' without the programmer having to think about it. Only when they are doing weird things do they have to be aware that they need to deal explicitly with this stuff. We even give a nicer alternative, in GApplication, to allow the use of GActions for sending arbitrary requests between processes, and if you use this, the timestamp stuff is still handled automatically for you. That's a convenience -- people should always be given the ability to deal with it manually if they want to.

Matthew Paul Thomas (mpt) wrote :

1. I don't buy the "awkward" objection, because dealing with focus grants would be far from the only case where Mir should inspect -- and possibly remove items from -- the request queue. For example, at the moment you start manually resizing a window, Mir should immediately deal with any queued request from the app to close the window, or to change its current, minimum, or maximum size. At the moment you start moving a window, Mir should immediately deal with any queued request from the app to close the window, to resize it (because size constrains how far it can move), or to change its type (because the new type might be unmovable). And at the moment you choose to minimize a window, Mir should immediately deal with any queued request from the app to change its type or parent (because the new combination might be unminimizable).

2. The spec already says that the grant system has the possibility of false positives. But the interloper window might be from the same app -- for example, a popup that your browser's popup blocker failed to catch, or a progress window that your file manager opened because an operation had taken more than a few seconds so far. That's why the focus decision algorithm makes no distinction about what app the new window is from.

3. Oh, just going by last input is clever. I guess pointer movement alone doesn't count as an "incoming event"? If so, the one case I can think of where it might fail is during drags, where an app might know whether a drag was the reason that a window was opening, but its toolkit wouldn't know.

Given all that, if you're still confident that the cookie system is better than focus grants, I'll change the spec accordingly.

desrt (desrt) wrote :

> 1. I don't buy the "awkward" objection, because dealing with focus grants would be far from the only case where Mir should inspect -- and possibly remove items from -- the request queue.

The examples you give involve removing items from the queue of the same app. I'm talking about removing events from _all other_ apps. That's where the awkwardness comes.

> 3. Oh, just going by last input is clever. I guess pointer movement alone doesn't count as an "incoming event"?

Correct. It has to be an active event such as keypress, mouse click, touch, etc.

> If so, the one case I can think of where it might fail is during drags

During the sprint in Brussels we actually came up with a pretty good mechanism for passing off the event timestamps for the drag-and-drop case.

> Given all that, if you're still confident that the cookie system is better than focus grants, I'll change the spec accordingly.

I am. The cookie system allows for no false positives and is not more difficult to implement. It also fits better with how existing toolkits function, which means that overall, it may be substantially easier to implement.

kevin gunn (kgunn72) on 2015-06-09
Changed in mir:
assignee: Alberto Aguirre (albaguirre) → nobody
summary: - Need a mechanism to prevent focus stealing
+ [enhancement] Need a mechanism to prevent focus stealing
Changed in ubuntu-ux:
assignee: nobody → Matthew Paul Thomas (mpt)
status: New → Triaged
importance: Undecided → Medium
John Lea (johnlea) on 2015-07-30
summary: - [enhancement] Need a mechanism to prevent focus stealing
+ [MIR] Need a mechanism to prevent focus stealing

[MIR] means "main inclusion request". I don't think that's what you mean :)
https://bugs.launchpad.net/~ubuntu-mir/+subscribedbugs

summary: - [MIR] Need a mechanism to prevent focus stealing
+ Need a mechanism to prevent focus stealing
summary: - Need a mechanism to prevent focus stealing
+ [enhancement] Need a mechanism to prevent focus stealing
tags: added: focus xmir
Michał Sawicz (saviq) wrote :

Syncing task from Mir.

Changed in mir (Ubuntu):
importance: Undecided → Wishlist
status: New → Triaged
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers