Trying to make focus change on workspace switch less random

Hello there,

It’s my first post around here, and I’m not sure if it’s in the appropriate category or properly tagged.

The situation
I’ve been using Gnome for many years now and always had the feeling something wasn’t working properly with window focusing when I switched workspaces on a dual screen setup.

Let’s consider the following situation: I have a primary screen (A) and a secondary screen (B). Changing workspace only affects screen A in the current setup. Let’s say I’m on workspace 1. I have Firefox focused on screen B. I click on VLC that’s on screen A to focus it. I then switch workspace 2, that contains Terminator on screen A.

Intuitively I always expect Terminator to get focused, but it doesn’t. Focus goes to Firefox. Probably because it’s the latest program currently visible that had focus.

This is weird because I would have never changed workspaces if my intention was to focus a program that was already on screen.

What I want to do about it

I believe this is something that can be fixed with an extension. I have no prior knowledge on how to implement an extension. I have read through the GJS guide on extensions. It did show me how to create an empty extension and I’ve learned from it that I could access objects that interact with workspaces and windows from an extension. I couldn’t find any guidance on how to write the actual code that goes in the extension or on how to use importable “gi” modules.

What I want to implement is fairly simple in natural language:

  • detect that a workspace switch is in progress ;
  • on the main display find the latest window that had focus ;
  • focus that window.

Right now I’m reading workspace.js from the gnome-shell repo just so I can find examples on what can be done with workspaces and windows, as I have no idea where I could find documentation on how these things interact with each other. I’ve also encountered the meta 9 API documentation which I’m reading at the same time as to understand the types I’m seing in workspace.js.

For context: I’m on Gnome 42.1, running Wayland on ArchLinux.

Why am I here?

I would love it if someone could offer some guidance as to where I should focus my efforts to get this extension going.

For instance I still have no idea on how to listen to a workspace switching event, or how I can interact with the windows on that workspace to focus the latest active one.

Big thanks for reading this, and if you have time to help me.

1 Like

Focus goes to Firefox. Probably because it’s the latest program currently visible that had focus.

Correct.

This is weird because I would have never changed workspaces if my intention was to focus a program that was already on screen.

Agreed, this sounds like a safe assumption.

For instance I still have no idea on how to listen to a workspace switching event, or how I can interact with the windows on that workspace to focus the latest active one.

The following should work as intended:

const { Meta } = imports.gi;

class Extension {
    _onActiveWorkspaceChanged() {
        if (!Meta.prefs_get_workspaces_only_on_primary())
            return;
            
        const activeWs = global.workspace_manager.get_active_workspace();
        const [newFocus] = activeWs.list_windows()
            .filter(w => w.is_on_primary_monitor());
        newFocus?.activate(global.get_current_time());
    }
    
    enable() {
        global.workspace_manager.connectObject(
            'active-workspace-changed', () => this._onActiveWorkspaceChanged(),
            this);
    }

    disable() {
        global.workspace_manager.disconnectObject(this);
    }
}

function init() {
    return new Extension();
}

However precisely because the suggested behavior is reasonable, it should be implemented in mutter itself.

2 Likes

Glad we can agree on the fact that its reasonable behaviour! I’ll look into making an issue on the mutter project.

Thanks for the code! I’ll be trying your code once I have access to the affected computer, and I’ll make sure to update this thread with my findings.

Just to make sure it was a Gnome on Wayland issue, I’ve tried on Xorg and the focus change on workspace switch works as intended.

After trying a few use cases, I realised that the wrong windows from the primary screen would get focused, instead of the last used windows from the primary screen.

Fixed it with the following code:

        let windows = activeWs.list_windows()
              .filter(w => w.is_on_primary_monitor());
        const newFocus = windows.reduce((prev, current) => {
            if (current.get_user_time() > prev.get_user_time()) {
                return current;
            }
            return prev;
        });
        newFocus?.activate(global.get_current_time());

Don’t know if windows are unsorted, or if they were sorted in another order, or even if my code works out of sheer luck. I will try it for a few days.