mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-19 14:26:58 +01:00
Compare commits
48 Commits
feature/ba
...
feature/dp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fe350edff | ||
|
|
26ec574452 | ||
|
|
1c7a5ccb42 | ||
|
|
876439b96b | ||
|
|
d1b1a9e006 | ||
|
|
04791f427b | ||
|
|
e9bccd0316 | ||
|
|
29201b6b94 | ||
|
|
8efce49f2c | ||
|
|
6c022f8d69 | ||
|
|
748659db35 | ||
|
|
91c7f0588c | ||
|
|
60ec439d06 | ||
|
|
ead175ddbc | ||
|
|
5dd3e76602 | ||
|
|
ebcd7ce224 | ||
|
|
b982021573 | ||
|
|
3c84bfd27e | ||
|
|
c874bfc7bf | ||
|
|
85f9c381e5 | ||
|
|
336a4e358f | ||
|
|
4576078b96 | ||
|
|
39971774ea | ||
|
|
7da431081e | ||
|
|
7cc69a4a40 | ||
|
|
5f325a7458 | ||
|
|
34a7b2eb0c | ||
|
|
b08eb0d50c | ||
|
|
005a95b1e6 | ||
|
|
092e36b8b3 | ||
|
|
70be6f4ea4 | ||
|
|
e09d55e71a | ||
|
|
7cef7b53b5 | ||
|
|
8594e72d31 | ||
|
|
bc22ab699f | ||
|
|
b3844af1f3 | ||
|
|
b43f03ce83 | ||
|
|
5c1cfe7b2e | ||
|
|
6269e5972c | ||
|
|
ffa0b0b55e | ||
|
|
66199c5b15 | ||
|
|
aa42a64a48 | ||
|
|
17f1923423 | ||
|
|
5cfc3e831b | ||
|
|
aaf0434053 | ||
|
|
2224479c30 | ||
|
|
957588f60d | ||
|
|
d111d68c0b |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: LGUG2Z
|
||||
2
.github/workflows/windows.yaml
vendored
2
.github/workflows/windows.yaml
vendored
@@ -93,7 +93,7 @@ jobs:
|
||||
if ! type kokai >/dev/null; then cargo install --locked kokai --force; fi
|
||||
kokai release --no-emoji --add-links github:commits,issues --ref "$(git tag --points-at HEAD)" >"CHANGELOG.md"
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
version: latest
|
||||
|
||||
@@ -16,7 +16,7 @@ builds:
|
||||
hooks:
|
||||
post:
|
||||
- mkdir -p dist/windows_amd64
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebi.exe" ".\dist\komorebi_windows_amd64\komorebi.exe"
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebi.exe" ".\dist\komorebi_windows_amd64_v1\komorebi.exe"
|
||||
- id: komorebic
|
||||
main: dummy.go
|
||||
goos: ["windows"]
|
||||
@@ -25,7 +25,7 @@ builds:
|
||||
hooks:
|
||||
post:
|
||||
- mkdir -p dist/windows_amd64
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic.exe" ".\dist\komorebic_windows_amd64\komorebic.exe"
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic.exe" ".\dist\komorebic_windows_amd64_v1\komorebic.exe"
|
||||
|
||||
archives:
|
||||
- replacements:
|
||||
@@ -42,19 +42,4 @@ checksum:
|
||||
name_template: checksums.txt
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
|
||||
scoop:
|
||||
bucket:
|
||||
owner: LGUG2Z
|
||||
name: komorebi-bucket
|
||||
token: "{{ .Env.SCOOP_TOKEN }}"
|
||||
homepage: https://github.com/LGUG2Z/komorebi
|
||||
description: A tiling window manager for Windows
|
||||
license: MIT
|
||||
pre_install:
|
||||
- if (Get-Process -Name komorebi -ErrorAction SilentlyContinue) { komorebic stop }
|
||||
post_install:
|
||||
- Write-Host "`nRun 'cp $original_dir\komorebi.sample.ahk $Env:UserProfile\komorebi.ahk' to get started with the sample configuration"
|
||||
- Write-Host "`nRun 'komorebic ahk-library' if you would like to generate an AHK helper library to use in your configuration"
|
||||
- Write-Host "`nOnce you have a configuration file in place, you can run 'komorebic start' to start the window manager"
|
||||
sort: asc
|
||||
446
Cargo.lock
generated
446
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
37
README.md
37
README.md
@@ -155,6 +155,26 @@ the `AutoHotKey64.exe` executable for AutoHotKey v2 is in your `Path`. If both `
|
||||
exist in your home directory, only `komorebi.ahk` will be loaded. An example of an AutoHotKey v2 configuration file
|
||||
for _komorebi_ can be found [here](https://gist.github.com/crosstyan/dafacc0778dabf693ce9236c57b201cd).
|
||||
|
||||
#### Using Different AHK Executables
|
||||
|
||||
The preferred way to install AutoHotKey for use with `komorebi` is to install it via `scoop`:
|
||||
|
||||
```powershell
|
||||
scoop install autohotkey
|
||||
```
|
||||
|
||||
If you install AutoHotKey using a different method, the name of the executable file may differ from the name given by
|
||||
`scoop`, and thus what is expected by default in `komorebi`.
|
||||
|
||||
You may override the executables that `komorebi` looks for to launch and reload `komorebi.ahk` configuration files using
|
||||
by setting one of the following two environment variables depending on which version of AutoHotKey you wish to use:
|
||||
|
||||
- `$Env:KOMOREBI_AHK_V1_EXE`
|
||||
- `$Env:KOMOREBI_AHK_V2_EXE`
|
||||
|
||||
Please keep in mind that even when setting a custom executable name using these environment variables, the executables
|
||||
are still required to be in your `Path`.
|
||||
|
||||
### Common First-Time Tips
|
||||
|
||||
#### Generating Common Application-Specific Configurations
|
||||
@@ -219,6 +239,15 @@ If you already have configuration files that you wish to keep, move them to the
|
||||
The next time you run `komorebic start`, any files created by or loaded by _komorebi_ will be placed or expected to
|
||||
exist in this folder.
|
||||
|
||||
#### Removing Gaps
|
||||
|
||||
If you would like to remove all gaps from a given workspace, both between windows themselves, and between the monitor edges and the windows, you can set the following two configuration options to `0` for the desired monitors and workspaces:
|
||||
|
||||
```powershell
|
||||
komorebic.exe container-padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
|
||||
komorebic.exe workspace padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
|
||||
```
|
||||
|
||||
#### Floating Windows
|
||||
|
||||
Sometimes you will want a specific application to never be tiled, and instead float all the time. You add add rules to
|
||||
@@ -475,6 +504,8 @@ unmanage Unmanage a window that was forcibly m
|
||||
reload-configuration Reload ~/komorebi.ahk (if it exists)
|
||||
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
|
||||
window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks
|
||||
cross-monitor-move-behaviour Set the behaviour when moving windows across monitor boundaries
|
||||
toggle-cross-monitor-move-behaviour Toggle the behaviour when moving windows across monitor boundaries
|
||||
unmanaged-window-operation-behaviour Set the operation behaviour when the focused window is not managed
|
||||
float-rule Add a rule to always float the specified application
|
||||
manage-rule Add a rule to always manage the specified application
|
||||
@@ -511,7 +542,9 @@ used [is available here](komorebi.sample.with.lib.ahk).
|
||||
- [x] Window stacks
|
||||
- [x] Cycle through stacked windows
|
||||
- [x] Change focused window by direction
|
||||
- [x] Change focused window by direction across monitor boundary
|
||||
- [x] Move focused window container in direction
|
||||
- [x] Move focused window container in direction across monitor boundary
|
||||
- [x] Move focused window container to monitor and follow
|
||||
- [x] Move focused window container to workspace follow
|
||||
- [x] Send focused window container to monitor
|
||||
@@ -584,14 +617,14 @@ the IDE for completions and navigation:
|
||||
|
||||
## 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 `%LOCALAPPDATA%/komorebi/komorebi.log`; this file is never rotated or overwritten, so it will keep
|
||||
growing until it is deleted by the user.
|
||||
|
||||
Whenever running the `komorebic stop` command or sending a Ctrl-C signal to `komorebi` directly, the `komorebi` process
|
||||
ensures that all hidden windows are restored before termination.
|
||||
|
||||
If however, you ever end up with windows that are hidden and cannot be restored, a list of window handles known
|
||||
to `komorebi` are stored and continuously updated in `~/komorebi.hwnd.json`.
|
||||
to `komorebi` are stored and continuously updated in `%LOCALAPPDATA%/komorebi//komorebi.hwnd.json`.
|
||||
|
||||
### Restoring Windows
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-core"
|
||||
version = "0.1.8"
|
||||
version = "0.1.10"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -15,7 +15,7 @@ strum = { version = "0.24", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.35"
|
||||
version = "0.39"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
]
|
||||
|
||||
@@ -57,6 +57,8 @@ pub enum SocketMessage {
|
||||
ToggleMaximize,
|
||||
ToggleWindowContainerBehaviour,
|
||||
WindowHidingBehaviour(HidingBehaviour),
|
||||
ToggleCrossMonitorMoveBehaviour,
|
||||
CrossMonitorMoveBehaviour(MoveBehaviour),
|
||||
UnmanagedWindowOperationBehaviour(OperationBehaviour),
|
||||
// Current Workspace Commands
|
||||
ManageFocusedWindow,
|
||||
@@ -161,6 +163,13 @@ pub enum WindowContainerBehaviour {
|
||||
Append,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum MoveBehaviour {
|
||||
Swap,
|
||||
Insert,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HidingBehaviour {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi"
|
||||
version = "0.1.8"
|
||||
version = "0.1.10"
|
||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||
description = "A tiling window manager for Windows"
|
||||
categories = ["tiling-window-manager", "windows"]
|
||||
@@ -29,7 +29,7 @@ paste = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
strum = { version = "0.24", features = ["derive"] }
|
||||
sysinfo = "0.23"
|
||||
sysinfo = "0.24"
|
||||
tracing = "0.1"
|
||||
tracing-appender = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
@@ -41,16 +41,17 @@ winreg = "0.10"
|
||||
schemars = "0.8"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.35"
|
||||
version = "0.39"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_RemoteDesktop",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_WindowsAndMessaging"
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::redundant_pub_crate)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
@@ -118,6 +118,29 @@ lazy_static! {
|
||||
dirs::home_dir().expect("there is no home directory")
|
||||
}
|
||||
};
|
||||
static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
|
||||
static ref AHK_V1_EXE: String = {
|
||||
let mut ahk_v1: String = String::from("autohotkey.exe");
|
||||
|
||||
if let Ok(komorebi_ahk_v1_exe) = std::env::var("KOMOREBI_AHK_V1_EXE") {
|
||||
if which(&komorebi_ahk_v1_exe).is_ok() {
|
||||
ahk_v1 = komorebi_ahk_v1_exe;
|
||||
}
|
||||
}
|
||||
|
||||
ahk_v1
|
||||
};
|
||||
static ref AHK_V2_EXE: String = {
|
||||
let mut ahk_v2: String = String::from("AutoHotkey64.exe");
|
||||
|
||||
if let Ok(komorebi_ahk_v2_exe) = std::env::var("KOMOREBI_AHK_V2_EXE") {
|
||||
if which(&komorebi_ahk_v2_exe).is_ok() {
|
||||
ahk_v2 = komorebi_ahk_v2_exe;
|
||||
}
|
||||
}
|
||||
|
||||
ahk_v2
|
||||
};
|
||||
}
|
||||
|
||||
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
||||
@@ -134,8 +157,7 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
}
|
||||
|
||||
let home = HOME_DIR.clone();
|
||||
let appender = tracing_appender::rolling::never(home, "komorebi.log");
|
||||
let appender = tracing_appender::rolling::never(DATA_DIR.clone(), "komorebi.log");
|
||||
let color_appender = tracing_appender::rolling::never(std::env::temp_dir(), "komorebi.log");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(appender);
|
||||
let (color_non_blocking, color_guard) = tracing_appender::non_blocking(color_appender);
|
||||
@@ -196,7 +218,7 @@ pub fn load_configuration() -> Result<()> {
|
||||
let mut config_v2 = home;
|
||||
config_v2.push("komorebi.ahk2");
|
||||
|
||||
if config_v1.exists() && which("autohotkey.exe").is_ok() {
|
||||
if config_v1.exists() && which(&*AHK_V1_EXE).is_ok() {
|
||||
tracing::info!(
|
||||
"loading configuration file: {}",
|
||||
config_v1
|
||||
@@ -208,7 +230,7 @@ pub fn load_configuration() -> Result<()> {
|
||||
Command::new("autohotkey.exe")
|
||||
.arg(config_v1.as_os_str())
|
||||
.output()?;
|
||||
} else if config_v2.exists() && which("AutoHotkey64.exe").is_ok() {
|
||||
} else if config_v2.exists() && which(&*AHK_V2_EXE).is_ok() {
|
||||
tracing::info!(
|
||||
"loading configuration file: {}",
|
||||
config_v2
|
||||
@@ -364,14 +386,14 @@ fn main() -> Result<()> {
|
||||
let matched_procs: Vec<&Process> = system.processes_by_name("komorebi.exe").collect();
|
||||
|
||||
if matched_procs.len() > 1 {
|
||||
let mut shim_is_active = false;
|
||||
let mut len = matched_procs.len();
|
||||
for proc in matched_procs {
|
||||
if proc.root().ends_with("shims") {
|
||||
shim_is_active = true;
|
||||
len -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !shim_is_active {
|
||||
if len > 1 {
|
||||
tracing::error!("komorebi.exe is already running, please exit the existing process before starting a new one");
|
||||
std::process::exit(1);
|
||||
}
|
||||
@@ -385,6 +407,7 @@ fn main() -> Result<()> {
|
||||
|
||||
let process_id = WindowsApi::current_process_id();
|
||||
WindowsApi::allow_set_foreground_window(process_id)?;
|
||||
WindowsApi::set_process_dpi_awareness_context()?;
|
||||
|
||||
let (outgoing, incoming): (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) =
|
||||
crossbeam_channel::unbounded();
|
||||
|
||||
@@ -20,6 +20,7 @@ use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::Axis;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::Layout;
|
||||
use komorebi_core::MoveBehaviour;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
use komorebi_core::Sizing;
|
||||
@@ -378,15 +379,40 @@ impl WindowManager {
|
||||
SocketMessage::ResizeWindowAxis(axis, sizing) => {
|
||||
// If the user has a custom layout, allow for the resizing of the primary column
|
||||
// with this signal
|
||||
if let Layout::Custom(ref mut custom) = self.focused_workspace_mut()?.layout_mut() {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let container_len = workspace.containers().len();
|
||||
let no_layout_rules = workspace.layout_rules().is_empty();
|
||||
|
||||
if let Layout::Custom(ref mut custom) = workspace.layout_mut() {
|
||||
if matches!(axis, Axis::Horizontal) {
|
||||
let percentage = custom
|
||||
.primary_width_percentage()
|
||||
.unwrap_or(100 / custom.len());
|
||||
|
||||
match sizing {
|
||||
Sizing::Increase => custom.set_primary_width_percentage(percentage + 5),
|
||||
Sizing::Decrease => custom.set_primary_width_percentage(percentage - 5),
|
||||
if no_layout_rules {
|
||||
match sizing {
|
||||
Sizing::Increase => {
|
||||
custom.set_primary_width_percentage(percentage + 5);
|
||||
}
|
||||
Sizing::Decrease => {
|
||||
custom.set_primary_width_percentage(percentage - 5);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for rule in workspace.layout_rules_mut() {
|
||||
if container_len >= rule.0 {
|
||||
if let Layout::Custom(ref mut custom) = rule.1 {
|
||||
match sizing {
|
||||
Sizing::Increase => {
|
||||
custom.set_primary_width_percentage(percentage + 5);
|
||||
}
|
||||
Sizing::Decrease => {
|
||||
custom.set_primary_width_percentage(percentage - 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise proceed with the resizing logic for individual window containers in the
|
||||
@@ -681,6 +707,19 @@ impl WindowManager {
|
||||
let mut hiding_behaviour = HIDING_BEHAVIOUR.lock();
|
||||
*hiding_behaviour = behaviour;
|
||||
}
|
||||
SocketMessage::ToggleCrossMonitorMoveBehaviour => {
|
||||
match self.cross_monitor_move_behaviour {
|
||||
MoveBehaviour::Swap => {
|
||||
self.cross_monitor_move_behaviour = MoveBehaviour::Insert;
|
||||
}
|
||||
MoveBehaviour::Insert => {
|
||||
self.cross_monitor_move_behaviour = MoveBehaviour::Swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::CrossMonitorMoveBehaviour(behaviour) => {
|
||||
self.cross_monitor_move_behaviour = behaviour;
|
||||
}
|
||||
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
|
||||
self.unmanaged_window_operation_behaviour = behaviour;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::DATA_DIR;
|
||||
use crate::HIDDEN_HWNDS;
|
||||
use crate::HOME_DIR;
|
||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||
|
||||
#[tracing::instrument]
|
||||
@@ -288,6 +288,7 @@ impl WindowManager {
|
||||
.ok_or_else(|| anyhow!("cannot get monitor idx from current position"))?;
|
||||
|
||||
let new_window_behaviour = self.window_container_behaviour;
|
||||
let invisible_borders = self.invisible_borders;
|
||||
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
if workspace
|
||||
@@ -340,7 +341,10 @@ 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
|
||||
|| resize.right.abs() == invisible_borders.right
|
||||
&& resize.bottom.abs() == invisible_borders.bottom;
|
||||
|
||||
if is_move {
|
||||
tracing::info!("moving with mouse");
|
||||
@@ -483,8 +487,7 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
let mut hwnd_json = HOME_DIR.clone();
|
||||
hwnd_json.push("komorebi.hwnd.json");
|
||||
let hwnd_json = DATA_DIR.join("komorebi.hwnd.json");
|
||||
let file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
@@ -36,18 +37,18 @@ impl Display for Window {
|
||||
let mut display = format!("(hwnd: {}", self.hwnd);
|
||||
|
||||
if let Ok(title) = self.title() {
|
||||
display.push_str(&format!(", title: {}", title));
|
||||
write!(display, ", title: {}", title)?;
|
||||
}
|
||||
|
||||
if let Ok(exe) = self.exe() {
|
||||
display.push_str(&format!(", exe: {}", exe));
|
||||
write!(display, ", exe: {}", exe)?;
|
||||
}
|
||||
|
||||
if let Ok(class) = self.class() {
|
||||
display.push_str(&format!(", class: {}", class));
|
||||
write!(display, ", class: {}", class)?;
|
||||
}
|
||||
|
||||
display.push(')');
|
||||
write!(display, ")")?;
|
||||
|
||||
write!(f, "{}", display)
|
||||
}
|
||||
@@ -198,7 +199,19 @@ impl Window {
|
||||
// Attach komorebi thread to Window thread
|
||||
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
|
||||
let current_thread_id = WindowsApi::current_thread_id();
|
||||
WindowsApi::attach_thread_input(current_thread_id, window_thread_id, true)?;
|
||||
|
||||
// This can be allowed to fail if a window doesn't have a message queue or if a journal record
|
||||
// hook has been installed
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput#remarks
|
||||
match WindowsApi::attach_thread_input(current_thread_id, window_thread_id, true) {
|
||||
Ok(()) => {}
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
"could not attach to window thread input processing mechanism, but continuing execution of focus(): {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Raise Window to foreground
|
||||
match WindowsApi::set_foreground_window(self.hwnd()) {
|
||||
@@ -280,23 +293,35 @@ impl Window {
|
||||
// If not allowing cloaked windows, we need to ensure the window is not cloaked
|
||||
(false, false) => {
|
||||
if let (Ok(title), Ok(exe_name), Ok(class)) = (self.title(), self.exe(), self.class()) {
|
||||
let mut should_float = false;
|
||||
|
||||
{
|
||||
let float_identifiers = FLOAT_IDENTIFIERS.lock();
|
||||
if float_identifiers.contains(&title)
|
||||
|| float_identifiers.contains(&exe_name)
|
||||
|| float_identifiers.contains(&class) {
|
||||
return Ok(false);
|
||||
for identifier in float_identifiers.iter() {
|
||||
if title.starts_with(identifier) || title.ends_with(identifier) ||
|
||||
class.starts_with(identifier) || class.ends_with(identifier) ||
|
||||
identifier == &exe_name {
|
||||
should_float = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let managed_override = {
|
||||
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
|
||||
manage_identifiers.contains(&exe_name) || manage_identifiers.contains(&class)
|
||||
manage_identifiers.contains(&exe_name)
|
||||
|| manage_identifiers.contains(&class)
|
||||
|| manage_identifiers.contains(&title)
|
||||
};
|
||||
|
||||
if should_float && !managed_override {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let allow_layered = {
|
||||
let layered_whitelist = LAYERED_WHITELIST.lock();
|
||||
layered_whitelist.contains(&exe_name) || layered_whitelist.contains(&class)
|
||||
layered_whitelist.contains(&exe_name)
|
||||
|| layered_whitelist.contains(&class)
|
||||
|| layered_whitelist.contains(&title)
|
||||
};
|
||||
|
||||
let allow_wsl2_gui = {
|
||||
|
||||
@@ -22,6 +22,7 @@ use komorebi_core::CycleDirection;
|
||||
use komorebi_core::DefaultLayout;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::Layout;
|
||||
use komorebi_core::MoveBehaviour;
|
||||
use komorebi_core::OperationBehaviour;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
@@ -39,6 +40,7 @@ use crate::windows_api::WindowsApi;
|
||||
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
use crate::DATA_DIR;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
use crate::HOME_DIR;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
@@ -57,6 +59,7 @@ pub struct WindowManager {
|
||||
pub work_area_offset: Option<Rect>,
|
||||
pub resize_delta: i32,
|
||||
pub window_container_behaviour: WindowContainerBehaviour,
|
||||
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||
pub unmanaged_window_operation_behaviour: OperationBehaviour,
|
||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||
pub mouse_follows_focus: bool,
|
||||
@@ -73,6 +76,7 @@ pub struct State {
|
||||
pub invisible_borders: Rect,
|
||||
pub resize_delta: i32,
|
||||
pub new_window_behaviour: WindowContainerBehaviour,
|
||||
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||
pub work_area_offset: Option<Rect>,
|
||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||
pub mouse_follows_focus: bool,
|
||||
@@ -100,6 +104,7 @@ impl From<&WindowManager> for State {
|
||||
work_area_offset: wm.work_area_offset,
|
||||
resize_delta: wm.resize_delta,
|
||||
new_window_behaviour: wm.window_container_behaviour,
|
||||
cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour,
|
||||
focus_follows_mouse: wm.focus_follows_mouse.clone(),
|
||||
mouse_follows_focus: wm.mouse_follows_focus,
|
||||
has_pending_raise_op: wm.has_pending_raise_op,
|
||||
@@ -142,10 +147,7 @@ impl EnforceWorkspaceRuleOp {
|
||||
impl WindowManager {
|
||||
#[tracing::instrument]
|
||||
pub fn new(incoming: Arc<Mutex<Receiver<WindowManagerEvent>>>) -> Result<Self> {
|
||||
let home = HOME_DIR.clone();
|
||||
let mut socket = home;
|
||||
socket.push("komorebi.sock");
|
||||
let socket = socket.as_path();
|
||||
let socket = DATA_DIR.join("komorebi.sock");
|
||||
|
||||
match std::fs::remove_file(&socket) {
|
||||
Ok(_) => {}
|
||||
@@ -174,6 +176,7 @@ impl WindowManager {
|
||||
virtual_desktop_id: current_virtual_desktop(),
|
||||
work_area_offset: None,
|
||||
window_container_behaviour: WindowContainerBehaviour::Create,
|
||||
cross_monitor_move_behaviour: MoveBehaviour::Swap,
|
||||
unmanaged_window_operation_behaviour: OperationBehaviour::Op,
|
||||
resize_delta: 50,
|
||||
focus_follows_mouse: None,
|
||||
@@ -266,6 +269,39 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn monitor_index_in_direction(&self, direction: OperationDirection) -> Option<usize> {
|
||||
let current_monitor_size = self.focused_monitor_size().ok()?;
|
||||
|
||||
for (idx, monitor) in self.monitors.elements().iter().enumerate() {
|
||||
match direction {
|
||||
OperationDirection::Left => {
|
||||
if monitor.size().left + monitor.size().right == current_monitor_size.left {
|
||||
return Option::from(idx);
|
||||
}
|
||||
}
|
||||
OperationDirection::Right => {
|
||||
if current_monitor_size.right + current_monitor_size.left == monitor.size().left
|
||||
{
|
||||
return Option::from(idx);
|
||||
}
|
||||
}
|
||||
OperationDirection::Up => {
|
||||
if monitor.size().top + monitor.size().bottom == current_monitor_size.top {
|
||||
return Option::from(idx);
|
||||
}
|
||||
}
|
||||
OperationDirection::Down => {
|
||||
if current_monitor_size.top + current_monitor_size.bottom == monitor.size().top
|
||||
{
|
||||
return Option::from(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn reconcile_monitors(&mut self) -> Result<()> {
|
||||
let valid_hmonitors = WindowsApi::valid_hmonitors()?;
|
||||
@@ -905,13 +941,24 @@ impl WindowManager {
|
||||
|
||||
tracing::info!("focusing container");
|
||||
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let workspace = self.focused_workspace()?;
|
||||
let new_idx = workspace.new_idx_for_direction(direction);
|
||||
|
||||
let new_idx = workspace
|
||||
.new_idx_for_direction(direction)
|
||||
.ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?;
|
||||
// if there is no container in that direction for this workspace
|
||||
match new_idx {
|
||||
None => {
|
||||
let monitor_idx = self
|
||||
.monitor_index_in_direction(direction)
|
||||
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
}
|
||||
Some(idx) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
workspace.focus_container(idx);
|
||||
}
|
||||
}
|
||||
|
||||
workspace.focus_container(new_idx);
|
||||
self.focused_window_mut()?.focus(self.mouse_follows_focus)?;
|
||||
|
||||
Ok(())
|
||||
@@ -923,15 +970,103 @@ impl WindowManager {
|
||||
|
||||
tracing::info!("moving container");
|
||||
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let workspace = self.focused_workspace()?;
|
||||
|
||||
let current_idx = workspace.focused_container_idx();
|
||||
let new_idx = workspace
|
||||
.new_idx_for_direction(direction)
|
||||
.ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?;
|
||||
let origin_container_idx = workspace.focused_container_idx();
|
||||
let origin_monitor_idx = self.focused_monitor_idx();
|
||||
let target_container_idx = workspace.new_idx_for_direction(direction);
|
||||
|
||||
match target_container_idx {
|
||||
// If there is nowhere to move on the current workspace, try to move it onto the monitor
|
||||
// in that direction if there is one
|
||||
None => {
|
||||
let target_monitor_idx = self
|
||||
.monitor_index_in_direction(direction)
|
||||
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
|
||||
|
||||
{
|
||||
// remove the container from the origin monitor workspace
|
||||
let origin_container = self
|
||||
.focused_workspace_mut()?
|
||||
.remove_container_by_idx(origin_container_idx)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("could not remove container at given origin index")
|
||||
})?;
|
||||
|
||||
// focus the target monitor
|
||||
self.focus_monitor(target_monitor_idx)?;
|
||||
|
||||
// get the focused workspace on the target monitor
|
||||
let target_workspace = self.focused_workspace_mut()?;
|
||||
|
||||
// insert the origin container into the focused workspace on the target monitor
|
||||
// at the position where the currently focused container on that workspace is
|
||||
target_workspace.insert_container_at_idx(
|
||||
target_workspace.focused_container_idx(),
|
||||
origin_container,
|
||||
);
|
||||
|
||||
// if there is only one container on the target workspace after the insertion
|
||||
// it means that there won't be one swapped back, so we have to decrement the
|
||||
// focused position
|
||||
if target_workspace.containers().len() == 1 {
|
||||
let origin_workspace =
|
||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||
|
||||
origin_workspace
|
||||
.focus_container(origin_workspace.focused_container_idx() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// if our MoveBehaviour is Swap, let's try to send back the window container
|
||||
// whose position which just took over
|
||||
if matches!(self.cross_monitor_move_behaviour, MoveBehaviour::Swap) {
|
||||
{
|
||||
let target_workspace = self.focused_workspace_mut()?;
|
||||
|
||||
// if the target workspace doesn't have more than one container, this means it
|
||||
// was previously empty, by only doing the second part of the swap when there is
|
||||
// more than one container, we can fall back to a "move" if there is nothing to
|
||||
// swap with on the target monitor
|
||||
if target_workspace.containers().len() > 1 {
|
||||
// remove the container from the target monitor workspace
|
||||
let target_container = target_workspace
|
||||
// this is now focused_container_idx + 1 because we have inserted our origin container
|
||||
.remove_container_by_idx(
|
||||
target_workspace.focused_container_idx() + 1,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("could not remove container at given target index")
|
||||
})?;
|
||||
|
||||
let origin_workspace =
|
||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||
|
||||
// insert the container from the target monitor workspace into the origin monitor workspace
|
||||
// at the same position from which our origin container was removed
|
||||
origin_workspace
|
||||
.insert_container_at_idx(origin_container_idx, target_container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to update the origin monitor workspace layout because it is no
|
||||
// longer focused so it won't get updated at the end of this fn
|
||||
let offset = self.work_area_offset;
|
||||
let invisible_borders = self.invisible_borders;
|
||||
|
||||
self.monitors_mut()
|
||||
.get_mut(origin_monitor_idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.update_focused_workspace(offset, &invisible_borders)?;
|
||||
}
|
||||
Some(new_idx) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
workspace.swap_containers(origin_container_idx, new_idx);
|
||||
workspace.focus_container(new_idx);
|
||||
}
|
||||
}
|
||||
|
||||
workspace.swap_containers(current_idx, new_idx);
|
||||
workspace.focus_container(new_idx);
|
||||
self.update_focused_workspace(self.mouse_follows_focus)
|
||||
}
|
||||
|
||||
@@ -1629,6 +1764,13 @@ impl WindowManager {
|
||||
self.update_focused_workspace(false)
|
||||
}
|
||||
|
||||
pub fn focused_monitor_size(&self) -> Result<Rect> {
|
||||
Ok(*self
|
||||
.focused_monitor()
|
||||
.ok_or_else(|| anyhow!("there is no monitor"))?
|
||||
.size())
|
||||
}
|
||||
|
||||
pub fn focused_monitor_work_area(&self) -> Result<Rect> {
|
||||
Ok(*self
|
||||
.focused_monitor()
|
||||
@@ -1661,7 +1803,7 @@ impl WindowManager {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn monitor_idx_from_current_pos(&mut self) -> Option<usize> {
|
||||
pub fn monitor_idx_from_current_pos(&self) -> Option<usize> {
|
||||
let hmonitor = WindowsApi::monitor_from_point(WindowsApi::cursor_pos().ok()?);
|
||||
|
||||
for (i, monitor) in self.monitors().iter().enumerate() {
|
||||
@@ -1687,6 +1829,22 @@ impl WindowManager {
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
pub fn focused_workspace_for_monitor_idx(&self, idx: usize) -> Result<&Workspace> {
|
||||
self.monitors()
|
||||
.get(idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.focused_workspace()
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
pub fn focused_workspace_for_monitor_idx_mut(&mut self, idx: usize) -> Result<&mut Workspace> {
|
||||
self.monitors_mut()
|
||||
.get_mut(idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.focused_workspace_mut()
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_workspace(&mut self, idx: usize) -> Result<()> {
|
||||
tracing::info!("focusing workspace");
|
||||
|
||||
@@ -28,56 +28,56 @@ pub enum WindowManagerEvent {
|
||||
impl Display for WindowManagerEvent {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WindowManagerEvent::Manage(window) => {
|
||||
Self::Manage(window) => {
|
||||
write!(f, "Manage (Window: {})", window)
|
||||
}
|
||||
WindowManagerEvent::Unmanage(window) => {
|
||||
Self::Unmanage(window) => {
|
||||
write!(f, "Unmanage (Window: {})", window)
|
||||
}
|
||||
WindowManagerEvent::Destroy(winevent, window) => {
|
||||
Self::Destroy(winevent, window) => {
|
||||
write!(f, "Destroy (WinEvent: {}, Window: {})", winevent, window)
|
||||
}
|
||||
WindowManagerEvent::FocusChange(winevent, window) => {
|
||||
Self::FocusChange(winevent, window) => {
|
||||
write!(
|
||||
f,
|
||||
"FocusChange (WinEvent: {}, Window: {})",
|
||||
winevent, window
|
||||
)
|
||||
}
|
||||
WindowManagerEvent::Hide(winevent, window) => {
|
||||
Self::Hide(winevent, window) => {
|
||||
write!(f, "Hide (WinEvent: {}, Window: {})", winevent, window)
|
||||
}
|
||||
WindowManagerEvent::Minimize(winevent, window) => {
|
||||
Self::Minimize(winevent, window) => {
|
||||
write!(f, "Minimize (WinEvent: {}, Window: {})", winevent, window)
|
||||
}
|
||||
WindowManagerEvent::Show(winevent, window) => {
|
||||
Self::Show(winevent, window) => {
|
||||
write!(f, "Show (WinEvent: {}, Window: {})", winevent, window)
|
||||
}
|
||||
WindowManagerEvent::MoveResizeStart(winevent, window) => {
|
||||
Self::MoveResizeStart(winevent, window) => {
|
||||
write!(
|
||||
f,
|
||||
"MoveResizeStart (WinEvent: {}, Window: {})",
|
||||
winevent, window
|
||||
)
|
||||
}
|
||||
WindowManagerEvent::MoveResizeEnd(winevent, window) => {
|
||||
Self::MoveResizeEnd(winevent, window) => {
|
||||
write!(
|
||||
f,
|
||||
"MoveResizeEnd (WinEvent: {}, Window: {})",
|
||||
winevent, window
|
||||
)
|
||||
}
|
||||
WindowManagerEvent::MouseCapture(winevent, window) => {
|
||||
Self::MouseCapture(winevent, window) => {
|
||||
write!(
|
||||
f,
|
||||
"MouseCapture (WinEvent: {}, Window: {})",
|
||||
winevent, window
|
||||
)
|
||||
}
|
||||
WindowManagerEvent::Raise(window) => {
|
||||
Self::Raise(window) => {
|
||||
write!(f, "Raise (Window: {})", window)
|
||||
}
|
||||
WindowManagerEvent::MonitorPoll(winevent, window) => {
|
||||
Self::MonitorPoll(winevent, window) => {
|
||||
write!(
|
||||
f,
|
||||
"MonitorPoll (WinEvent: {}, Window: {})",
|
||||
@@ -91,18 +91,18 @@ impl Display for WindowManagerEvent {
|
||||
impl WindowManagerEvent {
|
||||
pub const fn window(self) -> Window {
|
||||
match self {
|
||||
WindowManagerEvent::Destroy(_, window)
|
||||
| WindowManagerEvent::FocusChange(_, window)
|
||||
| WindowManagerEvent::Hide(_, window)
|
||||
| WindowManagerEvent::Minimize(_, window)
|
||||
| WindowManagerEvent::Show(_, window)
|
||||
| WindowManagerEvent::MoveResizeStart(_, window)
|
||||
| WindowManagerEvent::MoveResizeEnd(_, window)
|
||||
| WindowManagerEvent::MouseCapture(_, window)
|
||||
| WindowManagerEvent::MonitorPoll(_, window)
|
||||
| WindowManagerEvent::Raise(window)
|
||||
| WindowManagerEvent::Manage(window)
|
||||
| WindowManagerEvent::Unmanage(window) => window,
|
||||
Self::Destroy(_, window)
|
||||
| Self::FocusChange(_, window)
|
||||
| Self::Hide(_, window)
|
||||
| Self::Minimize(_, window)
|
||||
| Self::Show(_, window)
|
||||
| Self::MoveResizeStart(_, window)
|
||||
| Self::MoveResizeEnd(_, window)
|
||||
| Self::MouseCapture(_, window)
|
||||
| Self::MonitorPoll(_, window)
|
||||
| Self::Raise(window)
|
||||
| Self::Manage(window)
|
||||
| Self::Unmanage(window) => window,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ use windows::Win32::System::Threading::QueryFullProcessImageNameW;
|
||||
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::SetProcessDpiAwarenessContext;
|
||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
||||
@@ -130,7 +132,7 @@ macro_rules! impl_process_windows_crate_integer_wrapper_result {
|
||||
$(
|
||||
impl ProcessWindowsCrateResult<$deref> for $input {
|
||||
fn process(self) -> Result<$deref> {
|
||||
if self.0 == 0 {
|
||||
if self == $input(0) {
|
||||
Err(std::io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(self.0)
|
||||
@@ -565,6 +567,12 @@ impl WindowsApi {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn set_process_dpi_awareness_context() -> Result<()> {
|
||||
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }
|
||||
.ok()
|
||||
.process()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn system_parameters_info_w(
|
||||
action: SYSTEM_PARAMETERS_INFO_ACTION,
|
||||
|
||||
@@ -88,7 +88,7 @@ use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_EVENTID_START;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_PROPID_END;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_PROPID_START;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Display, JsonSchema)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Display, JsonSchema)]
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
pub enum WinEvent {
|
||||
|
||||
@@ -412,7 +412,11 @@ impl Workspace {
|
||||
self.focus_last_container();
|
||||
}
|
||||
|
||||
fn remove_container_by_idx(&mut self, idx: usize) -> Option<Container> {
|
||||
pub fn insert_container_at_idx(&mut self, idx: usize, container: Container) {
|
||||
self.containers_mut().insert(idx, container);
|
||||
}
|
||||
|
||||
pub fn remove_container_by_idx(&mut self, idx: usize) -> Option<Container> {
|
||||
if idx < self.resize_dimensions().len() {
|
||||
self.resize_dimensions_mut().remove(idx);
|
||||
}
|
||||
|
||||
@@ -256,6 +256,14 @@ WindowHidingBehaviour(hiding_behaviour) {
|
||||
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
|
||||
}
|
||||
|
||||
CrossMonitorMoveBehaviour(move_behaviour) {
|
||||
Run, komorebic.exe cross-monitor-move-behaviour %move_behaviour%, , Hide
|
||||
}
|
||||
|
||||
ToggleCrossMonitorMoveBehaviour() {
|
||||
Run, komorebic.exe toggle-cross-monitor-move-behaviour, , Hide
|
||||
}
|
||||
|
||||
UnmanagedWindowOperationBehaviour(operation_behaviour) {
|
||||
Run, komorebic.exe unmanaged-window-operation-behaviour %operation_behaviour%, , Hide
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebic"
|
||||
version = "0.1.8"
|
||||
version = "0.1.10"
|
||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
|
||||
categories = ["cli", "tiling-window-manager", "windows"]
|
||||
@@ -21,14 +21,14 @@ fs-tail = "0.1"
|
||||
heck = "0.4"
|
||||
lazy_static = "1"
|
||||
paste = "1"
|
||||
powershell_script = "0.3"
|
||||
powershell_script = "1.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.8"
|
||||
uds_windows = "1"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.35"
|
||||
version = "0.39"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_UI_WindowsAndMessaging"
|
||||
|
||||
@@ -36,6 +36,7 @@ use komorebi_core::CycleDirection;
|
||||
use komorebi_core::DefaultLayout;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::MoveBehaviour;
|
||||
use komorebi_core::OperationBehaviour;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
@@ -60,6 +61,9 @@ lazy_static! {
|
||||
dirs::home_dir().expect("there is no home directory")
|
||||
}
|
||||
};
|
||||
static ref DATA_DIR: PathBuf = dirs::data_local_dir()
|
||||
.expect("there is no local data directory")
|
||||
.join("komorebi");
|
||||
}
|
||||
|
||||
trait AhkLibrary {
|
||||
@@ -115,6 +119,7 @@ gen_enum_subcommand_args! {
|
||||
MouseFollowsFocus: BooleanState,
|
||||
Query: StateQuery,
|
||||
WindowHidingBehaviour: HidingBehaviour,
|
||||
CrossMonitorMoveBehaviour: MoveBehaviour,
|
||||
UnmanagedWindowOperationBehaviour: OperationBehaviour,
|
||||
}
|
||||
|
||||
@@ -207,6 +212,7 @@ pub struct WorkspaceLayoutRule {
|
||||
/// The number of window containers on-screen required to trigger this layout rule
|
||||
at_container_count: usize,
|
||||
|
||||
#[clap(arg_enum)]
|
||||
layout: DefaultLayout,
|
||||
}
|
||||
|
||||
@@ -624,6 +630,11 @@ enum SubCommand {
|
||||
/// Set the window behaviour when switching workspaces / cycling stacks
|
||||
#[clap(arg_required_else_help = true)]
|
||||
WindowHidingBehaviour(WindowHidingBehaviour),
|
||||
/// Set the behaviour when moving windows across monitor boundaries
|
||||
#[clap(arg_required_else_help = true)]
|
||||
CrossMonitorMoveBehaviour(CrossMonitorMoveBehaviour),
|
||||
/// Toggle the behaviour when moving windows across monitor boundaries
|
||||
ToggleCrossMonitorMoveBehaviour,
|
||||
/// Set the operation behaviour when the focused window is not managed
|
||||
#[clap(arg_required_else_help = true)]
|
||||
UnmanagedWindowOperationBehaviour(UnmanagedWindowOperationBehaviour),
|
||||
@@ -675,12 +686,9 @@ enum SubCommand {
|
||||
}
|
||||
|
||||
pub fn send_message(bytes: &[u8]) -> Result<()> {
|
||||
let mut socket = HOME_DIR.clone();
|
||||
socket.push("komorebi.sock");
|
||||
let socket = socket.as_path();
|
||||
|
||||
let socket = DATA_DIR.join("komorebi.sock");
|
||||
let mut stream = UnixStream::connect(&socket)?;
|
||||
Ok(stream.write_all(&*bytes)?)
|
||||
Ok(stream.write_all(bytes)?)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
@@ -720,46 +728,47 @@ fn main() -> Result<()> {
|
||||
color_log.push("komorebi.log");
|
||||
let file = TailedFile::new(File::open(color_log)?);
|
||||
let locked = file.lock();
|
||||
for line in locked.lines() {
|
||||
println!("{}", line?);
|
||||
#[allow(clippy::significant_drop_in_scrutinee)]
|
||||
for line in locked.lines().flatten() {
|
||||
println!("{}", line);
|
||||
}
|
||||
}
|
||||
SubCommand::Focus(arg) => {
|
||||
send_message(&*SocketMessage::FocusWindow(arg.operation_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::FocusWindow(arg.operation_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Promote => {
|
||||
send_message(&*SocketMessage::Promote.as_bytes()?)?;
|
||||
send_message(&SocketMessage::Promote.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::TogglePause => {
|
||||
send_message(&*SocketMessage::TogglePause.as_bytes()?)?;
|
||||
send_message(&SocketMessage::TogglePause.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Retile => {
|
||||
send_message(&*SocketMessage::Retile.as_bytes()?)?;
|
||||
send_message(&SocketMessage::Retile.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Move(arg) => {
|
||||
send_message(&*SocketMessage::MoveWindow(arg.operation_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::MoveWindow(arg.operation_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::CycleFocus(arg) => {
|
||||
send_message(&*SocketMessage::CycleFocusWindow(arg.cycle_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::CycleFocusWindow(arg.cycle_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::CycleMove(arg) => {
|
||||
send_message(&*SocketMessage::CycleMoveWindow(arg.cycle_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::CycleMoveWindow(arg.cycle_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::MoveToMonitor(arg) => {
|
||||
send_message(&*SocketMessage::MoveContainerToMonitorNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::MoveContainerToMonitorNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::MoveToWorkspace(arg) => {
|
||||
send_message(&*SocketMessage::MoveContainerToWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::MoveContainerToWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::SendToMonitor(arg) => {
|
||||
send_message(&*SocketMessage::SendContainerToMonitorNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::SendContainerToMonitorNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::SendToWorkspace(arg) => {
|
||||
send_message(&*SocketMessage::SendContainerToWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::SendContainerToWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::SendToMonitorWorkspace(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::SendContainerToMonitorWorkspaceNumber(
|
||||
&SocketMessage::SendContainerToMonitorWorkspaceNumber(
|
||||
arg.target_monitor,
|
||||
arg.target_workspace,
|
||||
)
|
||||
@@ -767,11 +776,11 @@ fn main() -> Result<()> {
|
||||
)?;
|
||||
}
|
||||
SubCommand::MoveWorkspaceToMonitor(arg) => {
|
||||
send_message(&*SocketMessage::MoveWorkspaceToMonitorNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::MoveWorkspaceToMonitorNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::InvisibleBorders(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::InvisibleBorders(Rect {
|
||||
&SocketMessage::InvisibleBorders(Rect {
|
||||
left: arg.left,
|
||||
top: arg.top,
|
||||
right: arg.right,
|
||||
@@ -782,7 +791,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
SubCommand::WorkAreaOffset(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkAreaOffset(Rect {
|
||||
&SocketMessage::WorkAreaOffset(Rect {
|
||||
left: arg.left,
|
||||
top: arg.top,
|
||||
right: arg.right,
|
||||
@@ -793,50 +802,50 @@ fn main() -> Result<()> {
|
||||
}
|
||||
SubCommand::ContainerPadding(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::ContainerPadding(arg.monitor, arg.workspace, arg.size)
|
||||
&SocketMessage::ContainerPadding(arg.monitor, arg.workspace, arg.size)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::WorkspacePadding(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspacePadding(arg.monitor, arg.workspace, arg.size)
|
||||
&SocketMessage::WorkspacePadding(arg.monitor, arg.workspace, arg.size)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::AdjustWorkspacePadding(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::AdjustWorkspacePadding(arg.sizing, arg.adjustment).as_bytes()?,
|
||||
&SocketMessage::AdjustWorkspacePadding(arg.sizing, arg.adjustment).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::AdjustContainerPadding(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::AdjustContainerPadding(arg.sizing, arg.adjustment).as_bytes()?,
|
||||
&SocketMessage::AdjustContainerPadding(arg.sizing, arg.adjustment).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::ToggleFocusFollowsMouse(arg) => {
|
||||
send_message(&*SocketMessage::ToggleFocusFollowsMouse(arg.implementation).as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleFocusFollowsMouse(arg.implementation).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleTiling => {
|
||||
send_message(&*SocketMessage::ToggleTiling.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleTiling.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleFloat => {
|
||||
send_message(&*SocketMessage::ToggleFloat.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleFloat.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleMonocle => {
|
||||
send_message(&*SocketMessage::ToggleMonocle.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleMonocle.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleMaximize => {
|
||||
send_message(&*SocketMessage::ToggleMaximize.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleMaximize.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::WorkspaceLayout(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceLayout(arg.monitor, arg.workspace, arg.value)
|
||||
&SocketMessage::WorkspaceLayout(arg.monitor, arg.workspace, arg.value)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::WorkspaceCustomLayout(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceLayoutCustom(
|
||||
&SocketMessage::WorkspaceLayoutCustom(
|
||||
arg.monitor,
|
||||
arg.workspace,
|
||||
resolve_windows_path(&arg.path)?,
|
||||
@@ -846,7 +855,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
SubCommand::WorkspaceLayoutRule(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceLayoutRule(
|
||||
&SocketMessage::WorkspaceLayoutRule(
|
||||
arg.monitor,
|
||||
arg.workspace,
|
||||
arg.at_container_count,
|
||||
@@ -857,7 +866,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
SubCommand::WorkspaceCustomLayoutRule(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceLayoutCustomRule(
|
||||
&SocketMessage::WorkspaceLayoutCustomRule(
|
||||
arg.monitor,
|
||||
arg.workspace,
|
||||
arg.at_container_count,
|
||||
@@ -868,13 +877,12 @@ fn main() -> Result<()> {
|
||||
}
|
||||
SubCommand::ClearWorkspaceLayoutRules(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::ClearWorkspaceLayoutRules(arg.monitor, arg.workspace)
|
||||
.as_bytes()?,
|
||||
&SocketMessage::ClearWorkspaceLayoutRules(arg.monitor, arg.workspace).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::WorkspaceTiling(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceTiling(arg.monitor, arg.workspace, arg.value.into())
|
||||
&SocketMessage::WorkspaceTiling(arg.monitor, arg.workspace, arg.value.into())
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
@@ -922,7 +930,7 @@ fn main() -> Result<()> {
|
||||
},
|
||||
);
|
||||
|
||||
match powershell_script::run(&script, true) {
|
||||
match powershell_script::run(&script) {
|
||||
Ok(output) => {
|
||||
println!("{}", output);
|
||||
}
|
||||
@@ -932,49 +940,49 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
SubCommand::Stop => {
|
||||
send_message(&*SocketMessage::Stop.as_bytes()?)?;
|
||||
send_message(&SocketMessage::Stop.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FloatRule(arg) => {
|
||||
send_message(&*SocketMessage::FloatRule(arg.identifier, arg.id).as_bytes()?)?;
|
||||
send_message(&SocketMessage::FloatRule(arg.identifier, arg.id).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ManageRule(arg) => {
|
||||
send_message(&*SocketMessage::ManageRule(arg.identifier, arg.id).as_bytes()?)?;
|
||||
send_message(&SocketMessage::ManageRule(arg.identifier, arg.id).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::WorkspaceRule(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceRule(arg.identifier, arg.id, arg.monitor, arg.workspace)
|
||||
&SocketMessage::WorkspaceRule(arg.identifier, arg.id, arg.monitor, arg.workspace)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::Stack(arg) => {
|
||||
send_message(&*SocketMessage::StackWindow(arg.operation_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::StackWindow(arg.operation_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Unstack => {
|
||||
send_message(&*SocketMessage::UnstackWindow.as_bytes()?)?;
|
||||
send_message(&SocketMessage::UnstackWindow.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::CycleStack(arg) => {
|
||||
send_message(&*SocketMessage::CycleStack(arg.cycle_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::CycleStack(arg.cycle_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ChangeLayout(arg) => {
|
||||
send_message(&*SocketMessage::ChangeLayout(arg.default_layout).as_bytes()?)?;
|
||||
send_message(&SocketMessage::ChangeLayout(arg.default_layout).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::LoadCustomLayout(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::ChangeLayoutCustom(resolve_windows_path(&arg.path)?).as_bytes()?,
|
||||
&SocketMessage::ChangeLayoutCustom(resolve_windows_path(&arg.path)?).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::FlipLayout(arg) => {
|
||||
send_message(&*SocketMessage::FlipLayout(arg.axis).as_bytes()?)?;
|
||||
send_message(&SocketMessage::FlipLayout(arg.axis).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FocusMonitor(arg) => {
|
||||
send_message(&*SocketMessage::FocusMonitorNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::FocusMonitorNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FocusWorkspace(arg) => {
|
||||
send_message(&*SocketMessage::FocusWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
send_message(&SocketMessage::FocusWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FocusMonitorWorkspace(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::FocusMonitorWorkspaceNumber(
|
||||
&SocketMessage::FocusMonitorWorkspaceNumber(
|
||||
arg.target_monitor,
|
||||
arg.target_workspace,
|
||||
)
|
||||
@@ -982,23 +990,23 @@ fn main() -> Result<()> {
|
||||
)?;
|
||||
}
|
||||
SubCommand::CycleMonitor(arg) => {
|
||||
send_message(&*SocketMessage::CycleFocusMonitor(arg.cycle_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::CycleFocusMonitor(arg.cycle_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::CycleWorkspace(arg) => {
|
||||
send_message(&*SocketMessage::CycleFocusWorkspace(arg.cycle_direction).as_bytes()?)?;
|
||||
send_message(&SocketMessage::CycleFocusWorkspace(arg.cycle_direction).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::NewWorkspace => {
|
||||
send_message(&*SocketMessage::NewWorkspace.as_bytes()?)?;
|
||||
send_message(&SocketMessage::NewWorkspace.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::WorkspaceName(name) => {
|
||||
send_message(
|
||||
&*SocketMessage::WorkspaceName(name.monitor, name.workspace, name.value)
|
||||
&SocketMessage::WorkspaceName(name.monitor, name.workspace, name.value)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::EnsureWorkspaces(workspaces) => {
|
||||
send_message(
|
||||
&*SocketMessage::EnsureWorkspaces(workspaces.monitor, workspaces.workspace_count)
|
||||
&SocketMessage::EnsureWorkspaces(workspaces.monitor, workspaces.workspace_count)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
@@ -1019,7 +1027,7 @@ fn main() -> Result<()> {
|
||||
},
|
||||
};
|
||||
|
||||
send_message(&*SocketMessage::State.as_bytes()?)?;
|
||||
send_message(&SocketMessage::State.as_bytes()?)?;
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
match listener.accept() {
|
||||
@@ -1053,7 +1061,7 @@ fn main() -> Result<()> {
|
||||
},
|
||||
};
|
||||
|
||||
send_message(&*SocketMessage::Query(arg.state_query).as_bytes()?)?;
|
||||
send_message(&SocketMessage::Query(arg.state_query).as_bytes()?)?;
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
match listener.accept() {
|
||||
@@ -1071,8 +1079,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
SubCommand::RestoreWindows => {
|
||||
let mut hwnd_json = HOME_DIR.clone();
|
||||
hwnd_json.push("komorebi.hwnd.json");
|
||||
let hwnd_json = DATA_DIR.join("komorebi.hwnd.json");
|
||||
|
||||
let file = File::open(hwnd_json)?;
|
||||
let reader = BufReader::new(file);
|
||||
@@ -1083,93 +1090,96 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
SubCommand::ResizeEdge(resize) => {
|
||||
send_message(
|
||||
&*SocketMessage::ResizeWindowEdge(resize.edge, resize.sizing).as_bytes()?,
|
||||
)?;
|
||||
send_message(&SocketMessage::ResizeWindowEdge(resize.edge, resize.sizing).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ResizeAxis(arg) => {
|
||||
send_message(&*SocketMessage::ResizeWindowAxis(arg.axis, arg.sizing).as_bytes()?)?;
|
||||
send_message(&SocketMessage::ResizeWindowAxis(arg.axis, arg.sizing).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FocusFollowsMouse(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::FocusFollowsMouse(arg.implementation, arg.boolean_state.into())
|
||||
&SocketMessage::FocusFollowsMouse(arg.implementation, arg.boolean_state.into())
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::ReloadConfiguration => {
|
||||
send_message(&*SocketMessage::ReloadConfiguration.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ReloadConfiguration.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::WatchConfiguration(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::WatchConfiguration(arg.boolean_state.into()).as_bytes()?,
|
||||
)?;
|
||||
send_message(&SocketMessage::WatchConfiguration(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::IdentifyObjectNameChangeApplication(target) => {
|
||||
send_message(
|
||||
&*SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
|
||||
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::IdentifyTrayApplication(target) => {
|
||||
send_message(
|
||||
&*SocketMessage::IdentifyTrayApplication(target.identifier, target.id)
|
||||
.as_bytes()?,
|
||||
&SocketMessage::IdentifyTrayApplication(target.identifier, target.id).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::IdentifyLayeredApplication(target) => {
|
||||
send_message(
|
||||
&*SocketMessage::IdentifyLayeredApplication(target.identifier, target.id)
|
||||
&SocketMessage::IdentifyLayeredApplication(target.identifier, target.id)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::IdentifyBorderOverflowApplication(target) => {
|
||||
send_message(
|
||||
&*SocketMessage::IdentifyBorderOverflowApplication(target.identifier, target.id)
|
||||
&SocketMessage::IdentifyBorderOverflowApplication(target.identifier, target.id)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::Manage => {
|
||||
send_message(&*SocketMessage::ManageFocusedWindow.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ManageFocusedWindow.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Unmanage => {
|
||||
send_message(&*SocketMessage::UnmanageFocusedWindow.as_bytes()?)?;
|
||||
send_message(&SocketMessage::UnmanageFocusedWindow.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::QuickSaveResize => {
|
||||
send_message(&*SocketMessage::QuickSave.as_bytes()?)?;
|
||||
send_message(&SocketMessage::QuickSave.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::QuickLoadResize => {
|
||||
send_message(&*SocketMessage::QuickLoad.as_bytes()?)?;
|
||||
send_message(&SocketMessage::QuickLoad.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::SaveResize(arg) => {
|
||||
send_message(&*SocketMessage::Save(resolve_windows_path(&arg.path)?).as_bytes()?)?;
|
||||
send_message(&SocketMessage::Save(resolve_windows_path(&arg.path)?).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::LoadResize(arg) => {
|
||||
send_message(&*SocketMessage::Load(resolve_windows_path(&arg.path)?).as_bytes()?)?;
|
||||
send_message(&SocketMessage::Load(resolve_windows_path(&arg.path)?).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Subscribe(arg) => {
|
||||
send_message(&*SocketMessage::AddSubscriber(arg.named_pipe).as_bytes()?)?;
|
||||
send_message(&SocketMessage::AddSubscriber(arg.named_pipe).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Unsubscribe(arg) => {
|
||||
send_message(&*SocketMessage::RemoveSubscriber(arg.named_pipe).as_bytes()?)?;
|
||||
send_message(&SocketMessage::RemoveSubscriber(arg.named_pipe).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleMouseFollowsFocus => {
|
||||
send_message(&*SocketMessage::ToggleMouseFollowsFocus.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleMouseFollowsFocus.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::MouseFollowsFocus(arg) => {
|
||||
send_message(&*SocketMessage::MouseFollowsFocus(arg.boolean_state.into()).as_bytes()?)?;
|
||||
send_message(&SocketMessage::MouseFollowsFocus(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
send_message(&*SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ToggleWindowContainerBehaviour => {
|
||||
send_message(&*SocketMessage::ToggleWindowContainerBehaviour.as_bytes()?)?;
|
||||
send_message(&SocketMessage::ToggleWindowContainerBehaviour.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::WindowHidingBehaviour(arg) => {
|
||||
send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?;
|
||||
send_message(&SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::CrossMonitorMoveBehaviour(arg) => {
|
||||
send_message(
|
||||
&SocketMessage::CrossMonitorMoveBehaviour(arg.move_behaviour).as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::ToggleCrossMonitorMoveBehaviour => {
|
||||
send_message(&SocketMessage::ToggleCrossMonitorMoveBehaviour.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::UnmanagedWindowOperationBehaviour(arg) => {
|
||||
send_message(
|
||||
&*SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour)
|
||||
&SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
@@ -1241,7 +1251,7 @@ fn main() -> Result<()> {
|
||||
},
|
||||
};
|
||||
|
||||
send_message(&*SocketMessage::NotificationSchema.as_bytes()?)?;
|
||||
send_message(&SocketMessage::NotificationSchema.as_bytes()?)?;
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
match listener.accept() {
|
||||
|
||||
Reference in New Issue
Block a user