feat(wm): add focus follows mouse toggle

This commit is contained in:
LGUG2Z
2021-08-09 07:49:00 -07:00
parent 8b4ce48a66
commit f3661325d9
6 changed files with 90 additions and 25 deletions

View File

@@ -6,26 +6,26 @@ Tiling Window Management for Windows.
## About ## About
*komorebi* is a tiling window manager that works as an extension to _komorebi_ is a tiling window manager that works as an extension to
Microsoft's [Desktop Window Manager](https://docs.microsoft.com/en-us/windows/win32/dwm/dwm-overview) in Windows 10 and Microsoft's [Desktop Window Manager](https://docs.microsoft.com/en-us/windows/win32/dwm/dwm-overview) in Windows 10 and
above. above.
*komorebi* allows you to control application windows, virtual workspaces and display monitors with a CLI which can be used _komorebi_ allows you to control application windows, virtual workspaces and display monitors with a CLI which can be used
with third-party software such as [AutoHotKey](https://github.com/Lexikos/AutoHotkey_L) to set user-defined keyboard with third-party software such as [AutoHotKey](https://github.com/Lexikos/AutoHotkey_L) to set user-defined keyboard
shortcuts. shortcuts.
## Description ## Description
*komorebi* only responds to [WinEvents](https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants) and the _komorebi_ only responds to [WinEvents](https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants) and the
messages it receives on a dedicated socket. messages it receives on a dedicated socket.
*komorebic* is a CLI that writes messages on *komorebi*'s socket. _komorebic_ is a CLI that writes messages on _komorebi_'s socket.
*komorebi* doesn't handle any keyboard or mouse inputs; a third party program (e.g. AutoHotKey) is needed in order to _komorebi_ doesn't handle any keyboard or mouse inputs; a third party program (e.g. AutoHotKey) is needed in order to
translate keyboard and mouse events to *komorebic* commands. translate keyboard and mouse events to _komorebic_ commands.
This architecture, popularised by [*bspwm*](https://github.com/baskerville/bspwm) on Linux and This architecture, popularised by [_bspwm_](https://github.com/baskerville/bspwm) on Linux and
[*yabai*](https://github.com/koekeishiya/yabai) on macOS, is outlined as follows: [_yabai_](https://github.com/koekeishiya/yabai) on macOS, is outlined as follows:
``` ```
PROCESS SOCKET PROCESS SOCKET
@@ -34,15 +34,15 @@ ahk --------> komorebic <------> komorebi
## Design ## Design
*komorebi* is the successor to [*yatta*](https://github.com/LGUG2Z/yatta) and as such aims to build on the learnings _komorebi_ is the successor to [_yatta_](https://github.com/LGUG2Z/yatta) and as such aims to build on the learnings
from that project. from that project.
While *yatta* was primary an attempt to learn how to work with and call Windows APIs from Rust, while secondarily While _yatta_ was primary an attempt to learn how to work with and call Windows APIs from Rust, while secondarily
implementing a minimal viable tiling window manager for my own needs (largely single monitor, single workspace), implementing a minimal viable tiling window manager for my own needs (largely single monitor, single workspace),
*komorebi* has been redesigned from the ground-up to support more complex features that have become standard in tiling _komorebi_ has been redesigned from the ground-up to support more complex features that have become standard in tiling
window managers on other platforms. window managers on other platforms.
*komorebi* holds a list of physical monitors. _komorebi_ holds a list of physical monitors.
A monitor is just a rectangle of the available work area which contains one or more virtual workspaces. A monitor is just a rectangle of the available work area which contains one or more virtual workspaces.
@@ -52,16 +52,16 @@ A container is just a rectangle where one or more application windows can be dis
This means that: This means that:
* Every monitor has its own collection of virtual workspaces - Every monitor has its own collection of virtual workspaces
* Workspaces only know about containers and their dimensions, not about individual application windows - Workspaces only know about containers and their dimensions, not about individual application windows
* Every application window must belong to a container, even if that container only contains one application window - Every application window must belong to a container, even if that container only contains one application window
* Many application windows can be stacked and cycled through in the same container within a workspace - Many application windows can be stacked and cycled through in the same container within a workspace
## Getting Started ## Getting Started
This project is still heavily under development and there are no prebuilt binaries available yet. This project is still heavily under development and there are no prebuilt binaries available yet.
If you would like to use *komorebi*, you will need If you would like to use _komorebi_, you will need
a [working Rust development environment on Windows 10](https://rustup.rs/). If you are using a [working Rust development environment on Windows 10](https://rustup.rs/). If you are using
the `x86_64-pc-windows-msvc` toolchain, make sure you have also installed the `x86_64-pc-windows-msvc` toolchain, make sure you have also installed
the [Build Tools for Visual Studio 2019](https://stackoverflow.com/a/55603112). the [Build Tools for Visual Studio 2019](https://stackoverflow.com/a/55603112).
@@ -100,6 +100,7 @@ sample [komorebi.ahk](komorebi.sample.ahk) AHK script that you can use as a star
- [x] Move focused window container in direction - [x] Move focused window container in direction
- [x] Move focused window container to monitor - [x] Move focused window container to monitor
- [x] Move focused window container to workspace - [x] Move focused window container to workspace
- [x] Mouse follows focused container
- [x] Resize window container in direction - [x] Resize window container in direction
- [ ] Resize child window containers by split ratio - [ ] Resize child window containers by split ratio
- [x] Mouse drag to swap window container position - [x] Mouse drag to swap window container position
@@ -113,6 +114,7 @@ sample [komorebi.ahk](komorebi.sample.ahk) AHK script that you can use as a star
- [x] Floating rules based on window class - [x] Floating rules based on window class
- [x] Toggle floating windows - [x] Toggle floating windows
- [x] Toggle monocle window - [x] Toggle monocle window
- [x] Toggle focus follows mouse
- [x] Pause all window management - [x] Pause all window management
- [x] View window manager state - [x] View window manager state
@@ -121,15 +123,15 @@ sample [komorebi.ahk](komorebi.sample.ahk) AHK script that you can use as a star
If you would like to contribute code to this repository, there are a few requests that I have to ensure a foundation of If you would like to contribute code to this repository, there are a few requests that I have to ensure a foundation of
code quality, consistency and commit hygiene: code quality, consistency and commit hygiene:
* Flatten all `use` statements except in `bindings/build.rs` - Flatten all `use` statements except in `bindings/build.rs`
* Run `cargo +nightly clippy` and ensure that all lints and suggestions have been addressed before committing - Run `cargo +nightly clippy` and ensure that all lints and suggestions have been addressed before committing
* Run `cargo +nightly fmt --all` to ensure consistent formatting before committing - Run `cargo +nightly fmt --all` to ensure consistent formatting before committing
* Use `git cz` with - Use `git cz` with
the [Commitizen CLI](https://github.com/commitizen/cz-cli#conventional-commit-messages-as-a-global-utility) to prepare the [Commitizen CLI](https://github.com/commitizen/cz-cli#conventional-commit-messages-as-a-global-utility) to prepare
commit messages commit messages
* Provide at least one short sentence or paragraph in your commit message body to describe your thought process for the - Provide at least one short sentence or paragraph in your commit message body to describe your thought process for the
changes being committed changes being committed
## Logs and Debugging ## Logs and Debugging
Logs from `komorebi` will be appended to `~/komorebi.log`; this file is never rotated or overwritten, so it will keep Logs from `komorebi` will be appended to `~/komorebi.log`; this file is never rotated or overwritten, so it will keep
@@ -261,7 +263,7 @@ like [Stackline](https://github.com/AdamWagner/stackline) for Windows, you could
"layout_flip": null, "layout_flip": null,
"workspace_padding": 10, "workspace_padding": 10,
"container_padding": 10 "container_padding": 10
}, }
], ],
"focused": 0 "focused": 0
} }
@@ -271,4 +273,4 @@ like [Stackline](https://github.com/AdamWagner/stackline) for Windows, you could
}, },
"is_paused": false "is_paused": false
} }
``` ```

View File

@@ -53,6 +53,7 @@ pub enum SocketMessage {
FloatExe(String), FloatExe(String),
FloatTitle(String), FloatTitle(String),
State, State,
FocusFollowsMouse(bool),
} }
impl SocketMessage { impl SocketMessage {

View File

@@ -1,3 +1,6 @@
; Enable focus follows mouse
Run, komorebic.exe focus-follows-mouse enable
; Ensure there are 3 workspaces created on monitor 0 ; Ensure there are 3 workspaces created on monitor 0
Run, komorebic.exe ensure-workspaces 0 4 Run, komorebic.exe ensure-workspaces 0 4

View File

@@ -13,6 +13,7 @@ use uds_windows::UnixStream;
use komorebi_core::SocketMessage; use komorebi_core::SocketMessage;
use crate::window_manager::WindowManager; use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::FLOAT_CLASSES; use crate::FLOAT_CLASSES;
use crate::FLOAT_EXES; use crate::FLOAT_EXES;
use crate::FLOAT_TITLES; use crate::FLOAT_TITLES;
@@ -153,6 +154,13 @@ impl WindowManager {
SocketMessage::ResizeWindow(direction, sizing) => { SocketMessage::ResizeWindow(direction, sizing) => {
self.resize_window(direction, sizing, Option::from(50))?; self.resize_window(direction, sizing, Option::from(50))?;
} }
SocketMessage::FocusFollowsMouse(enable) => {
if enable {
WindowsApi::enable_focus_follows_mouse()?;
} else {
WindowsApi::disable_focus_follows_mouse()?;
}
}
} }
tracing::info!("processed"); tracing::info!("processed");

View File

@@ -1,6 +1,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
use std::ffi::c_void;
use color_eyre::eyre::ContextCompat; use color_eyre::eyre::ContextCompat;
use color_eyre::Result; use color_eyre::Result;
@@ -56,6 +57,7 @@ use bindings::Windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW; use bindings::Windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SetWindowPos; use bindings::Windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use bindings::Windows::Win32::UI::WindowsAndMessaging::ShowWindow; use bindings::Windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE; use bindings::Windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GWL_STYLE; use bindings::Windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT; use bindings::Windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
@@ -63,8 +65,12 @@ use bindings::Windows::Win32::UI::WindowsAndMessaging::HWND_NOTOPMOST;
use bindings::Windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST; use bindings::Windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS; use bindings::Windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD; use bindings::Windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_HIDE; use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_RESTORE; use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX; use bindings::Windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC; use bindings::Windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use komorebi_core::Rect; use komorebi_core::Rect;
@@ -519,4 +525,33 @@ impl WindowsApi {
monitor_info.rcWork.into(), monitor_info.rcWork.into(),
)) ))
} }
pub fn system_parameters_info_w(
action: SYSTEM_PARAMETERS_INFO_ACTION,
ui_param: u32,
pv_param: *mut c_void,
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
SystemParametersInfoW(action, ui_param, pv_param, update_flags)
}))
}
pub fn enable_focus_follows_mouse() -> Result<()> {
Self::system_parameters_info_w(
SPI_SETACTIVEWINDOWTRACKING,
0,
1 as *mut c_void,
SPIF_SENDCHANGE,
)
}
pub fn disable_focus_follows_mouse() -> Result<()> {
Self::system_parameters_info_w(
SPI_SETACTIVEWINDOWTRACKING,
0,
std::ptr::null_mut::<c_void>(),
SPIF_SENDCHANGE,
)
}
} }

View File

@@ -60,6 +60,7 @@ enum SubCommand {
AdjustContainerPadding(SizingAdjustment), AdjustContainerPadding(SizingAdjustment),
AdjustWorkspacePadding(SizingAdjustment), AdjustWorkspacePadding(SizingAdjustment),
FlipLayout(LayoutFlip), FlipLayout(LayoutFlip),
FocusFollowsMouse(BooleanState),
} }
#[derive(Clap)] #[derive(Clap)]
@@ -111,6 +112,12 @@ struct Resize {
sizing: Sizing, sizing: Sizing,
} }
#[derive(Clap)]
enum BooleanState {
Enable,
Disable,
}
pub fn send_message(bytes: &[u8]) -> Result<()> { pub fn send_message(bytes: &[u8]) -> Result<()> {
let mut socket = dirs::home_dir().context("there is no home directory")?; let mut socket = dirs::home_dir().context("there is no home directory")?;
socket.push("komorebi.sock"); socket.push("komorebi.sock");
@@ -322,6 +329,15 @@ fn main() -> Result<()> {
.unwrap(); .unwrap();
send_message(&*bytes)?; send_message(&*bytes)?;
} }
SubCommand::FocusFollowsMouse(enable) => {
let enable = match enable {
BooleanState::Enable => true,
BooleanState::Disable => false,
};
let bytes = SocketMessage::FocusFollowsMouse(enable).as_bytes().unwrap();
send_message(&*bytes)?;
}
} }
Ok(()) Ok(())