From 973de45f7187bfb5c866ddcb09054260978ff6c3 Mon Sep 17 00:00:00 2001 From: Lars Berger Date: Tue, 12 Sep 2023 04:43:35 +0800 Subject: [PATCH 1/4] wip --- .../PopulateInitialStateHandler.cs | 2 +- .../CommandHandlers/FocusInDirectionHandler.cs | 4 +++- .../CommandHandlers/SetNativeFocusHandler.cs | 17 +++++++++++------ .../CommandHandlers/ToggleFocusModeHandler.cs | 3 ++- GlazeWM.Domain/Containers/ContainerService.cs | 6 ++++++ .../RegisterKeybindingsHandler.cs | 1 + .../CommandHandlers/ManageWindowHandler.cs | 4 ++-- .../CommandHandlers/UnmanageWindowHandler.cs | 3 ++- .../EventHandlers/WindowMinimizedHandler.cs | 5 +++-- .../Windows/EventHandlers/WindowShownHandler.cs | 1 + .../CommandHandlers/FocusWorkspaceHandler.cs | 4 +--- .../MoveWindowToWorkspaceHandler.cs | 2 +- 12 files changed, 34 insertions(+), 18 deletions(-) diff --git a/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs b/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs index fbce59374..1f1718ddb 100644 --- a/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs +++ b/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs @@ -59,7 +59,7 @@ public CommandResponse Handle(PopulateInitialStateCommand command) _bus.Invoke(new SetFocusedDescendantCommand(containerToFocus)); _bus.Emit(new FocusChangedEvent(containerToFocus)); - _bus.Invoke(new SetNativeFocusCommand(containerToFocus)); + _bus.Invoke(new SetNativeFocusCommand()); return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs index 834d55701..cffaf5679 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs @@ -34,7 +34,9 @@ public CommandResponse Handle(FocusInDirectionCommand command) if (focusTarget is null || focusTarget == focusedContainer) return CommandResponse.Ok; - _bus.Invoke(new SetNativeFocusCommand(focusTarget)); + _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); + _containerService.HasPendingNativeFocus = true; + return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs index e8dbf9b8d..3d79d2fd9 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs @@ -13,6 +13,7 @@ internal sealed class SetNativeFocusHandler : ICommandHandler window.Handle, Workspace => _windowService.DesktopWindowHandle, @@ -36,15 +41,15 @@ public CommandResponse Handle(SetNativeFocusCommand command) KeybdEvent(0, 0, 0, 0); SetForegroundWindow(handleToFocus); - _bus.Emit(new NativeFocusReassignedEvent(containerToFocus)); + _bus.Emit(new NativeFocusReassignedEvent(focusedContainer)); // Setting focus to the desktop window does not emit `EVENT_SYSTEM_FOREGROUND` window event, // so `SetFocusedDescendantCommand` has to be manually called. // TODO: This is called twice unnecessarily when setting workspace focus on unmanage. - if (containerToFocus is Workspace) + if (focusedContainer is Workspace) { - _bus.Invoke(new SetFocusedDescendantCommand(containerToFocus)); - _bus.Emit(new FocusChangedEvent(containerToFocus)); + _bus.Invoke(new SetFocusedDescendantCommand(focusedContainer)); + _bus.Emit(new FocusChangedEvent(focusedContainer)); } return CommandResponse.Ok; diff --git a/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs index 366c6b613..ea9d260d3 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs @@ -34,7 +34,8 @@ public CommandResponse Handle(ToggleFocusModeCommand command) if (windowToFocus is null) return CommandResponse.Ok; - _bus.Invoke(new SetNativeFocusCommand(windowToFocus)); + _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); + _containerService.HasPendingNativeFocus = true; return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/ContainerService.cs b/GlazeWM.Domain/Containers/ContainerService.cs index 6bbd7d32d..4c4fbe3e6 100644 --- a/GlazeWM.Domain/Containers/ContainerService.cs +++ b/GlazeWM.Domain/Containers/ContainerService.cs @@ -28,6 +28,12 @@ public class ContainerService /// public Container FocusedContainer => ContainerTree.LastFocusedDescendant; + /// + /// Whether native focus will need to be reassigned to the WM's focused container. + /// + /// TODO: Alternatively: HasPendingFocusSync + public bool HasPendingNativeFocus { get; set; } + /// /// Whether a tiling or floating container is currently focused. /// diff --git a/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs b/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs index f2eb2de85..41092d5d7 100644 --- a/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs +++ b/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs @@ -54,6 +54,7 @@ public CommandResponse Handle(RegisterKeybindingsCommand command) _bus.Invoke(new RunWithSubjectContainerCommand(commandStrings)); _bus.Invoke(new RedrawContainersCommand()); + _bus.Invoke(new SetNativeFocusCommand()); } } catch (Exception e) diff --git a/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs index 7e722f77b..100f8491f 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs @@ -85,8 +85,8 @@ public CommandResponse Handle(ManageWindowCommand command) _logger.LogWindowEvent("New window managed", window); _bus.Emit(new WindowManagedEvent(window)); - // Set OS focus to the newly added window in case it's not already focused. - _bus.Invoke(new SetNativeFocusCommand(window)); + // OS focus should be set to the newly added window in case it's not already focused. + _containerService.HasPendingNativeFocus = true; return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs index ff88e2f77..8826f75d3 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs @@ -34,8 +34,9 @@ public CommandResponse Handle(UnmanageWindowCommand command) // cause focus to briefly flicker to the OS focus target and then to the WM's focus // target. + _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); _containerService.PendingFocusContainer = focusTarget; - _bus.InvokeAsync(new SetNativeFocusCommand(focusTarget)); + _containerService.HasPendingNativeFocus = true; _bus.Emit(new WindowUnmanagedEvent(window.Id, window.Handle)); diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs index dfdb097c8..547ab0bc0 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs @@ -60,8 +60,9 @@ public void Handle(WindowMinimizedEvent @event) _bus.Invoke(new ReplaceContainerCommand(minimizedWindow, window.Parent, window.Index)); - // Reassign focus to appropriate container. - _bus.InvokeAsync(new SetNativeFocusCommand(focusTarget)); + // Focus should be reassigned to appropriate container. + _bus.InvokeAsync(new SetFocusedDescendant(focusTarget)); + _containerService.HasPendingNativeFocus = true; _containerService.ContainersToRedraw.Add(workspace); _bus.Invoke(new RedrawContainersCommand()); diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs index e300e1243..11380e2b6 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs @@ -38,6 +38,7 @@ public void Handle(WindowShownEvent @event) _bus.Invoke(new ManageWindowCommand(@event.WindowHandle)); _bus.Invoke(new RedrawContainersCommand()); + _bus.Invoke(new SetNativeFocusCommand()); } } } diff --git a/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs b/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs index 8e5239ce8..198ee8244 100644 --- a/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs +++ b/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs @@ -67,7 +67,7 @@ public CommandResponse Handle(FocusWorkspaceCommand command) : workspaceToFocus; _bus.Invoke(new SetFocusedDescendantCommand(containerToFocus)); - _bus.Emit(new FocusChangedEvent(containerToFocus)); + _containerService.HasPendingNativeFocus = true; // Display the workspace to switch focus to. if (focusedWorkspace.Parent == workspaceToFocus.Parent) @@ -76,8 +76,6 @@ public CommandResponse Handle(FocusWorkspaceCommand command) _containerService.ContainersToRedraw.Add(workspaceToFocus); } - _bus.Invoke(new SetNativeFocusCommand(containerToFocus)); - // Get empty workspace to destroy (if any are found). Cannot destroy empty workspaces if // they're the only workspace on the monitor or are pending focus. var workspaceToDestroy = _workspaceService diff --git a/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs b/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs index a5e74c229..04402d886 100644 --- a/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs +++ b/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs @@ -64,7 +64,7 @@ public CommandResponse Handle(MoveWindowToWorkspaceCommand command) // immediately afterwards and they should behave as if `focusTarget` is the focused // descendant. _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); - _bus.Invoke(new SetNativeFocusCommand(focusTarget)); + _containerService.HasPendingNativeFocus = true; _containerService.ContainersToRedraw.Add(currentWorkspace); _containerService.ContainersToRedraw.Add(windowToMove); From 7b1886b30f89017f165345e9db722d2e936dda4f Mon Sep 17 00:00:00 2001 From: Lars Berger Date: Wed, 13 Sep 2023 02:46:21 +0800 Subject: [PATCH 2/4] wip --- GlazeWM.App.WindowManager/WmStartup.cs | 24 ++++++-------- .../WorkspacesComponentViewModel.cs | 1 + .../PopulateInitialStateHandler.cs | 4 +-- GlazeWM.Domain/Common/DomainEvent.cs | 2 +- .../FocusInDirectionHandler.cs | 2 +- ...usHandler.cs => SyncNativeFocusHandler.cs} | 33 +++++++++---------- .../CommandHandlers/ToggleFocusModeHandler.cs | 2 +- .../Commands/SetNativeFocusCommand.cs | 14 -------- .../Commands/SyncNativeFocusCommand.cs | 8 +++++ GlazeWM.Domain/Containers/ContainerService.cs | 3 +- .../Events/NativeFocusReassignedEvent.cs | 8 ----- .../Events/NativeFocusSyncedEvent.cs | 8 +++++ GlazeWM.Domain/DependencyInjection.cs | 2 +- .../RegisterKeybindingsHandler.cs | 2 +- .../CommandHandlers/ManageWindowHandler.cs | 2 +- .../CommandHandlers/MoveWindowHandler.cs | 4 --- .../CommandHandlers/ResizeWindowHandler.cs | 1 - .../CommandHandlers/UnmanageWindowHandler.cs | 2 +- .../EventHandlers/WindowFocusedHandler.cs | 11 +++++-- .../EventHandlers/WindowMinimizedHandler.cs | 2 +- .../EventHandlers/WindowShownHandler.cs | 2 +- .../CommandHandlers/FocusWorkspaceHandler.cs | 2 +- .../MoveWindowToWorkspaceHandler.cs | 2 +- 23 files changed, 65 insertions(+), 76 deletions(-) rename GlazeWM.Domain/Containers/CommandHandlers/{SetNativeFocusHandler.cs => SyncNativeFocusHandler.cs} (53%) delete mode 100644 GlazeWM.Domain/Containers/Commands/SetNativeFocusCommand.cs create mode 100644 GlazeWM.Domain/Containers/Commands/SyncNativeFocusCommand.cs delete mode 100644 GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs create mode 100644 GlazeWM.Domain/Containers/Events/NativeFocusSyncedEvent.cs diff --git a/GlazeWM.App.WindowManager/WmStartup.cs b/GlazeWM.App.WindowManager/WmStartup.cs index 5200efc3a..07e054d83 100644 --- a/GlazeWM.App.WindowManager/WmStartup.cs +++ b/GlazeWM.App.WindowManager/WmStartup.cs @@ -75,22 +75,14 @@ public ExitCode Run() _systemTrayIcon = new SystemTrayIcon(systemTrayIconConfig); _systemTrayIcon.Show(); - var nativeFocusReassigned = _bus.Events - .OfType() - .Select((@event) => @event.FocusedContainer); - if (_userConfigService.FocusBorderConfig.Active.Enabled || _userConfigService.FocusBorderConfig.Inactive.Enabled) { - var focusChanged = _bus.Events - .OfType() - .Select(@event => @event.FocusedContainer); - - focusChanged - .Merge(nativeFocusReassigned) - .Subscribe((container) => - _bus.InvokeAsync(new SetActiveWindowBorderCommand(container as Window)) - ); + _bus.Events.OfType().Subscribe((@event) => + _bus.InvokeAsync( + new SetActiveWindowBorderCommand(@event.FocusedContainer as Window) + ) + ); } // Hook mouse event for focus follows cursor. @@ -108,7 +100,11 @@ public ExitCode Run() .OfType() .Select(@event => @event.FocusedContainer); - focusedContainerMoved.Merge(nativeFocusReassigned) + var nativeFocusSynced = _bus.Events + .OfType() + .Select((@event) => @event.FocusedContainer); + + focusedContainerMoved.Merge(nativeFocusSynced) .Where(container => container is Window) .Subscribe((window) => _bus.InvokeAsync(new CenterCursorOnContainerCommand(window))); } diff --git a/GlazeWM.Bar/Components/WorkspacesComponentViewModel.cs b/GlazeWM.Bar/Components/WorkspacesComponentViewModel.cs index 0929e8a93..772be35c4 100644 --- a/GlazeWM.Bar/Components/WorkspacesComponentViewModel.cs +++ b/GlazeWM.Bar/Components/WorkspacesComponentViewModel.cs @@ -78,6 +78,7 @@ public WorkspacesComponentViewModel( @event is WorkspaceActivatedEvent or WorkspaceDeactivatedEvent or FocusChangedEvent + or FocusedContainerMovedEvent ); // Refresh contents of workspaces collection. diff --git a/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs b/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs index 1f1718ddb..14041b957 100644 --- a/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs +++ b/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs @@ -58,8 +58,8 @@ public CommandResponse Handle(PopulateInitialStateCommand command) _workspaceService.GetActiveWorkspaces().FirstOrDefault() as Container; _bus.Invoke(new SetFocusedDescendantCommand(containerToFocus)); - _bus.Emit(new FocusChangedEvent(containerToFocus)); - _bus.Invoke(new SetNativeFocusCommand()); + _containerService.HasPendingFocusSync = true; + _bus.Invoke(new SyncNativeFocusCommand()); return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Common/DomainEvent.cs b/GlazeWM.Domain/Common/DomainEvent.cs index a5291668d..7eb80e458 100644 --- a/GlazeWM.Domain/Common/DomainEvent.cs +++ b/GlazeWM.Domain/Common/DomainEvent.cs @@ -7,7 +7,7 @@ public static class DomainEvent public const string FocusedContainerMoved = "focused_container_moved"; public const string MonitorAdded = "monitor_added"; public const string MonitorRemoved = "monitor_removed"; - public const string NativeFocusReassigned = "native_focus_reassigned"; + public const string NativeFocusSynced = "native_focus_synced"; public const string TilingDirectionChanged = "tiling_direction_changed"; public const string UserConfigReloaded = "user_config_reloaded"; public const string WindowManaged = "window_managed"; diff --git a/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs index cffaf5679..0e79d7522 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/FocusInDirectionHandler.cs @@ -35,7 +35,7 @@ public CommandResponse Handle(FocusInDirectionCommand command) return CommandResponse.Ok; _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs similarity index 53% rename from GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs rename to GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs index 3d79d2fd9..26cff51a5 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/SetNativeFocusHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/SyncNativeFocusHandler.cs @@ -8,26 +8,32 @@ namespace GlazeWM.Domain.Containers.CommandHandlers { - internal sealed class SetNativeFocusHandler : ICommandHandler + internal sealed class SyncNativeFocusHandler : ICommandHandler { private readonly Bus _bus; + private readonly ContainerService _containerService; private readonly WindowService _windowService; - // TODO: Rename `SyncNativeFocusCommand`. - public SetNativeFocusHandler(Bus bus, WindowService windowService) + public SyncNativeFocusHandler( + Bus bus, + ContainerService containerService, + WindowService windowService) { _bus = bus; + _containerService = containerService; _windowService = windowService; } - public CommandResponse Handle(SetNativeFocusCommand command) + public CommandResponse Handle(SyncNativeFocusCommand command) { - var hasPendingNativeFocus = _containerService.HasPendingNativeFocus; + var hasPendingFocusSync = _containerService.HasPendingFocusSync; - if (!hasPendingNativeFocus) + if (!hasPendingFocusSync) return CommandResponse.Ok; + // Container that the WM believes should have focus. var focusedContainer = _containerService.FocusedContainer; + var handleToFocus = focusedContainer switch { Window window => window.Handle, @@ -36,21 +42,12 @@ public CommandResponse Handle(SetNativeFocusCommand command) }; // Set focus to the given window handle. If the container is a normal window, then this - // will trigger `EVENT_SYSTEM_FOREGROUND` window event and its handler. This, in turn, - // calls `SetFocusedDescendantCommand`. + // will trigger `EVENT_SYSTEM_FOREGROUND` window event and its handler. KeybdEvent(0, 0, 0, 0); SetForegroundWindow(handleToFocus); - _bus.Emit(new NativeFocusReassignedEvent(focusedContainer)); - - // Setting focus to the desktop window does not emit `EVENT_SYSTEM_FOREGROUND` window event, - // so `SetFocusedDescendantCommand` has to be manually called. - // TODO: This is called twice unnecessarily when setting workspace focus on unmanage. - if (focusedContainer is Workspace) - { - _bus.Invoke(new SetFocusedDescendantCommand(focusedContainer)); - _bus.Emit(new FocusChangedEvent(focusedContainer)); - } + _bus.Emit(new NativeFocusSyncedEvent(focusedContainer)); + _bus.Emit(new FocusChangedEvent(focusedContainer)); return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs index ea9d260d3..641e5e5fd 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs @@ -35,7 +35,7 @@ public CommandResponse Handle(ToggleFocusModeCommand command) return CommandResponse.Ok; _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/Commands/SetNativeFocusCommand.cs b/GlazeWM.Domain/Containers/Commands/SetNativeFocusCommand.cs deleted file mode 100644 index d5a847891..000000000 --- a/GlazeWM.Domain/Containers/Commands/SetNativeFocusCommand.cs +++ /dev/null @@ -1,14 +0,0 @@ -using GlazeWM.Infrastructure.Bussing; - -namespace GlazeWM.Domain.Containers.Commands -{ - public class SetNativeFocusCommand : Command - { - public Container ContainerToFocus { get; } - - public SetNativeFocusCommand(Container containerToFocus) - { - ContainerToFocus = containerToFocus; - } - } -} diff --git a/GlazeWM.Domain/Containers/Commands/SyncNativeFocusCommand.cs b/GlazeWM.Domain/Containers/Commands/SyncNativeFocusCommand.cs new file mode 100644 index 000000000..7f52faf99 --- /dev/null +++ b/GlazeWM.Domain/Containers/Commands/SyncNativeFocusCommand.cs @@ -0,0 +1,8 @@ +using GlazeWM.Infrastructure.Bussing; + +namespace GlazeWM.Domain.Containers.Commands +{ + public class SyncNativeFocusCommand : Command + { + } +} diff --git a/GlazeWM.Domain/Containers/ContainerService.cs b/GlazeWM.Domain/Containers/ContainerService.cs index 4c4fbe3e6..bf625e0e4 100644 --- a/GlazeWM.Domain/Containers/ContainerService.cs +++ b/GlazeWM.Domain/Containers/ContainerService.cs @@ -31,8 +31,7 @@ public class ContainerService /// /// Whether native focus will need to be reassigned to the WM's focused container. /// - /// TODO: Alternatively: HasPendingFocusSync - public bool HasPendingNativeFocus { get; set; } + public bool HasPendingFocusSync { get; set; } /// /// Whether a tiling or floating container is currently focused. diff --git a/GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs b/GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs deleted file mode 100644 index 7c1795d07..000000000 --- a/GlazeWM.Domain/Containers/Events/NativeFocusReassignedEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using GlazeWM.Domain.Common; -using GlazeWM.Infrastructure.Bussing; - -namespace GlazeWM.Domain.Containers.Events -{ - public record NativeFocusReassignedEvent(Container FocusedContainer) - : Event(DomainEvent.NativeFocusReassigned); -} diff --git a/GlazeWM.Domain/Containers/Events/NativeFocusSyncedEvent.cs b/GlazeWM.Domain/Containers/Events/NativeFocusSyncedEvent.cs new file mode 100644 index 000000000..7c59f9c47 --- /dev/null +++ b/GlazeWM.Domain/Containers/Events/NativeFocusSyncedEvent.cs @@ -0,0 +1,8 @@ +using GlazeWM.Domain.Common; +using GlazeWM.Infrastructure.Bussing; + +namespace GlazeWM.Domain.Containers.Events +{ + public record NativeFocusSyncedEvent(Container FocusedContainer) + : Event(DomainEvent.NativeFocusSynced); +} diff --git a/GlazeWM.Domain/DependencyInjection.cs b/GlazeWM.Domain/DependencyInjection.cs index 293d0c607..2180e2ebe 100644 --- a/GlazeWM.Domain/DependencyInjection.cs +++ b/GlazeWM.Domain/DependencyInjection.cs @@ -52,7 +52,7 @@ public static IServiceCollection AddDomainServices(this IServiceCollection servi services.AddSingleton, ReplaceContainerHandler>(); services.AddSingleton, ResizeContainerHandler>(); services.AddSingleton, SetFocusedDescendantHandler>(); - services.AddSingleton, SetNativeFocusHandler>(); + services.AddSingleton, SyncNativeFocusHandler>(); services.AddSingleton, ToggleFocusModeHandler>(); services.AddSingleton, AddMonitorHandler>(); services.AddSingleton, RefreshMonitorStateHandler>(); diff --git a/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs b/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs index 41092d5d7..a6fc2f543 100644 --- a/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs +++ b/GlazeWM.Domain/UserConfigs/CommandHandlers/RegisterKeybindingsHandler.cs @@ -54,7 +54,7 @@ public CommandResponse Handle(RegisterKeybindingsCommand command) _bus.Invoke(new RunWithSubjectContainerCommand(commandStrings)); _bus.Invoke(new RedrawContainersCommand()); - _bus.Invoke(new SetNativeFocusCommand()); + _bus.Invoke(new SyncNativeFocusCommand()); } } catch (Exception e) diff --git a/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs index 100f8491f..8c03d8295 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/ManageWindowHandler.cs @@ -86,7 +86,7 @@ public CommandResponse Handle(ManageWindowCommand command) _bus.Emit(new WindowManagedEvent(window)); // OS focus should be set to the newly added window in case it's not already focused. - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Windows/CommandHandlers/MoveWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/MoveWindowHandler.cs index 6b477b5ea..220a1c626 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/MoveWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/MoveWindowHandler.cs @@ -128,9 +128,6 @@ private void MoveToWorkspaceInDirection(Window windowToMove, Direction direction _bus.Invoke(new MoveContainerWithinTreeCommand(windowToMove, workspaceInDirection, true)); else _bus.Invoke(new MoveContainerWithinTreeCommand(windowToMove, workspaceInDirection, 0, true)); - - // Refresh state in bar of which workspace has focus. - _bus.Emit(new FocusChangedEvent(windowToMove)); } private void ChangeWorkspaceTilingDirection(Window windowToMove, Direction direction) @@ -289,7 +286,6 @@ private void MoveFloatingWindow(Window windowToMove, Direction direction) // Change the window's parent workspace. _bus.Invoke(new MoveContainerWithinTreeCommand(windowToMove, workspaceInDirection, false)); - _bus.Emit(new FocusChangedEvent(windowToMove)); // Redrawing twice to fix weird WindowsOS dpi behaviour windowToMove.HasPendingDpiAdjustment = true; diff --git a/GlazeWM.Domain/Windows/CommandHandlers/ResizeWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/ResizeWindowHandler.cs index a32463f34..ff8f555a8 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/ResizeWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/ResizeWindowHandler.cs @@ -131,7 +131,6 @@ private void ResizeFloatingWindow(Window windowToResize, ResizeDimension dimensi // Change the window's parent workspace. _bus.Invoke(new MoveContainerWithinTreeCommand(windowToResize, targetWorkspace, false)); - _bus.Emit(new FocusChangedEvent(windowToResize)); windowToResize.HasPendingDpiAdjustment = true; } diff --git a/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs b/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs index 8826f75d3..5e2b56b14 100644 --- a/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs +++ b/GlazeWM.Domain/Windows/CommandHandlers/UnmanageWindowHandler.cs @@ -36,7 +36,7 @@ public CommandResponse Handle(UnmanageWindowCommand command) _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); _containerService.PendingFocusContainer = focusTarget; - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; _bus.Emit(new WindowUnmanagedEvent(window.Id, window.Handle)); diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs index 5be8315c5..e611b1946 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs @@ -61,7 +61,7 @@ public void Handle(WindowFocusedEvent @event) return; } - _bus.InvokeAsync(new SetNativeFocusCommand(pendingFocusContainer)); + _bus.InvokeAsync(new SyncNativeFocusCommand()); return; } @@ -71,8 +71,15 @@ public void Handle(WindowFocusedEvent @event) if (window is null || window?.IsDisplayed == false) return; - _logger.LogWindowEvent("Window focused", window); + _logger.LogWindowEvent("Native focus event", window); + var focusedContainer = _containerService.FocusedContainer; + + // Focus is already set to the WM's focused container. + if (window == focusedContainer) + 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 547ab0bc0..ff13552eb 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs @@ -62,7 +62,7 @@ public void Handle(WindowMinimizedEvent @event) // Focus should be reassigned to appropriate container. _bus.InvokeAsync(new SetFocusedDescendant(focusTarget)); - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; _containerService.ContainersToRedraw.Add(workspace); _bus.Invoke(new RedrawContainersCommand()); diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs index 11380e2b6..410b1e9cb 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowShownHandler.cs @@ -38,7 +38,7 @@ public void Handle(WindowShownEvent @event) _bus.Invoke(new ManageWindowCommand(@event.WindowHandle)); _bus.Invoke(new RedrawContainersCommand()); - _bus.Invoke(new SetNativeFocusCommand()); + _bus.Invoke(new SyncNativeFocusCommand()); } } } diff --git a/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs b/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs index 198ee8244..680708800 100644 --- a/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs +++ b/GlazeWM.Domain/Workspaces/CommandHandlers/FocusWorkspaceHandler.cs @@ -67,7 +67,7 @@ public CommandResponse Handle(FocusWorkspaceCommand command) : workspaceToFocus; _bus.Invoke(new SetFocusedDescendantCommand(containerToFocus)); - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; // Display the workspace to switch focus to. if (focusedWorkspace.Parent == workspaceToFocus.Parent) diff --git a/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs b/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs index 04402d886..a4ab961d0 100644 --- a/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs +++ b/GlazeWM.Domain/Workspaces/CommandHandlers/MoveWindowToWorkspaceHandler.cs @@ -64,7 +64,7 @@ public CommandResponse Handle(MoveWindowToWorkspaceCommand command) // immediately afterwards and they should behave as if `focusTarget` is the focused // descendant. _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); - _containerService.HasPendingNativeFocus = true; + _containerService.HasPendingFocusSync = true; _containerService.ContainersToRedraw.Add(currentWorkspace); _containerService.ContainersToRedraw.Add(windowToMove); From 5bc3f681cb14187c4a2953087b5e38a062fa47ef Mon Sep 17 00:00:00 2001 From: Lars Berger Date: Wed, 13 Sep 2023 23:19:15 +0800 Subject: [PATCH 3/4] feat: sync focus at end of window destroyed, hidden, and minimized --- GlazeWM.Domain/Containers/ContainerService.cs | 2 +- GlazeWM.Domain/Windows/EventHandlers/WindowDestroyedHandler.cs | 1 + GlazeWM.Domain/Windows/EventHandlers/WindowHiddenHandler.cs | 1 + GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/GlazeWM.Domain/Containers/ContainerService.cs b/GlazeWM.Domain/Containers/ContainerService.cs index bf625e0e4..e7b68ed0d 100644 --- a/GlazeWM.Domain/Containers/ContainerService.cs +++ b/GlazeWM.Domain/Containers/ContainerService.cs @@ -29,7 +29,7 @@ public class ContainerService public Container FocusedContainer => ContainerTree.LastFocusedDescendant; /// - /// Whether native focus will need to be reassigned to the WM's focused container. + /// Whether native focus needs to be reassigned to `FocusedContainer`. /// public bool HasPendingFocusSync { get; set; } diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowDestroyedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowDestroyedHandler.cs index 2c318b517..63cc3857e 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowDestroyedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowDestroyedHandler.cs @@ -47,6 +47,7 @@ public void Handle(WindowDestroyedEvent @event) // If window is in tree, detach the removed window from its parent. _bus.Invoke(new UnmanageWindowCommand(window)); _bus.Invoke(new RedrawContainersCommand()); + _bus.Invoke(new SyncNativeFocusCommand()); } } } diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowHiddenHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowHiddenHandler.cs index 47c36430c..e7db591a0 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowHiddenHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowHiddenHandler.cs @@ -50,6 +50,7 @@ public void Handle(WindowHiddenEvent @event) // Detach the hidden window from its parent. _bus.Invoke(new UnmanageWindowCommand(window)); _bus.Invoke(new RedrawContainersCommand()); + _bus.Invoke(new SyncNativeFocusCommand()); } } } diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs index ff13552eb..0ebd81cf0 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs @@ -66,6 +66,7 @@ public void Handle(WindowMinimizedEvent @event) _containerService.ContainersToRedraw.Add(workspace); _bus.Invoke(new RedrawContainersCommand()); + _bus.Invoke(new SyncNativeFocusCommand()); } } } From 253ddc66a5fcf3f22ac6790a5454d026165ca8e6 Mon Sep 17 00:00:00 2001 From: Lars Berger Date: Wed, 13 Sep 2023 23:42:10 +0800 Subject: [PATCH 4/4] minor fixes --- GlazeWM.App.WindowManager/WmStartup.cs | 1 + .../Common/CommandHandlers/PopulateInitialStateHandler.cs | 8 +++++--- .../Containers/CommandHandlers/ToggleFocusModeHandler.cs | 2 +- .../Windows/EventHandlers/WindowFocusedHandler.cs | 2 -- .../Windows/EventHandlers/WindowMinimizedHandler.cs | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/GlazeWM.App.WindowManager/WmStartup.cs b/GlazeWM.App.WindowManager/WmStartup.cs index 07e054d83..78488316f 100644 --- a/GlazeWM.App.WindowManager/WmStartup.cs +++ b/GlazeWM.App.WindowManager/WmStartup.cs @@ -49,6 +49,7 @@ public ExitCode Run() // Populate initial monitors, windows, workspaces and user config. _bus.Invoke(new PopulateInitialStateCommand()); _bus.Invoke(new RedrawContainersCommand()); + _bus.Invoke(new SyncNativeFocusCommand()); // Listen on registered keybindings. _keybindingService.Start(); diff --git a/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs b/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs index 14041b957..a2df4969a 100644 --- a/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs +++ b/GlazeWM.Domain/Common/CommandHandlers/PopulateInitialStateHandler.cs @@ -2,7 +2,6 @@ using GlazeWM.Domain.Common.Commands; using GlazeWM.Domain.Containers; using GlazeWM.Domain.Containers.Commands; -using GlazeWM.Domain.Containers.Events; using GlazeWM.Domain.Monitors; using GlazeWM.Domain.Monitors.Commands; using GlazeWM.Domain.UserConfigs.Commands; @@ -17,16 +16,20 @@ namespace GlazeWM.Domain.Common.CommandHandlers internal sealed class PopulateInitialStateHandler : ICommandHandler { private readonly Bus _bus; + private readonly ContainerService _containerService; private readonly MonitorService _monitorService; private readonly WindowService _windowService; private readonly WorkspaceService _workspaceService; - public PopulateInitialStateHandler(Bus bus, + public PopulateInitialStateHandler( + Bus bus, + ContainerService containerService, MonitorService monitorService, WindowService windowService, WorkspaceService workspaceService) { _bus = bus; + _containerService = containerService; _monitorService = monitorService; _windowService = windowService; _workspaceService = workspaceService; @@ -59,7 +62,6 @@ public CommandResponse Handle(PopulateInitialStateCommand command) _bus.Invoke(new SetFocusedDescendantCommand(containerToFocus)); _containerService.HasPendingFocusSync = true; - _bus.Invoke(new SyncNativeFocusCommand()); return CommandResponse.Ok; } diff --git a/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs b/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs index 641e5e5fd..a0e18b3da 100644 --- a/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs +++ b/GlazeWM.Domain/Containers/CommandHandlers/ToggleFocusModeHandler.cs @@ -34,7 +34,7 @@ public CommandResponse Handle(ToggleFocusModeCommand command) if (windowToFocus is null) return CommandResponse.Ok; - _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); + _bus.Invoke(new SetFocusedDescendantCommand(windowToFocus)); _containerService.HasPendingFocusSync = true; return CommandResponse.Ok; diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs index e611b1946..02b891d74 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowFocusedHandler.cs @@ -4,11 +4,9 @@ using GlazeWM.Domain.Containers.Commands; using GlazeWM.Domain.Containers.Events; using GlazeWM.Domain.Workspaces; -using GlazeWM.Domain.Workspaces.Commands; using GlazeWM.Infrastructure.Bussing; using GlazeWM.Infrastructure.Common.Events; using Microsoft.Extensions.Logging; -using static GlazeWM.Infrastructure.WindowsApi.WindowsApiService; namespace GlazeWM.Domain.Windows.EventHandlers { diff --git a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs index 0ebd81cf0..75e4b02fa 100644 --- a/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs +++ b/GlazeWM.Domain/Windows/EventHandlers/WindowMinimizedHandler.cs @@ -61,7 +61,7 @@ public void Handle(WindowMinimizedEvent @event) _bus.Invoke(new ReplaceContainerCommand(minimizedWindow, window.Parent, window.Index)); // Focus should be reassigned to appropriate container. - _bus.InvokeAsync(new SetFocusedDescendant(focusTarget)); + _bus.Invoke(new SetFocusedDescendantCommand(focusTarget)); _containerService.HasPendingFocusSync = true; _containerService.ContainersToRedraw.Add(workspace);