Skip to content

Whim loses keyboard focus (e.g. while switching workspaces) #1127

@CaMeLCaseError

Description

@CaMeLCaseError

Is there an existing issue for this?

  • I have searched the existing issues

Behavior

Current Behavior

When switching workspaces whim can lose control over keyboard presses. (possibly also on other actions). So keyboard presses go to an active window instead of whim hotkey actions.

Expected Behavior

use a keybpard combination to switch to a desktop. The window does not receive keyboard input while whim is moving or changing the windows.

C# Config

#nullable enable
#r "C:\Program Files\Whim\whim.dll"
#r "C:\Program Files\Whim\plugins\Whim.Bar\Whim.Bar.dll"
#r "C:\Program Files\Whim\plugins\Whim.CommandPalette\Whim.CommandPalette.dll"
#r "C:\Program Files\Whim\plugins\Whim.FloatingWindow\Whim.FloatingWindow.dll"
#r "C:\Program Files\Whim\plugins\Whim.FocusIndicator\Whim.FocusIndicator.dll"
#r "C:\Program Files\Whim\plugins\Whim.Gaps\Whim.Gaps.dll"
#r "C:\Program Files\Whim\plugins\Whim.LayoutPreview\Whim.LayoutPreview.dll"
#r "C:\Program Files\Whim\plugins\Whim.SliceLayout\Whim.SliceLayout.dll"
#r "C:\Program Files\Whim\plugins\Whim.TreeLayout\Whim.TreeLayout.dll"
#r "C:\Program Files\Whim\plugins\Whim.TreeLayout.Bar\Whim.TreeLayout.Bar.dll"
#r "C:\Program Files\Whim\plugins\Whim.TreeLayout.CommandPalette\Whim.TreeLayout.CommandPalette.dll"
#r "C:\Program Files\Whim\plugins\Whim.Updater\Whim.Updater.dll"
#r "C:\Program Files\Whim\plugins\Whim.Yaml\Whim.Yaml.dll"

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.UI;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;
using Whim;
using Whim.Bar;
using Whim.CommandPalette;
using Whim.FloatingWindow;
using Whim.FocusIndicator;
using Whim.Gaps;
using Whim.LayoutPreview;
using Whim.SliceLayout;
using Whim.TreeLayout;
using Whim.TreeLayout.Bar;
using Whim.TreeLayout.CommandPalette;
using Whim.Updater;
using Whim.Yaml;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using System.Linq;
using System.Diagnostics;

/// <summary>
/// This is what's called when Whim is loaded.
/// </summary>
/// <param name="context"></param>
// Function to create a command and keybind for moving a window to a specific workspace
/// <summary>
/// This is what's called when Whim is loaded.
/// </summary>
/// <param name="context"></param>
void DoConfig(IContext context)
{
    // KeyModifiers mod1 = KeyModifiers.RWin | KeyModifiers.LShift;
    // KeyModifiers mod2 = KeyModifiers.LAlt | KeyModifiers.RWin | KeyModifiers.LShift;
    ///KeyModifiers mod2 = [VIRTUAL_KEY.VK_OEM_PA1, KeyModifiers.LAlt];
    var mod1 = new VIRTUAL_KEY[] { VIRTUAL_KEY.VK_OEM_PA1 }; // VK_OEM_PA1 is tze Muhenkan Key
    var mod2 = new VIRTUAL_KEY[] { VIRTUAL_KEY.VK_OEM_PA1, VIRTUAL_KEY.VK_LMENU }; // VK_LMENU is the left Alt Key
	
    context.Logger.Config = new LoggerConfig();

	// YAML config. It's best to load this first so that you can use it in your C# config.
	YamlLoader.Load(context);

    context.KeybindManager.SetKeybind("whim.core.activate_workspace_1", new Keybind(mod1, VIRTUAL_KEY.VK_1));
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_2", new Keybind(mod1, VIRTUAL_KEY.VK_2));
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_3", new Keybind(mod1, VIRTUAL_KEY.VK_3));
    
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_4", new Keybind(mod1, VIRTUAL_KEY.VK_Q));
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_5", new Keybind(mod1, VIRTUAL_KEY.VK_W));
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_6", new Keybind(mod1, VIRTUAL_KEY.VK_E));
    
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_7", new Keybind(mod1, VIRTUAL_KEY.VK_A));
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_8", new Keybind(mod1, VIRTUAL_KEY.VK_S));
    context.KeybindManager.SetKeybind("whim.core.activate_workspace_9", new Keybind(mod1, VIRTUAL_KEY.VK_D));
    

// Custom commands and keybinds for moving windows to workspaces (Win + Alt + Key)
    context.CommandManager.Add(
        identifier: "move_window_to_workspace_1",
        title: "Move window to workspace 1",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (0 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[0]);
                    context.Butler.Activate(workspaces[0]);
                }
                else
                {
                    Logger.Error($"Workspace index 1 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_1", new Keybind(mod2, VIRTUAL_KEY.VK_1));
    
    context.CommandManager.Add(
        identifier: "move_window_to_workspace_2",
        title: "Move window to workspace 2",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (1 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[1]);
                    context.Butler.Activate(workspaces[1]);
                }
                else
                {
                    Logger.Error($"Workspace index 2 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_2", new Keybind(mod2, VIRTUAL_KEY.VK_2));
    
    context.CommandManager.Add(
        identifier: "move_window_to_workspace_3",
        title: "Move window to workspace 3",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (2 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[2]);
                    context.Butler.Activate(workspaces[2]);
                }
                else
                {
                    Logger.Error($"Workspace index 3 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_3", new Keybind(mod2, VIRTUAL_KEY.VK_3));
    
    context.CommandManager.Add(
        identifier: "move_window_to_workspace_4",
        title: "Move window to workspace 4",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (3 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[3]);
                    context.Butler.Activate(workspaces[3]);
                }
                else
                {
                    Logger.Error($"Workspace index 4 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_4", new Keybind(mod2, VIRTUAL_KEY.VK_Q));
    
    context.CommandManager.Add(
        identifier: "move_window_to_workspace_5",
        title: "Move window to workspace 5",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (4 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[4]);
                    context.Butler.Activate(workspaces[4]);
                }
                else
                {
                    Logger.Error($"Workspace index 5 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_5", new Keybind(mod2, VIRTUAL_KEY.VK_W));

    context.CommandManager.Add(
        identifier: "move_window_to_workspace_6",
        title: "Move window to workspace 6",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (5 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[5]);
                    context.Butler.Activate(workspaces[5]);
                }
                else
                {
                    Logger.Error($"Workspace index 6 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_6", new Keybind(mod2, VIRTUAL_KEY.VK_E));

    context.CommandManager.Add(
        identifier: "move_window_to_workspace_7",
        title: "Move window to workspace 7",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (6 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[6]);
                    context.Butler.Activate(workspaces[6]);
                }
                else
                {
                    Logger.Error($"Workspace index 7 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_7", new Keybind(mod2, VIRTUAL_KEY.VK_A));

    context.CommandManager.Add(
        identifier: "move_window_to_workspace_8",
        title: "Move window to workspace 8",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (7 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[7]);
                    context.Butler.Activate(workspaces[7]);
                }
                else
                {
                    Logger.Error($"Workspace index 8 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_8", new Keybind(mod2, VIRTUAL_KEY.VK_S));

    context.CommandManager.Add(
        identifier: "move_window_to_workspace_9",
        title: "Move window to workspace 9",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
                if (8 < workspaces.Length)
                {
                    context.Butler.MoveWindowToWorkspace(workspaces[8]);
                    context.Butler.Activate(workspaces[8]);
                }
                else
                {
                    Logger.Error($"Workspace index 9 is out of bounds.");
                }
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_9", new Keybind(mod2, VIRTUAL_KEY.VK_D));

// Custom command to toggle maximize state of the focused window
    context.CommandManager.Add(
        identifier: "toggle_maximize_window",
        title: "Toggle maximize focused window",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                if (window.IsMaximized)
                {
                    window.Restore();
                }
                else
                {
                    window.ShowMaximized();
                }
            }
        }
    );
    // Create an associated keybind for RWin + Tab
    context.KeybindManager.SetKeybind("whim.custom.toggle_maximize_window", new Keybind(mod1, VIRTUAL_KEY.VK_TAB));
    // Customize your config in C# here.
    // For more, see https://dalyisaac.github.io/Whim/script/scripting.html
    // ...

    context.CommandManager.Add(
        identifier: "launch_explorer",
        title: "Launch Windows Explorer",
        callback: () =>
        {
            try
            {
                System.Diagnostics.Process.Start("explorer.exe");
            }
            catch (Exception ex)
            {
                Logger.Error($"Failed to launch explorer: {ex.Message}");
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.launch_explorer", new Keybind(mod1, VIRTUAL_KEY.VK_F));

    context.CommandManager.Add(
        identifier: "launch_shell",
        title: "Launch Powershell 7",
        callback: () =>
        {
            try
            {
                System.Diagnostics.Process.Start("C:/Program Files/PowerShell/7/pwsh.exe");
            }
            catch (Exception ex)3
            {
                Logger.Error($"Failed to launch Powershell: {ex.Message}");
            }
        }
    );
    context.KeybindManager.SetKeybind("whim.custom.launch_shell", new Keybind(mod1, VIRTUAL_KEY.VK_SPACE));



    // Custom commands and keybinds for moving windows to workspaces (Win + Alt + Key)

    // Custom command to close the focused window
    context.CommandManager.Add(
        identifier: "close_focused_window",
        title: "Close focused window",
        callback: () =>
        {
            if (context.Store.Pick(Pickers.PickLastFocusedWindow()).TryGet(out IWindow window))
            {
                window.Close();
            }
        }
    );
    // Create an associated keybind for Win + Alt + C
    context.KeybindManager.SetKeybind("whim.custom.close_focused_window", new Keybind(mod2, VIRTUAL_KEY.VK_C));
}

// We return doConfig here so that Whim can call it when it loads.
return DoConfig;

YAML Config

# yaml-language-server: $schema=C:\Program Files\Whim\plugins\Whim.Yaml\schema.json
workspaces:
  entries:
    - name:  " 1 "
    - name:  " 2 "
    - name:  " 3 "
    - name:  " Q "
    - name:  " W "
    - name:  " E "
    - name:  " A "
    - name:  " S "
    - name:  " D "

layout_engines:
  entries:
    - type: tree
      initial_direction: right

    - type: slice
      variant:
        type: row

    - type: slice
      variant:
        type: column

    - type: slice
      variant:
        type: primary_stack

    - type: slice
      variant:
        type: secondary_primary_stack

    - type: slice
      variant:
        type: multi_column_stack
        columns: [2, 1, 0]

    - type: slice
      variant:
        type: secondary_primary_stack
        primary_capacity: 1
        secondary_capacity: 2

    - type: focus
      maximize: false

    - type: floating

#keybinds:
 # entries:
    #- command: whim.core.focus_next_monitor
    #  keybind: LCtrl+LShift+LAlt+K

    #- command: whim.core.focus_previous_monitor
    #  keybind: LCtrl+LShift+LAlt+J

    #- command: whim.core.move_window_to_next_monitor
    #  keybind: LCtrl+LShift+Right

    #- command: whim.core.move_window_to_previous_monitor
    #  keybind: LCtrl+LShift+Left

    #- command: whim.core.cycle_layout_engine
    #  keybind: LCtrl+LShift+Space

    # Activate workspace keybinds (Win + Key)

    #- command: whim.command_palette.toggle
    #  keybond: Rwin+Shift+K

    # - command: whim.core.activate_workspace_1
    #   keybind: RWin+Shift+F1

    # - command: whim.core.activate_workspace_2
    #   keybind: RWin+Shift+F2

    # - command: whim.core.activate_workspace_3
    #   keybind: RWin+Shift+F3

    # - command: whim.core.activate_workspace_4
    #   keybind: RWin+Shift+F4

    # - command: whim.core.activate_workspace_5
    #   keybind: RWin+Shift+F5

    # - command: whim.core.activate_workspace_6
    #   keybind: RWin+Shift+F6

    # - command: whim.core.activate_workspace_7
    #   keybind: RWin+Shift+F7

    # - command: whim.core.activate_workspace_8
    #   keybind: RWin+Shift+F8

    # - command: whim.core.activate_workspace_9
    #   keybind: RWin+Shift+F9

#see ./whim.config.csx for:
# move windows to specific desktops (RWin F1...F9)
# toggle full screen (RWin TAB)


    # Move window to workspace keybinds (Alt + Win + Key): 
    #- command: whim.core.move_window_to_workspace_by_index_0
    #  keybind: Alt+RWin+Shift+F1

    #- command: whim.core.move_window_to_workspace_by_index_1
    #  keybind: Alt+RWin+Shift+F2

    #- command: whim.core.move_window_to_workspace_by_index_2
    #  keybind: Alt+RWin+Shift+F3

    #- command: whim.core.move_window_to_workspace_by_index_3
    #  keybind: Alt+RWin+Shift+F4

    #- command: whim.core.move_window_to_workspace_by_index_4
    #  keybind: Alt+RWin+Shift+F5

    #- command: whim.core.move_window_to_workspace_by_index_5
    #  keybind: Alt+RWin+Shift+F6

    #- command: whim.core.move_window_to_workspace_by_index_6
    #  keybind: Alt+RWin+Shift+F7

    #- command: whim.core.move_window_to_workspace_by_index_7
    #  keybind: Alt+RWin+Shift+F8

    #- command: whim.core.move_window_to_workspace_by_index_8
    #  keybind: Alt+RWin+Shift+F9


filters:

  entries: []

routers:
  routing_behavior: route_to_active_workspace #was: route_to_launched_workspace

plugins:
  bar:
    left_components:
      entries:
        - type: workspace_widget

    center_components:
      entries:
        - type: focused_window_widget
          shorten_title: true

    right_components:
      entries:
        - type: battery_widget
        - type: active_layout_widget
        - type: date_time_widget
          format: HH:mm:ss, dd MMM yyyy
        - type: tree_layout_widget

  floating_window:
    is_enabled: true

  gaps:
    is_enabled: true
    outer_gap: 1
    inner_gap: 1
    default_outer_delta: 2
    default_inner_delta: 2

  command_palette:
    is_enabled: true
    max_height_percent: 40
    max_width_pixels: 800
    y_position_percent: 20

  focus_indicator:
    is_enabled: true
    border_size: 4
    is_fade_enabled: false
    fade_timeout: 2
    color: red

  layout_preview:
    is_enabled: true

  updater:
    is_enabled: true
    release_channel: alpha
    update_frequency: monthly

Environment

  • Two Monitors, 4K (Laptop Internal+HDMI External)
  • Intel i7 12th gen, using integrated graphics (128MB shared Memory)
  • Windows: 24H2
  • Architecture: x64
  • Whim: v0.7.63-alpha+56e2a505

Steps To Reproduce

Configure whim to toggle to workspace1 using "modifier+1" and workspace2 using "modifier+2"

Open "Notepad" on Workspace1 (Laptop, Internal Monitor)
Open "Notepad" on Workspace2 (HDMI, External Monitor)
Give Focus so text can be typed on both Notepad Instances
Switch Between Workspace1 and Workspace2 very frequently.

test if you receive "1" or "2" in the notepad windows. I can not always trigger the behavior. I found following performance related influences:

The issue almost never happens when toggling between 2 workspaces that are on the same monitor.

Enabling "HDR" for the desktop makes the occurrence more frequent.

Having many windows on the 2 workspaces makes the occurrence more frequent.

Anything else?

There is a thread on discord:
https://discord.com/channels/1158330167400341615/1372887063929688125

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    Status

    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions