Compare commits

..

6 Commits

Author SHA1 Message Date
LGUG2Z
a63490e88a feat(client): introduce komorebi-client crate
This commit introduces the komorebi-client library crate for other Rust
applications to interact with a running instance of komorebi over Unix
Domain Sockets.

Currently the crate re-exports everything one might find in the
komorebi::State struct and everything that is publicly exposed in
komorebi-core.

Public types and methods are still lacking documentation, and this crate
should not be published on crates.io until this is no longer the case.
2024-02-26 18:15:40 -08:00
James Tucker
e5cf042ea9 fix(komorebi): fix unsound use of transmute
This was causing a crash in release mode when moving a window around by
titlebar.
2024-02-26 11:00:34 -08:00
James Tucker
c435f84afc fix(komorebi): remove some warnings that just snuck in 2024-02-25 22:31:39 -08:00
LGUG2Z
de0db4d014 docs(readme): contributing, uds subs + rust client
This commit adds some contribution guidelines and updates the "Window
Manager Event Subscriptions" section with information on using
subscribe-socket and komorebi-client.
2024-02-25 21:53:11 -08:00
James Tucker
0afcf6d86a fix(komorebi): don't scale for DPI, as we're not DPI aware
This fixes a regression from 344e6ad2fd
that assumed we were declared DPI aware.
2024-02-25 19:01:48 -08:00
LGUG2Z
e0e3afa5b9 feat(config): update border opts, add deprecations
This commit includes backwards-compatible renames of
active_window_border_offset and active_window_border_width to
border_offset and border_width respectively.

Since the invisible window border adjustments were removed in #678, the
invisible window borders set by the OS can now also be adjusted via
border_offset and border_width, which is the primary reason for the
rename.

invisible_borders has been marked as deprecated and the previously
deprecated alt_focus_hack option has been removed.
2024-02-25 17:41:49 -08:00
17 changed files with 422 additions and 77 deletions

10
Cargo.lock generated
View File

@@ -841,6 +841,16 @@ dependencies = [
"winreg 0.52.0",
]
[[package]]
name = "komorebi-client"
version = "0.1.22-dev.0"
dependencies = [
"komorebi",
"komorebi-core",
"serde_json",
"uds_windows",
]
[[package]]
name = "komorebi-core"
version = "0.1.22-dev.0"

View File

@@ -4,6 +4,7 @@ resolver = "2"
members = [
"derive-ahk",
"komorebi",
"komorebi-client",
"komorebi-core",
"komorebic",
"komorebic-no-console",

200
README.md
View File

@@ -101,20 +101,76 @@ widget enabled. The original video can be viewed
https://user-images.githubusercontent.com/13164844/163496414-a9cde3d1-b8a7-4a7a-96fb-a8985380bc70.mp4
# Development
# Contribution Guidelines
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:
If you would like to contribute to `komorebi` please take the time to carefully read the guidelines below.
## Commit hygiene
- Flatten all `use` statements
- Run `cargo +nightly clippy` and ensure that all lints and suggestions have been addressed before committing
- Run `cargo +stable clippy` and ensure that all lints and suggestions have been addressed before committing
- Run `cargo +nightly fmt --all` to ensure consistent formatting before committing
- Use `git cz` with
the [Commitizen CLI](https://github.com/commitizen/cz-cli#conventional-commit-messages-as-a-global-utility) to prepare
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
## PRs should contain only a single feature or bug fix
It is very difficult to review pull requests which touch multiple unrelated features and parts of the codebase.
Please do not submit pull requests like this; you will be asked to separate them into smaller PRs that deal only with
one feature or bug fix at a time.
If you are working on multiple features and bug fixes, I suggest that you cut a branch called `local-trunk`
from `master` which you keep up to date, and rebase the various independent branches you are working on onto that branch
if you want to test them together or create a build with everything integrated.
## Refactors to the codebase must have prior approval
`komorebi` is a mature codebase with an internal consistency and structure that has developed organically over close to
half a decade.
There are [countless hours of live coding videos](https://youtube.com/@LGUG2Z) demonstrating work on this project and
showing new contributors how to do everything from basic tasks like implementing new `komorebic` commands to
distinguishing monitors by manufacturer hardware identifiers and video card ports.
Refactors to the structure of the codebase are not taken lightly and require prior discussion and approval.
Please do not start refactoring the codebase with the expectation of having your changes integrated until you receive an
explicit approval or a request to do so.
Similarly, when implementing features and bug fixes, please stick to the structure of the codebase as much as possible
and do not take this as an opportunity to do some "refactoring along the way".
It is extremely difficult to review PRs for features and bug fixes if they are lost in sweeping changes to the structure
of the codebase.
## Breaking changes to user-facing interfaces are unacceptable
This includes but is not limited to:
- All `komorebic` commands
- The `komorebi.json` schema
- The [`komorebi-application-specific-configuration`](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
schema
No user should ever find that their configuration file has stopped working after upgrading to a new version
of `komorebi`.
More often than not there are ways to reformulate changes that may initially seem like they require breaking user-facing
interfaces into additive changes.
For some inspiration please take a look
at [this commit](https://github.com/LGUG2Z/komorebi/commit/e7d928a065eb63bb4ea1fb864c69c1cae8cc763b) which added the
ability for users to specify colours in `komorebi.json` in Hex format alongside RGB.
There is also a process in place for graceful, non-breaking, deprecation of configuration options that are no longer
required.
# Development
If you use IntelliJ, you should enable the following settings to ensure that code generated by macros is recognised by
the IDE for completions and navigation:
@@ -153,20 +209,21 @@ found, information about it will appear in the log which can be shared when open
# Window Manager State and Integrations
The current state of the window manager can be queried using the `komorebic state` command, which returns a JSON
representation of the `State` struct, which includes the current state of `WindowManager`.
representation of the `State` struct.
This may also be polled to build further integrations and widgets on top of (if you ever wanted to build something
like [Stackline](https://github.com/AdamWagner/stackline) for Windows, you could do it by polling this command).
This may also be polled to build further integrations and widgets on top of.
# Window Manager Event Subscriptions
It is also possible to subscribe to notifications of every `WindowManagerEvent` and `SocketMessage` handled
## Named Pipes
It is possible to subscribe to notifications of every `WindowManagerEvent` and `SocketMessage` handled
by `komorebi` using [Named Pipes](https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes).
First, your application must create a named pipe. Once the named pipe has been created, run the following command:
```powershell
komorebic.exe subscribe <your pipe name>
komorebic.exe subscribe-pipe <your pipe name>
```
Note that you do not have to include the full path of the named pipe, just the name.
@@ -190,12 +247,125 @@ You may then filter on the `type` key to listen to the events that you are inter
notification types, refer to the enum variants of `WindowManagerEvent` in `komorebi` and `SocketMessage`
in `komorebi-core`.
An example of how to create a named pipe and a subscription to `komorebi`'s handled events in Python
by [@denBot](https://github.com/denBot) can be
found [here](https://gist.github.com/denBot/4136279812f87819f86d99eba77c1ee0).
Below is an example of how you can subscribe to and filter on events using a named pipe in `nodejs`.
An example of how to create a named pipe and a subscription to `komorebi`'s handled events in Rust can also be found
in the [`komokana`](https://github.com/LGUG2Z/komokana) repository.
```javascript
const { exec } = require("child_process");
const net = require("net");
const pipeName = "\\\\.\\pipe\\komorebi-js";
const server = net.createServer((stream) => {
console.log("Client connected");
// Every time there is a workspace-related event, let's log the names of all
// workspaces on the currently focused monitor, and then log the name of the
// currently focused workspace on that monitor
stream.on("data", (data) => {
let json = JSON.parse(data.toString());
let event = json.event;
if (event.type.includes("Workspace")) {
let monitors = json.state.monitors;
let current_monitor = monitors.elements[monitors.focused];
let workspaces = monitors.elements[monitors.focused].workspaces;
let current_workspace = workspaces.elements[workspaces.focused];
console.log(
workspaces.elements
.map((workspace) => workspace.name)
.filter((name) => name !== null)
);
console.log(current_workspace.name);
}
});
stream.on("end", () => {
console.log("Client disconnected");
});
});
server.listen(pipeName, () => {
console.log("Named pipe server listening");
});
const command = "komorebic subscribe-pipe komorebi-js";
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing command: ${error}`);
return;
}
});
```
## Unix Domain Sockets
It is possible to subscribe to notifications of every `WindowManagerEvent` and `SocketMessage` handled
by `komorebi` using [Unix Domain Sockets](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/).
UDS are also the only mode of communication between `komorebi` and `komorebic`.
First, your application must create a socket in `$ENV:LocalAppData\komorebi`. Once the socket has been created, run the
following command:
```powershell
komorebic.exe subscribe-socket <your socket name>
```
If the socket exists, komorebi will start pushing JSON data of successfully handled events and messages as in the
example above in the Named Pipes section.
## Rust Client
As of `v0.1.22` it is possible to use the `komorebi-client` crate to subscribe to notifications of
every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust codebase.
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
```rust
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.22"}
use anyhow::Result;
use komorebi_client::Notification;
use komorebi_client::NotificationEvent;
use komorebi_client::UnixListener;
use komorebi_client::WindowManagerEvent;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
pub fn main() -> anyhow::Result<()> {
let socket = komorebi_client::subscribe(NAME)?;
for incoming in socket.incoming() {
match incoming {
Ok(data) => {
let reader = BufReader::new(data.try_clone()?);
for line in reader.lines().flatten() {
let notification: Notification = match serde_json::from_str(&line) {
Ok(notification) => notification,
Err(error) => {
log::debug!("discarding malformed komorebi notification: {error}");
continue;
}
};
// match and filter on desired notifications
}
}
Err(error) => {
log::debug!("{error}");
}
}
}
}
```
A read-world example can be found
in [komokana](https://github.com/LGUG2Z/komokana/blob/feature/komorebi-uds/src/main.rs).
## Subscription Event Notification Schema

View File

@@ -0,0 +1,12 @@
[package]
name = "komorebi-client"
version = "0.1.22-dev.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
komorebi = { path = "../komorebi" }
komorebi-core = { path = "../komorebi-core" }
uds_windows = "1"
serde_json = "1"

View File

@@ -0,0 +1,79 @@
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
#![allow(clippy::missing_errors_doc)]
pub use komorebi::container::Container;
pub use komorebi::monitor::Monitor;
pub use komorebi::ring::Ring;
pub use komorebi::window::Window;
pub use komorebi::window_manager_event::WindowManagerEvent;
pub use komorebi::workspace::Workspace;
pub use komorebi::Notification;
pub use komorebi::NotificationEvent;
pub use komorebi::State;
pub use komorebi_core::Arrangement;
pub use komorebi_core::Axis;
pub use komorebi_core::CustomLayout;
pub use komorebi_core::CycleDirection;
pub use komorebi_core::DefaultLayout;
pub use komorebi_core::Direction;
pub use komorebi_core::Layout;
pub use komorebi_core::OperationDirection;
pub use komorebi_core::Rect;
pub use komorebi_core::SocketMessage;
use komorebi::DATA_DIR;
use std::io::BufReader;
use std::io::Read;
use std::io::Write;
use std::net::Shutdown;
pub use uds_windows::UnixListener;
use uds_windows::UnixStream;
const KOMOREBI: &str = "komorebi.sock";
pub fn send_message(message: &SocketMessage) -> std::io::Result<()> {
let socket = DATA_DIR.join(KOMOREBI);
let mut connected = false;
while !connected {
if let Ok(mut stream) = UnixStream::connect(&socket) {
connected = true;
stream.write_all(serde_json::to_string(message)?.as_bytes())?;
}
}
Ok(())
}
pub fn send_query(message: &SocketMessage) -> std::io::Result<String> {
let socket = DATA_DIR.join(KOMOREBI);
let mut stream = UnixStream::connect(socket)?;
stream.write_all(serde_json::to_string(message)?.as_bytes())?;
stream.shutdown(Shutdown::Write)?;
let mut reader = BufReader::new(stream);
let mut response = String::new();
reader.read_to_string(&mut response)?;
Ok(response)
}
pub fn subscribe(name: &str) -> std::io::Result<UnixListener> {
let socket = DATA_DIR.join(name);
match std::fs::remove_file(&socket) {
Ok(()) => {}
Err(error) => match error.kind() {
std::io::ErrorKind::NotFound => {}
_ => {
return Err(error);
}
},
};
let listener = UnixListener::bind(&socket)?;
send_message(&SocketMessage::AddSubscriberSocket(name.to_string()))?;
Ok(listener)
}

View File

@@ -86,11 +86,7 @@ impl Border {
}
}
pub fn set_position(
self,
window: Window,
activate: bool,
) -> Result<()> {
pub fn set_position(self, window: Window, activate: bool) -> Result<()> {
if self.hwnd == 0 {
Ok(())
} else {

View File

@@ -170,7 +170,7 @@ lazy_static! {
}
})
};
static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
pub static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
pub static ref AHK_EXE: String = {
let mut ahk: String = String::from("autohotkey.exe");
@@ -205,7 +205,6 @@ pub static DEFAULT_CONTAINER_PADDING: AtomicI32 = AtomicI32::new(10);
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
pub static ALT_FOCUS_HACK: AtomicBool = AtomicBool::new(false);
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);

View File

@@ -192,10 +192,7 @@ impl Monitor {
self.workspaces().len()
}
pub fn update_focused_workspace(
&mut self,
offset: Option<Rect>,
) -> Result<()> {
pub fn update_focused_workspace(&mut self, offset: Option<Rect>) -> Result<()> {
let work_area = *self.work_area_size();
let offset = if self.work_area_offset().is_some() {
self.work_area_offset()

View File

@@ -48,7 +48,6 @@ use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::ALT_FOCUS_HACK;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_MONOCLE;
use crate::BORDER_COLOUR_SINGLE;
@@ -1252,8 +1251,8 @@ impl WindowManager {
BORDER_OFFSET.store(offset, Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::AltFocusHack(enable) => {
ALT_FOCUS_HACK.store(enable, Ordering::SeqCst);
SocketMessage::AltFocusHack(_) => {
tracing::info!("this action is deprecated");
}
SocketMessage::ApplicationSpecificConfigurationSchema => {
let asc = schema_for!(Vec<ApplicationConfiguration>);

View File

@@ -376,8 +376,7 @@ impl WindowManager {
// If we have moved across the monitors, use that override, otherwise determine
// if a move has taken place by ruling out a resize
let is_move = moved_across_monitors
|| resize.right == 0 && resize.bottom == 0;
let is_move = moved_across_monitors || resize.right == 0 && resize.bottom == 0;
if is_move {
tracing::info!("moving with mouse");

View File

@@ -214,7 +214,7 @@ impl From<&Monitor> for MonitorConfig {
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
/// The `komorebi.json` static configuration file reference for `v0.1.20`
pub struct StaticConfig {
/// Dimensions of Windows' own invisible borders; don't set these yourself unless you are told to
/// DEPRECATED from v0.1.22: no longer required
#[serde(skip_serializing_if = "Option::is_none")]
pub invisible_borders: Option<Rect>,
/// Delta to resize windows by (default 50)
@@ -238,12 +238,14 @@ pub struct StaticConfig {
/// Path to applications.yaml from komorebi-application-specific-configurations (default: None)
#[serde(skip_serializing_if = "Option::is_none")]
pub app_specific_configuration_path: Option<PathBuf>,
/// Width of the active window border (default: 20)
/// Width of the window border (default: 8)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_width: Option<i32>,
/// Offset of the active window border (default: None)
#[serde(alias = "active_window_border_width")]
pub border_width: Option<i32>,
/// Offset of the window border (default: -1)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_offset: Option<i32>,
#[serde(alias = "active_window_border_offset")]
pub border_offset: Option<i32>,
/// Display an active window border (default: false)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border: Option<bool>,
@@ -259,10 +261,6 @@ pub struct StaticConfig {
/// Monitor and workspace configurations
#[serde(skip_serializing_if = "Option::is_none")]
pub monitors: Option<Vec<MonitorConfig>>,
/// DEPRECATED from v0.1.20: no longer required
#[schemars(skip)]
#[serde(skip_serializing)]
pub alt_focus_hack: Option<bool>,
/// Which Windows signal to use when hiding windows (default: minimize)
#[serde(skip_serializing_if = "Option::is_none")]
pub window_hiding_behaviour: Option<HidingBehaviour>,
@@ -377,8 +375,8 @@ impl From<&WindowManager> for StaticConfig {
focus_follows_mouse: value.focus_follows_mouse,
mouse_follows_focus: Option::from(value.mouse_follows_focus),
app_specific_configuration_path: None,
active_window_border_width: Option::from(BORDER_WIDTH.load(Ordering::SeqCst)),
active_window_border_offset: Option::from(BORDER_OFFSET.load(Ordering::SeqCst)),
border_width: Option::from(BORDER_WIDTH.load(Ordering::SeqCst)),
border_offset: Option::from(BORDER_OFFSET.load(Ordering::SeqCst)),
active_window_border: Option::from(BORDER_ENABLED.load(Ordering::SeqCst)),
active_window_border_colours: border_colours,
default_workspace_padding: Option::from(
@@ -388,7 +386,6 @@ impl From<&WindowManager> for StaticConfig {
DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst),
),
monitors: Option::from(monitors),
alt_focus_hack: None,
window_hiding_behaviour: Option::from(*HIDING_BEHAVIOUR.lock()),
global_work_area_offset: value.work_area_offset,
float_rules: None,
@@ -429,7 +426,7 @@ impl StaticConfig {
DEFAULT_WORKSPACE_PADDING.store(workspace, Ordering::SeqCst);
}
self.active_window_border_width.map_or_else(
self.border_width.map_or_else(
|| {
BORDER_WIDTH.store(8, Ordering::SeqCst);
},
@@ -438,7 +435,7 @@ impl StaticConfig {
},
);
BORDER_OFFSET.store(self.active_window_border_offset.unwrap_or(-1), Ordering::SeqCst);
BORDER_OFFSET.store(self.border_offset.unwrap_or(-1), Ordering::SeqCst);
if let Some(colours) = &self.active_window_border_colours {
BORDER_COLOUR_SINGLE.store(u32::from(colours.single), Ordering::SeqCst);
@@ -895,6 +892,12 @@ impl StaticConfig {
wm.focus_follows_mouse = value.focus_follows_mouse;
let monitor_count = wm.monitors().len();
for i in 0..monitor_count {
wm.update_focused_workspace_by_monitor_idx(i)?;
}
Ok(())
}
}

View File

@@ -4,7 +4,6 @@ use std::convert::TryFrom;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Write as _;
use std::sync::atomic::Ordering;
use color_eyre::eyre;
use color_eyre::eyre::anyhow;
@@ -19,9 +18,6 @@ use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use windows::Win32::Foundation::HWND;
use winput::press;
use winput::release;
use winput::Vk;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
@@ -31,7 +27,6 @@ use crate::styles::ExtendedWindowStyle;
use crate::styles::WindowStyle;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::ALT_FOCUS_HACK;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDDEN_HWNDS;
use crate::HIDING_BEHAVIOUR;
@@ -143,11 +138,7 @@ impl Window {
)
}
pub fn set_position(
&mut self,
layout: &Rect,
top: bool,
) -> Result<()> {
pub fn set_position(&mut self, layout: &Rect, top: bool) -> Result<()> {
let rect = *layout;
WindowsApi::position_window(self.hwnd(), &rect, top)
}
@@ -298,12 +289,7 @@ impl Window {
let mut tried_resetting_foreground_access = false;
let mut max_attempts = 10;
let hotkey_uses_alt = WindowsApi::alt_is_pressed();
while !foregrounded && max_attempts > 0 {
if ALT_FOCUS_HACK.load(Ordering::SeqCst) {
press(Vk::Alt);
}
match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(()) => {
foregrounded = true;
@@ -324,10 +310,6 @@ impl Window {
}
}
};
if ALT_FOCUS_HACK.load(Ordering::SeqCst) && !hotkey_uses_alt {
release(Vk::Alt);
}
}
// Center cursor in Window

View File

@@ -54,8 +54,6 @@ use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
use windows::Win32::UI::HiDpi::GetDpiForMonitor;
use windows::Win32::UI::HiDpi::GetDpiForSystem;
use windows::Win32::UI::HiDpi::GetDpiForWindow;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI;
@@ -352,7 +350,7 @@ impl WindowsApi {
| SetWindowPosition::FRAME_CHANGED;
let shadow_rect = Self::shadow_rect(hwnd)?;
let rect = Rect{
let rect = Rect {
left: layout.left + shadow_rect.left,
top: layout.top + shadow_rect.top,
right: layout.right + shadow_rect.right,
@@ -499,9 +497,11 @@ impl WindowsApi {
let mut rect = unsafe { std::mem::zeroed() };
if Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect).is_ok() {
let window_scale = unsafe { GetDpiForWindow(hwnd) };
let system_scale = unsafe { GetDpiForSystem() };
Ok(Rect::from(rect).scale(system_scale.try_into()?, window_scale.try_into()?))
// TODO(raggi): once we declare DPI awareness, we will need to scale the rect.
// let window_scale = unsafe { GetDpiForWindow(hwnd) };
// let system_scale = unsafe { GetDpiForSystem() };
// Ok(Rect::from(rect).scale(system_scale.try_into()?, window_scale.try_into()?))
Ok(Rect::from(rect))
} else {
unsafe { GetWindowRect(hwnd, &mut rect) }.process()?;
Ok(Rect::from(rect))

View File

@@ -39,6 +39,7 @@ use crate::ring::Ring;
use crate::window::Window;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::winevent_listener;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_RECT;
@@ -186,7 +187,10 @@ pub extern "system" fn win_event_hook(
let window = Window { hwnd: hwnd.0 };
let winevent = unsafe { ::std::mem::transmute(event) };
let winevent = match WinEvent::try_from(event) {
Ok(event) => event,
Err(_) => return,
};
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
None => return,
Some(event) => event,

View File

@@ -178,3 +178,100 @@ pub enum WinEvent {
UiaPropIdSEnd = EVENT_UIA_PROPID_END,
UiaPropIdStart = EVENT_UIA_PROPID_START,
}
impl TryFrom<u32> for WinEvent {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
EVENT_AIA_END => Ok(Self::AiaEnd),
EVENT_AIA_START => Ok(Self::AiaStart),
EVENT_CONSOLE_CARET => Ok(Self::ConsoleCaret),
EVENT_CONSOLE_END => Ok(Self::ConsoleEnd),
EVENT_CONSOLE_END_APPLICATION => Ok(Self::ConsoleEndApplication),
EVENT_CONSOLE_LAYOUT => Ok(Self::ConsoleLayout),
EVENT_CONSOLE_START_APPLICATION => Ok(Self::ConsoleStartApplication),
EVENT_CONSOLE_UPDATE_REGION => Ok(Self::ConsoleUpdateRegion),
EVENT_CONSOLE_UPDATE_SCROLL => Ok(Self::ConsoleUpdateScroll),
EVENT_CONSOLE_UPDATE_SIMPLE => Ok(Self::ConsoleUpdateSimple),
EVENT_OBJECT_ACCELERATORCHANGE => Ok(Self::ObjectAcceleratorChange),
EVENT_OBJECT_CLOAKED => Ok(Self::ObjectCloaked),
EVENT_OBJECT_CONTENTSCROLLED => Ok(Self::ObjectContentScrolled),
EVENT_OBJECT_CREATE => Ok(Self::ObjectCreate),
EVENT_OBJECT_DEFACTIONCHANGE => Ok(Self::ObjectDefActionChange),
EVENT_OBJECT_DESCRIPTIONCHANGE => Ok(Self::ObjectDescriptionChange),
EVENT_OBJECT_DESTROY => Ok(Self::ObjectDestroy),
EVENT_OBJECT_DRAGCANCEL => Ok(Self::ObjectDragCancel),
EVENT_OBJECT_DRAGCOMPLETE => Ok(Self::ObjectDragComplete),
EVENT_OBJECT_DRAGDROPPED => Ok(Self::ObjectDragDropped),
EVENT_OBJECT_DRAGENTER => Ok(Self::ObjectDragEnter),
EVENT_OBJECT_DRAGLEAVE => Ok(Self::ObjectDragLeave),
EVENT_OBJECT_DRAGSTART => Ok(Self::ObjectDragStart),
EVENT_OBJECT_END => Ok(Self::ObjectEnd),
EVENT_OBJECT_FOCUS => Ok(Self::ObjectFocus),
EVENT_OBJECT_HELPCHANGE => Ok(Self::ObjectHelpChange),
EVENT_OBJECT_HIDE => Ok(Self::ObjectHide),
EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED => Ok(Self::ObjectHostedObjectsInvalidated),
EVENT_OBJECT_IME_CHANGE => Ok(Self::ObjectImeChange),
EVENT_OBJECT_IME_HIDE => Ok(Self::ObjectImeHide),
EVENT_OBJECT_IME_SHOW => Ok(Self::ObjectImeShow),
EVENT_OBJECT_INVOKED => Ok(Self::ObjectInvoked),
EVENT_OBJECT_LIVEREGIONCHANGED => Ok(Self::ObjectLiveRegionChanged),
EVENT_OBJECT_LOCATIONCHANGE => Ok(Self::ObjectLocationChange),
EVENT_OBJECT_NAMECHANGE => Ok(Self::ObjectNameChange),
EVENT_OBJECT_PARENTCHANGE => Ok(Self::ObjectParentChange),
EVENT_OBJECT_REORDER => Ok(Self::ObjectReorder),
EVENT_OBJECT_SELECTION => Ok(Self::ObjectSelection),
EVENT_OBJECT_SELECTIONADD => Ok(Self::ObjectSelectionAdd),
EVENT_OBJECT_SELECTIONREMOVE => Ok(Self::ObjectSelectionRemove),
EVENT_OBJECT_SELECTIONWITHIN => Ok(Self::ObjectSelectionWithin),
EVENT_OBJECT_SHOW => Ok(Self::ObjectShow),
EVENT_OBJECT_STATECHANGE => Ok(Self::ObjectStateChange),
EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED => {
Ok(Self::ObjectTextEditConversionTargetChanged)
}
EVENT_OBJECT_TEXTSELECTIONCHANGED => Ok(Self::ObjectTextSelectionChanged),
EVENT_OBJECT_UNCLOAKED => Ok(Self::ObjectUncloaked),
EVENT_OBJECT_VALUECHANGE => Ok(Self::ObjectValueChange),
EVENT_OEM_DEFINED_END => Ok(Self::OemDefinedEnd),
EVENT_OEM_DEFINED_START => Ok(Self::OemDefinedStart),
EVENT_SYSTEM_ALERT => Ok(Self::SystemAlert),
EVENT_SYSTEM_ARRANGMENTPREVIEW => Ok(Self::SystemArrangementPreview),
EVENT_SYSTEM_CAPTUREEND => Ok(Self::SystemCaptureEnd),
EVENT_SYSTEM_CAPTURESTART => Ok(Self::SystemCaptureStart),
EVENT_SYSTEM_CONTEXTHELPEND => Ok(Self::SystemContextHelpEnd),
EVENT_SYSTEM_CONTEXTHELPSTART => Ok(Self::SystemContextHelpStart),
EVENT_SYSTEM_DESKTOPSWITCH => Ok(Self::SystemDesktopSwitch),
EVENT_SYSTEM_DIALOGEND => Ok(Self::SystemDialogEnd),
EVENT_SYSTEM_DIALOGSTART => Ok(Self::SystemDialogStart),
EVENT_SYSTEM_DRAGDROPEND => Ok(Self::SystemDragDropEnd),
EVENT_SYSTEM_DRAGDROPSTART => Ok(Self::SystemDragDropStart),
EVENT_SYSTEM_END => Ok(Self::SystemEnd),
EVENT_SYSTEM_FOREGROUND => Ok(Self::SystemForeground),
EVENT_SYSTEM_IME_KEY_NOTIFICATION => Ok(Self::SystemImeKeyNotification),
EVENT_SYSTEM_MENUEND => Ok(Self::SystemMenuEnd),
EVENT_SYSTEM_MENUPOPUPEND => Ok(Self::SystemMenuPopupEnd),
EVENT_SYSTEM_MENUPOPUPSTART => Ok(Self::SystemMenuPopupStart),
EVENT_SYSTEM_MENUSTART => Ok(Self::SystemMenuStart),
EVENT_SYSTEM_MINIMIZEEND => Ok(Self::SystemMinimizeEnd),
EVENT_SYSTEM_MINIMIZESTART => Ok(Self::SystemMinimizeStart),
EVENT_SYSTEM_MOVESIZEEND => Ok(Self::SystemMoveSizeEnd),
EVENT_SYSTEM_MOVESIZESTART => Ok(Self::SystemMoveSizeStart),
EVENT_SYSTEM_SCROLLINGEND => Ok(Self::SystemScrollingEnd),
EVENT_SYSTEM_SCROLLINGSTART => Ok(Self::SystemScrollingStart),
EVENT_SYSTEM_SOUND => Ok(Self::SystemSound),
EVENT_SYSTEM_SWITCHEND => Ok(Self::SystemSwitchEnd),
EVENT_SYSTEM_SWITCHER_APPDROPPED => Ok(Self::SystemSwitcherAppDropped),
EVENT_SYSTEM_SWITCHER_APPGRABBED => Ok(Self::SystemSwitcherAppGrabbed),
EVENT_SYSTEM_SWITCHER_APPOVERTARGET => Ok(Self::SystemSwitcherAppOverTarget),
EVENT_SYSTEM_SWITCHER_CANCELLED => Ok(Self::SystemSwitcherCancelled),
EVENT_SYSTEM_SWITCHSTART => Ok(Self::SystemSwitchStart),
EVENT_UIA_EVENTID_END => Ok(Self::UiaEventIdSEnd),
EVENT_UIA_EVENTID_START => Ok(Self::UiaEventIdStart),
EVENT_UIA_PROPID_END => Ok(Self::UiaPropIdSEnd),
EVENT_UIA_PROPID_START => Ok(Self::UiaPropIdStart),
_ => Err(()),
}
}
}

View File

@@ -203,11 +203,7 @@ impl Workspace {
Ok(())
}
pub fn update(
&mut self,
work_area: &Rect,
offset: Option<Rect>,
) -> Result<()> {
pub fn update(&mut self, work_area: &Rect, offset: Option<Rect>) -> Result<()> {
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
return Ok(());
}

View File

@@ -1077,8 +1077,9 @@ enum SubCommand {
WatchConfiguration(WatchConfiguration),
/// Signal that the final configuration option has been sent
CompleteConfiguration,
/// Enable or disable a hack simulating ALT key presses to ensure focus changes succeed
/// DEPRECATED since v0.1.22
#[clap(arg_required_else_help = true)]
#[clap(hide = true)]
AltFocusHack(AltFocusHack),
/// Set the window behaviour when switching workspaces / cycling stacks
#[clap(arg_required_else_help = true)]