From 2a4e6fa6dac8dc91c18b6939ccdf5408af4a4142 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Tue, 7 Sep 2021 08:33:50 -0700 Subject: [PATCH] feat(wm): get monitor idx from cursor on ws switch If a user wants to switch workspace on a secondary monitor which contains no windows, komorebi doesn't register the monitor as focused unless the corresponding komorebic command to switch monitor focus is called explicitly. This generally works fine for users with a keyboard-heavy workflow. This commit changes the workspace switching behaviour to look up the current monitor based on the cursor position just before the switch takes place, so that the behaviour is still intuitive when trying to change the monitor focus via the mouse. re #30 --- komorebi/src/process_command.rs | 9 +++++++++ komorebi/src/window_manager.rs | 12 ++++++++++++ komorebi/src/windows_api.rs | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index f6f3c66d..2e655e55 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -148,6 +148,15 @@ impl WindowManager { self.set_workspace_layout(monitor_idx, workspace_idx, layout)?; } SocketMessage::FocusWorkspaceNumber(workspace_idx) => { + // This is to ensure that even on an empty workspace on a secondary monitor, the + // secondary monitor where the cursor is focused will be used as the target for + // the workspace switch op + let monitor_idx = self.monitor_idx_from_current_pos().ok_or_else(|| { + anyhow!("there is no monitor associated with the current cursor position") + })?; + + self.focus_monitor(monitor_idx)?; + self.focus_workspace(workspace_idx)?; } SocketMessage::Stop => { diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 50422a77..86f3c8ee 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -1014,6 +1014,18 @@ impl WindowManager { None } + pub fn monitor_idx_from_current_pos(&mut self) -> Option { + let hmonitor = WindowsApi::monitor_from_point(WindowsApi::cursor_pos().ok()?); + + for (i, monitor) in self.monitors().iter().enumerate() { + if monitor.id() == hmonitor { + return Option::from(i); + } + } + + None + } + pub fn focused_workspace(&self) -> Result<&Workspace> { self.focused_monitor() .ok_or_else(|| anyhow!("there is no monitor"))? diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index abed190c..7e5ffcd9 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -23,6 +23,7 @@ use bindings::Windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED; use bindings::Windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL; use bindings::Windows::Win32::Graphics::Gdi::EnumDisplayMonitors; use bindings::Windows::Win32::Graphics::Gdi::GetMonitorInfoW; +use bindings::Windows::Win32::Graphics::Gdi::MonitorFromPoint; use bindings::Windows::Win32::Graphics::Gdi::MonitorFromWindow; use bindings::Windows::Win32::Graphics::Gdi::HDC; use bindings::Windows::Win32::Graphics::Gdi::HMONITOR; @@ -227,6 +228,12 @@ impl WindowsApi { unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }.0 } + pub fn monitor_from_point(point: POINT) -> isize { + // MONITOR_DEFAULTTONEAREST ensures that the return value will never be NULL + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow + unsafe { MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST) }.0 + } + pub fn position_window(hwnd: HWND, layout: &Rect, top: bool) -> Result<()> { let flags = SetWindowPosition::NO_ACTIVATE;