From 0c42b0b518d452adccd5eb7b5140337817903b9d Mon Sep 17 00:00:00 2001 From: lars-berger Date: Thu, 14 Sep 2023 02:50:10 +0800 Subject: [PATCH] feat: focus override based on timer (#396) --- .../CommandHandlers/SyncNativeFocusHandler.cs | 11 ++---- GlazeWM.Domain/Containers/ContainerService.cs | 6 --- .../CommandHandlers/UnmanageWindowHandler.cs | 22 ++++++++--- .../EventHandlers/WindowFocusedHandler.cs | 39 +++++-------------- .../EventHandlers/WindowMinimizedHandler.cs | 16 ++++++-- GlazeWM.Domain/Windows/WindowService.cs | 15 ++----- 6 files changed, 44 insertions(+), 65 deletions(-) diff --git a/GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs index 26cff51a5..8f8592b35 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs @@ -12,16 +12,11 @@ internal sealed class SyncNativeFocusHandler : ICommandHandler window.Handle, - Workspace => _windowService.DesktopWindowHandle, + Workspace => GetDesktopWindow(), _ => throw new Exception("Invalid container type to focus. This is a bug."), }; @@ -49,6 +44,8 @@ public CommandResponse Handle(SyncNativeFocusCommand command) _bus.Emit(new NativeFocusSyncedEvent(focusedContainer)); _bus.Emit(new FocusChangedEvent(focusedContainer)); + _containerService.HasPendingFocusSync = false; + return CommandResponse.Ok; } } diff --git a/GlazeWM.Domain/Containers/ContainerService.cs b/GlazeWM.Domain/Containers/ContainerService.cs index e7b68ed0d..4a6c6a877 100644 --- a/GlazeWM.Domain/Containers/ContainerService.cs +++ b/GlazeWM.Domain/Containers/ContainerService.cs @@ -44,12 +44,6 @@ public class ContainerService /// public string ActiveBindingMode { get; set; } - /// - /// If set, this container overrides the target container to set focus to on the next - /// focus window event (ie. `EVENT_SYSTEM_FOREGROUND`). - /// - public Container PendingFocusContainer { get; set; } - private readonly UserConfigService _userConfigService; public ContainerService(UserConfigService userConfigService) diff --git a/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs index 5e2b56b14..a78b0e0a0 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs @@ -10,11 +10,16 @@ internal sealed class UnmanageWindowHandler : ICommandHandler window.Handle == @event.WindowHandle); @@ -77,6 +47,15 @@ public void Handle(WindowFocusedEvent @event) if (window == focusedContainer) return; + var unmanagedStopwatch = _windowService.UnmanagedOrMinimizedStopwatch; + + if (unmanagedStopwatch?.ElapsedMilliseconds < 100) + { + _logger.LogDebug("Overriding native focus."); + _bus.Invoke(new SyncNativeFocusCommand()); + return; + } + // Update the WM's focus state. _bus.Invoke(new SetFocusedDescendantCommand(window)); _bus.Emit(new FocusChangedEvent(window)); diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs index 75e4b02fa..508f78f15 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs @@ -55,14 +55,22 @@ public void Handle(WindowMinimizedEvent @event) Id = window.Id }; - // Get container to switch focus to after the window has been minimized. - var focusTarget = WindowService.GetFocusTargetAfterRemoval(window); + // Get container to switch focus to after the window has been minimized. Need to + // get focus target prior to calling `ReplaceContainerCommand`. + var focusedContainer = _containerService.FocusedContainer; + var focusTarget = window == focusedContainer + ? WindowService.GetFocusTargetAfterRemoval(window) + : null; _bus.Invoke(new ReplaceContainerCommand(minimizedWindow, window.Parent, window.Index)); // Focus should be reassigned to appropriate container. - _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); - _containerService.HasPendingFocusSync = true; + if (focusTarget is not null) + { + _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); + _containerService.HasPendingFocusSync = true; + _windowService.UnmanagedOrMinimizedStopwatch.Restart(); + } _containerService.ContainersToRedraw.Add(workspace); _bus.Invoke(new RedrawContainersCommand()); diff --git a/GlazeWM.Domain/Windows/WindowService.cs b/GlazeWM.Domain/Windows/WindowService.cs index 864445533..9bc92567c 100644 --- a/GlazeWM.Domain/Windows/WindowService.cs +++ b/GlazeWM.Domain/Windows/WindowService.cs @@ -26,9 +26,10 @@ public class WindowService public List IgnoredHandles { get; set; } = new(); /// - /// Handle to the desktop window. + /// Time since a previously focused window was unmanaged or minimized. Used to + /// decide whether to override incoming focus events. /// - public IntPtr DesktopWindowHandle { get; } = GetDesktopWindowHandle(); + public Stopwatch UnmanagedOrMinimizedStopwatch { get; } = new(); private readonly ContainerService _containerService; private readonly MonitorService _monitorService; @@ -127,16 +128,6 @@ public static List GetAllWindowHandles() return windowHandles; } - private static IntPtr GetDesktopWindowHandle() - { - return GetAllWindowHandles().Find(handle => - { - var className = GetClassNameOfHandle(handle); - var process = GetProcessOfHandle(handle); - return className == "Progman" && process.ProcessName == "explorer"; - }); - } - public static WindowStylesEx GetWindowStylesEx(IntPtr handle) { return unchecked((WindowStylesEx)GetWindowLongPtr(handle, GWLEXSTYLE).ToInt64());