Make focusing newly mapped views optional (strict focus stealing prevention)
Also continuation of #2527 and #2626, creating a (mostly) complete solution for focus stealing prevention.
This adds an option (core/focus_on_map) that determines whether newly mapped (opened) views are focused. The default is true which keeps the current behavior (as I imagine the newly introduced behavior would be too restrictive for most users). When set to false, views must explicitly use the xdg-activation protocol to gain focus, even when first mapped.
This PR is organized into 4 parts:
- commit d3c872bea44e3271c7a8a6f56dd5c43725784c2a is just the same as #2626 (as I don't think there is much sense in merging this only)
- the next two commits (b29982fea5fa72ccb81c2af6a257b257f63c6354 and 95ebff8bec296f531de9b0e815b776605631099d) implement this option
- commit 65a1338efa754b9fd35db6cf2eb52142bbf693a5 adapts xdg-activation to be able to focus newly mapped views with a valid token
- the last two commits (e7af9084a3a295950cf84da33f1484ac9e3a17d3 and 228478f15db7d97aa199b457f678ae80d6454607) generate and pass valid tokens to commands that are run by Wayfire, so that when starting GUI apps, they can still receive focus
This is currently a draft, since some functionality is still missing:
- The main issue is that currently, newly mapped views are still stacked on top, but do not receive keyboard focus; ideally, they would be stacked below the currently active view, but I haven't yet figured out how to do this.
- From a UI perspective, I actually don't like putting this option in
core. it would be nice to collect all focus stealing / activation related options in one place (i.e. with xdg-activation); however, since this option needs to be accessed from core, I don't like the idea of putting it under a plugin. Maybe an alternative would be introducing one more signal, but that also sounds overkill. - Also, I'm unsure if this handles XWayland views correctly; I'm wondering if I should prevent focus here as well; in any case, I haven't tested with XWayland yet, but will do so next.
- I'll need to do more testing, but potentially I want to separately track the active view for normal and self-generated tokens (my main concern is that some commands will not result in a new view opening, so they should not invalidate normal tokens, but this is of course and edge case).
I think it's easier with python and a few lines:
from wayfire import WayfireSocket
sock = WayfireSocket()
last_focused_view_id = sock.get_focused_view()["id"]
sock.watch(["view-mapped", "view-focused"])
def should_continue(msg):
if "view" in msg:
if msg["view"] is not None:
if msg["view"]["role"] == "toplevel":
return msg["view"]
return None
while True:
msg = sock.read_next_event()
view = should_continue(msg)
if view is None:
continue
if msg["event"] == "view-mapped":
sock.set_focus(last_focused_view_id)
if msg["event"] == "view-focused":
last_focused_view_id = sock.get_focused_view()["id"]
I think it's easier with python and a few lines:
from wayfire import WayfireSocket sock = WayfireSocket() last_focused_view_id = sock.get_focused_view()["id"] sock.watch(["view-mapped", "view-focused"]) while True: msg = sock.read_next_event() if msg["event"] == "view-mapped": sock.set_focus(last_focused_view_id) if msg["event"] == "view-focused": last_focused_view_id = sock.get_focused_view()["id"]
While this is a good workaround in practice, we still switch the focus and then back. Depending on the app, this might not be what we want.