Compare commits

..

2 Commits

Author SHA1 Message Date
LGUG2Z
57825db886 wip 2024-05-19 11:51:10 -07:00
LGUG2Z
bdf66cc362 feat(wm): auto toggle-monocle on alt-tab
This commit adds a change to automatically toggle monocle mode off on
the focused workspace when a window on the same workspace is
foregrounded by alt-tab.

resolve #834
2024-05-19 11:08:06 -07:00
32 changed files with 168 additions and 4083 deletions

View File

@@ -93,10 +93,9 @@ jobs:
target/${{ matrix.target }}/release/komorebi.exe
target/${{ matrix.target }}/release/komorebic.exe
target/${{ matrix.target }}/release/komorebic-no-console.exe
target/${{ matrix.target }}/release/komorebi-gui.exe
target/${{ matrix.target }}/release/komorebi.pdb
target/${{ matrix.target }}/release/komorebic.pdb
target/${{ matrix.target }}/release/komorebi-gui.pdb
target/${{ matrix.target }}/release/komorebic-no-console.pdb
target/wix/komorebi-*.msi
retention-days: 7
- name: Check GoReleaser

View File

@@ -35,15 +35,6 @@ builds:
post:
- mkdir -p dist/windows_amd64
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic-no-console.exe" ".\dist\komorebic-no-console_windows_amd64_v1\komorebic-no-console.exe"
- id: komorebi-gui
main: dummy.go
goos: [ "windows" ]
goarch: [ "amd64" ]
binary: komorebi-gui
hooks:
post:
- mkdir -p dist/windows_amd64
- cp ".\target\x86_64-pc-windows-msvc\release\komorebi-gui.exe" ".\dist\komorebi-gui_windows_amd64_v1\komorebi-gui.exe"
archives:
- name_template: "{{ .ProjectName }}-{{ .Version }}-x86_64-pc-windows-msvc"

3070
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@ members = [
"komorebi",
"komorebi-client",
"komorebi-core",
"komorebi-gui",
"komorebic",
"komorebic-no-console",
]

View File

@@ -1,16 +0,0 @@
# cycle-move-workspace-to-monitor
```
Move the focused workspace monitor in the given cycle direction
Usage: komorebic.exe cycle-move-workspace-to-monitor <CYCLE_DIRECTION>
Arguments:
<CYCLE_DIRECTION>
[possible values: previous, next]
Options:
-h, --help
Print help
```

View File

@@ -1,12 +0,0 @@
# gui
```
Launch the komorebi-gui debugging tool
Usage: komorebic.exe gui
Options:
-h, --help
Print help
```

View File

@@ -1,16 +0,0 @@
# promote-window
```
Promote the window in the specified direction
Usage: komorebic.exe promote-window <OPERATION_DIRECTION>
Arguments:
<OPERATION_DIRECTION>
[possible values: left, right, up, down]
Options:
-h, --help
Print help
```

View File

@@ -114,7 +114,6 @@ Clone the git repository, enter the directory, and build the following binaries:
cargo +stable install --path komorebi --locked
cargo +stable install --path komorebic --locked
cargo +stable install --path komorebic-no-console --locked
cargo +stable install --path komorebi-gui --locked
```
If the binaries have been built and added to your `$PATH` correctly, you should

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-client"
version = "0.1.26"
version = "0.1.26-dev.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -29,7 +29,6 @@ pub use komorebi_core::Layout;
pub use komorebi_core::OperationDirection;
pub use komorebi_core::Rect;
pub use komorebi_core::SocketMessage;
pub use komorebi_core::StackbarLabel;
pub use komorebi_core::StackbarMode;
pub use komorebi_core::WindowKind;

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-core"
version = "0.1.26"
version = "0.1.26-dev.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,14 +0,0 @@
[package]
name = "komorebi-gui"
version = "0.1.26"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
egui_extras = { version = "0.27" }
eframe = "0.27"
komorebi-client = { path = "../komorebi-client" }
serde_json = "1"
random_word = { version = "0.4.3", features = ["en"] }
windows = { workspace = true }

View File

@@ -1,805 +0,0 @@
use eframe::egui;
use eframe::egui::color_picker::Alpha;
use eframe::egui::Color32;
use eframe::egui::ViewportBuilder;
use komorebi_client::BorderStyle;
use komorebi_client::Colour;
use komorebi_client::DefaultLayout;
use komorebi_client::GlobalState;
use komorebi_client::Layout;
use komorebi_client::Rect;
use komorebi_client::Rgb;
use komorebi_client::RuleDebug;
use komorebi_client::SocketMessage;
use komorebi_client::StackbarLabel;
use komorebi_client::StackbarMode;
use komorebi_client::State;
use komorebi_client::Window;
use komorebi_client::WindowKind;
use std::collections::HashMap;
use std::time::Duration;
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
fn main() {
let native_options = eframe::NativeOptions {
viewport: ViewportBuilder::default()
.with_always_on_top()
.with_inner_size([320.0, 500.0]),
follow_system_theme: true,
..Default::default()
};
let _ = eframe::run_native(
"komorebi-gui",
native_options,
Box::new(|cc| Box::new(KomorebiGui::new(cc))),
);
}
struct BorderColours {
single: Color32,
stack: Color32,
monocle: Color32,
unfocused: Color32,
}
struct BorderConfig {
border_enabled: bool,
border_colours: BorderColours,
border_style: BorderStyle,
border_offset: i32,
border_width: i32,
}
struct StackbarConfig {
mode: StackbarMode,
label: StackbarLabel,
height: i32,
width: i32,
focused_text_colour: Color32,
unfocused_text_colour: Color32,
background_colour: Color32,
}
struct MonitorConfig {
size: Rect,
work_area_offset: Rect,
workspaces: Vec<WorkspaceConfig>,
}
impl From<&komorebi_client::Monitor> for MonitorConfig {
fn from(value: &komorebi_client::Monitor) -> Self {
let mut workspaces = vec![];
for ws in value.workspaces() {
workspaces.push(WorkspaceConfig::from(ws));
}
Self {
size: *value.size(),
work_area_offset: value.work_area_offset().unwrap_or_default(),
workspaces,
}
}
}
struct WorkspaceConfig {
name: String,
tile: bool,
layout: DefaultLayout,
container_padding: i32,
workspace_padding: i32,
}
impl From<&komorebi_client::Workspace> for WorkspaceConfig {
fn from(value: &komorebi_client::Workspace) -> Self {
let layout = match value.layout() {
Layout::Default(layout) => *layout,
Layout::Custom(_) => DefaultLayout::BSP,
};
let name = value
.name()
.to_owned()
.unwrap_or_else(|| random_word::gen(random_word::Lang::En).to_string());
Self {
layout,
name,
tile: *value.tile(),
workspace_padding: value.workspace_padding().unwrap_or(20),
container_padding: value.container_padding().unwrap_or(20),
}
}
}
struct KomorebiGui {
border_config: BorderConfig,
stackbar_config: StackbarConfig,
mouse_follows_focus: bool,
monitors: Vec<MonitorConfig>,
workspace_names: HashMap<usize, Vec<String>>,
debug_hwnd: isize,
debug_windows: Vec<Window>,
debug_rule: Option<RuleDebug>,
}
fn colour32(colour: Option<Colour>) -> Color32 {
match colour {
Some(Colour::Rgb(rgb)) => Color32::from_rgb(rgb.r as u8, rgb.g as u8, rgb.b as u8),
Some(Colour::Hex(hex)) => {
let rgb = Rgb::from(hex);
Color32::from_rgb(rgb.r as u8, rgb.g as u8, rgb.b as u8)
}
None => Color32::from_rgb(0, 0, 0),
}
}
impl KomorebiGui {
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
// Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
// Restore app state using cc.storage (requires the "persistence" feature).
// Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
// for e.g. egui::PaintCallback.
let global_state: GlobalState = serde_json::from_str(
&komorebi_client::send_query(&SocketMessage::GlobalState).unwrap(),
)
.unwrap();
let state: State =
serde_json::from_str(&komorebi_client::send_query(&SocketMessage::State).unwrap())
.unwrap();
let border_colours = BorderColours {
single: colour32(global_state.border_colours.single),
stack: colour32(global_state.border_colours.stack),
monocle: colour32(global_state.border_colours.monocle),
unfocused: colour32(global_state.border_colours.unfocused),
};
let border_config = BorderConfig {
border_enabled: global_state.border_enabled,
border_colours,
border_style: global_state.border_style,
border_offset: global_state.border_offset,
border_width: global_state.border_width,
};
let mut monitors = vec![];
for m in state.monitors.elements() {
monitors.push(MonitorConfig::from(m));
}
let mut workspace_names = HashMap::new();
for (monitor_idx, m) in monitors.iter().enumerate() {
for ws in &m.workspaces {
let names = workspace_names.entry(monitor_idx).or_insert_with(Vec::new);
names.push(ws.name.clone());
}
}
let stackbar_config = StackbarConfig {
mode: global_state.stackbar_mode,
height: global_state.stackbar_height,
width: global_state.stackbar_tab_width,
label: global_state.stackbar_label,
focused_text_colour: colour32(Some(global_state.stackbar_focused_text_colour)),
unfocused_text_colour: colour32(Some(global_state.stackbar_unfocused_text_colour)),
background_colour: colour32(Some(global_state.stackbar_tab_background_colour)),
};
let mut debug_windows = vec![];
unsafe {
EnumWindows(
Some(enum_window),
windows::Win32::Foundation::LPARAM(&mut debug_windows as *mut Vec<Window> as isize),
)
.unwrap();
};
Self {
border_config,
mouse_follows_focus: state.mouse_follows_focus,
monitors,
workspace_names,
debug_hwnd: 0,
debug_windows,
stackbar_config,
debug_rule: None,
}
}
}
extern "system" fn enum_window(
hwnd: windows::Win32::Foundation::HWND,
lparam: windows::Win32::Foundation::LPARAM,
) -> windows::Win32::Foundation::BOOL {
let windows = unsafe { &mut *(lparam.0 as *mut Vec<Window>) };
let window = Window { hwnd: hwnd.0 };
if window.is_window()
&& !window.is_miminized()
&& window.is_visible()
&& window.title().is_ok()
&& window.exe().is_ok()
{
windows.push(window);
}
true.into()
}
fn json_view_ui(ui: &mut egui::Ui, code: &str) {
let language = "json";
let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx());
egui_extras::syntax_highlighting::code_view_ui(ui, &theme, code, language);
}
impl eframe::App for KomorebiGui {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ctx.set_pixels_per_point(2.0);
egui::ScrollArea::vertical().show(ui, |ui| {
ui.set_width(ctx.screen_rect().width());
ui.collapsing("Debugging", |ui| {
ui.collapsing("Window Rules", |ui| {
let window = Window {
hwnd: self.debug_hwnd,
};
let label = if let (Ok(title), Ok(exe)) = (window.title(), window.exe()) {
format!("{title} ({exe})")
} else {
String::from("Select a Window")
};
if ui.button("Refresh Windows").clicked() {
let mut debug_windows = vec![];
unsafe {
EnumWindows(
Some(enum_window),
windows::Win32::Foundation::LPARAM(
&mut debug_windows as *mut Vec<Window> as isize,
),
)
.unwrap();
};
self.debug_windows = debug_windows;
}
egui::ComboBox::from_label("Select a Window")
.selected_text(label)
.show_ui(ui, |ui| {
for w in &self.debug_windows {
if ui
.selectable_value(
&mut self.debug_hwnd,
w.hwnd,
format!(
"{} ({})",
w.title().unwrap(),
w.exe().unwrap()
),
)
.changed()
{
let debug_rule: RuleDebug = serde_json::from_str(
&komorebi_client::send_query(
&SocketMessage::DebugWindow(self.debug_hwnd),
)
.unwrap(),
)
.unwrap();
self.debug_rule = Some(debug_rule)
}
}
});
if let Some(debug_rule) = &self.debug_rule {
json_view_ui(ui, &serde_json::to_string_pretty(debug_rule).unwrap())
}
});
});
ui.collapsing("Mouse", |ui| {
if ui
.toggle_value(&mut self.mouse_follows_focus, "Mouse Follows Focus")
.changed()
{
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
self.mouse_follows_focus,
))
.unwrap();
}
});
ui.collapsing("Border", |ui| {
if ui
.toggle_value(&mut self.border_config.border_enabled, "Border")
.changed()
{
komorebi_client::send_message(&SocketMessage::Border(
self.border_config.border_enabled,
))
.unwrap();
}
ui.collapsing("Colours", |ui| {
ui.collapsing("Single", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.border_colours.single,
Alpha::Opaque,
) {
komorebi_client::send_message(&SocketMessage::BorderColour(
WindowKind::Single,
self.border_config.border_colours.single.r() as u32,
self.border_config.border_colours.single.g() as u32,
self.border_config.border_colours.single.b() as u32,
))
.unwrap();
}
});
ui.collapsing("Stack", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.border_colours.stack,
Alpha::Opaque,
) {
komorebi_client::send_message(&SocketMessage::BorderColour(
WindowKind::Stack,
self.border_config.border_colours.stack.r() as u32,
self.border_config.border_colours.stack.g() as u32,
self.border_config.border_colours.stack.b() as u32,
))
.unwrap();
}
});
ui.collapsing("Monocle", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.border_colours.monocle,
Alpha::Opaque,
) {
komorebi_client::send_message(&SocketMessage::BorderColour(
WindowKind::Monocle,
self.border_config.border_colours.monocle.r() as u32,
self.border_config.border_colours.monocle.g() as u32,
self.border_config.border_colours.monocle.b() as u32,
))
.unwrap();
}
});
ui.collapsing("Unfocused", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.border_colours.unfocused,
Alpha::Opaque,
) {
komorebi_client::send_message(&SocketMessage::BorderColour(
WindowKind::Unfocused,
self.border_config.border_colours.unfocused.r() as u32,
self.border_config.border_colours.unfocused.g() as u32,
self.border_config.border_colours.unfocused.b() as u32,
))
.unwrap();
}
})
});
ui.collapsing("Style", |ui| {
for option in [
BorderStyle::System,
BorderStyle::Rounded,
BorderStyle::Square,
] {
if ui
.add(egui::SelectableLabel::new(
self.border_config.border_style == option,
option.to_string(),
))
.clicked()
{
self.border_config.border_style = option;
komorebi_client::send_message(&SocketMessage::BorderStyle(
self.border_config.border_style,
))
.unwrap();
std::thread::sleep(Duration::from_secs(1));
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
}
}
});
ui.collapsing("Width", |ui| {
if ui
.add(egui::Slider::new(
&mut self.border_config.border_width,
-50..=50,
))
.changed()
{
komorebi_client::send_message(&SocketMessage::BorderWidth(
self.border_config.border_width,
))
.unwrap();
};
});
ui.collapsing("Offset", |ui| {
if ui
.add(egui::Slider::new(
&mut self.border_config.border_offset,
-50..=50,
))
.changed()
{
komorebi_client::send_message(&SocketMessage::BorderOffset(
self.border_config.border_offset,
))
.unwrap();
};
});
});
ui.collapsing("Stackbar", |ui| {
for option in [
StackbarMode::Never,
StackbarMode::OnStack,
StackbarMode::Always,
] {
if ui
.add(egui::SelectableLabel::new(
self.stackbar_config.mode == option,
option.to_string(),
))
.clicked()
{
self.stackbar_config.mode = option;
komorebi_client::send_message(&SocketMessage::StackbarMode(
self.stackbar_config.mode,
))
.unwrap();
komorebi_client::send_message(&SocketMessage::Retile).unwrap()
}
}
ui.collapsing("Label", |ui| {
for option in [StackbarLabel::Process, StackbarLabel::Title] {
if ui
.add(egui::SelectableLabel::new(
self.stackbar_config.label == option,
option.to_string(),
))
.clicked()
{
self.stackbar_config.label = option;
komorebi_client::send_message(&SocketMessage::StackbarLabel(
self.stackbar_config.label,
))
.unwrap();
}
}
});
ui.collapsing("Colours", |ui| {
ui.collapsing("Focused Text", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.stackbar_config.focused_text_colour,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::StackbarFocusedTextColour(
self.stackbar_config.focused_text_colour.r() as u32,
self.stackbar_config.focused_text_colour.g() as u32,
self.stackbar_config.focused_text_colour.b() as u32,
),
)
.unwrap();
}
});
ui.collapsing("Unfocused Text", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.stackbar_config.unfocused_text_colour,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::StackbarUnfocusedTextColour(
self.stackbar_config.unfocused_text_colour.r() as u32,
self.stackbar_config.unfocused_text_colour.g() as u32,
self.stackbar_config.unfocused_text_colour.b() as u32,
),
)
.unwrap();
}
});
ui.collapsing("Background", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.stackbar_config.background_colour,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::StackbarBackgroundColour(
self.stackbar_config.background_colour.r() as u32,
self.stackbar_config.background_colour.g() as u32,
self.stackbar_config.background_colour.b() as u32,
),
)
.unwrap();
}
})
});
ui.collapsing("Width", |ui| {
if ui
.add(egui::Slider::new(&mut self.stackbar_config.width, 0..=500))
.drag_stopped()
{
komorebi_client::send_message(&SocketMessage::StackbarTabWidth(
self.stackbar_config.width,
))
.unwrap();
komorebi_client::send_message(&SocketMessage::Retile).unwrap()
};
});
ui.collapsing("Height", |ui| {
if ui
.add(egui::Slider::new(&mut self.stackbar_config.height, 0..=100))
.drag_stopped()
{
komorebi_client::send_message(&SocketMessage::StackbarHeight(
self.stackbar_config.height,
))
.unwrap();
komorebi_client::send_message(&SocketMessage::Retile).unwrap()
};
});
});
for (monitor_idx, monitor) in self.monitors.iter_mut().enumerate() {
ui.collapsing(
format!(
"Monitor {monitor_idx} ({}x{})",
monitor.size.right, monitor.size.bottom
),
|ui| {
ui.collapsing("Work Area Offset", |ui| {
if ui
.add(
egui::Slider::new(
&mut monitor.work_area_offset.left,
0..=500,
)
.text("Left"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::MonitorWorkAreaOffset(
monitor_idx,
monitor.work_area_offset,
),
)
.unwrap();
};
if ui
.add(
egui::Slider::new(
&mut monitor.work_area_offset.top,
0..=500,
)
.text("Top"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::MonitorWorkAreaOffset(
monitor_idx,
monitor.work_area_offset,
),
)
.unwrap();
};
if ui
.add(
egui::Slider::new(
&mut monitor.work_area_offset.right,
0..=500,
)
.text("Right"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::MonitorWorkAreaOffset(
monitor_idx,
monitor.work_area_offset,
),
)
.unwrap();
};
if ui
.add(
egui::Slider::new(
&mut monitor.work_area_offset.bottom,
0..=500,
)
.text("Bottom"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::MonitorWorkAreaOffset(
monitor_idx,
monitor.work_area_offset,
),
)
.unwrap();
};
});
ui.collapsing("Workspaces", |ui| {
for (workspace_idx, workspace) in
monitor.workspaces.iter_mut().enumerate()
{
ui.collapsing(
format!("Workspace {workspace_idx} ({})", workspace.name),
|ui| {
if ui.button("Focus").clicked() {
komorebi_client::send_message(
&SocketMessage::MouseFollowsFocus(false),
)
.unwrap();
komorebi_client::send_message(
&SocketMessage::FocusMonitorWorkspaceNumber(
monitor_idx,
workspace_idx,
),
)
.unwrap();
komorebi_client::send_message(
&SocketMessage::MouseFollowsFocus(
self.mouse_follows_focus,
),
)
.unwrap();
}
if ui
.toggle_value(&mut workspace.tile, "Tiling")
.changed()
{
komorebi_client::send_message(
&SocketMessage::WorkspaceTiling(
monitor_idx,
workspace_idx,
workspace.tile,
),
)
.unwrap();
}
ui.collapsing("Name", |ui| {
let monitor_workspaces = self
.workspace_names
.get_mut(&monitor_idx)
.unwrap();
let workspace_name =
&mut monitor_workspaces[workspace_idx];
if ui
.text_edit_singleline(workspace_name)
.lost_focus()
{
workspace.name = workspace_name.clone();
komorebi_client::send_message(
&SocketMessage::WorkspaceName(
monitor_idx,
workspace_idx,
workspace.name.clone(),
),
)
.unwrap();
}
});
ui.collapsing("Layout", |ui| {
for option in [
DefaultLayout::BSP,
DefaultLayout::Columns,
DefaultLayout::Rows,
DefaultLayout::VerticalStack,
DefaultLayout::HorizontalStack,
DefaultLayout::UltrawideVerticalStack,
DefaultLayout::Grid,
] {
if ui
.add(egui::SelectableLabel::new(
workspace.layout == option,
option.to_string(),
))
.clicked()
{
workspace.layout = option;
komorebi_client::send_message(
&SocketMessage::WorkspaceLayout(
monitor_idx,
workspace_idx,
workspace.layout,
),
)
.unwrap();
}
}
});
ui.collapsing("Container Padding", |ui| {
if ui
.add(egui::Slider::new(
&mut workspace.container_padding,
0..=100,
))
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::ContainerPadding(
monitor_idx,
workspace_idx,
workspace.container_padding,
),
)
.unwrap();
};
});
ui.collapsing("Workspace Padding", |ui| {
if ui
.add(egui::Slider::new(
&mut workspace.workspace_padding,
0..=100,
))
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::WorkspacePadding(
monitor_idx,
workspace_idx,
workspace.workspace_padding,
),
)
.unwrap();
};
});
},
);
}
});
},
);
}
});
});
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.26"
version = "0.1.26-dev.0"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]

View File

@@ -9,7 +9,6 @@ pub mod monitor_reconciliator;
pub mod process_command;
pub mod process_event;
pub mod process_movement;
pub mod reaper;
pub mod set_window_position;
pub mod stackbar_manager;
pub mod static_config;

View File

@@ -30,7 +30,6 @@ use komorebi::process_command::listen_for_commands;
use komorebi::process_command::listen_for_commands_tcp;
use komorebi::process_event::listen_for_events;
use komorebi::process_movement::listen_for_movements;
use komorebi::reaper;
use komorebi::stackbar_manager;
use komorebi::static_config::StaticConfig;
use komorebi::window_manager::WindowManager;
@@ -259,7 +258,6 @@ fn main() -> Result<()> {
stackbar_manager::listen_for_notifications(wm.clone());
workspace_reconciliator::listen_for_notifications(wm.clone());
monitor_reconciliator::listen_for_notifications(wm.clone())?;
reaper::watch_for_orphans(wm.clone());
let (ctrlc_sender, ctrlc_receiver) = crossbeam_channel::bounded(1);
ctrlc::set_handler(move || {

View File

@@ -215,18 +215,8 @@ impl WindowManager {
WindowsApi::center_cursor_in_rect(&focused_window_rect)?;
WindowsApi::left_click();
}
SocketMessage::Close => {
Window {
hwnd: WindowsApi::foreground_window()?,
}
.close()?;
}
SocketMessage::Minimize => {
Window {
hwnd: WindowsApi::foreground_window()?,
}
.minimize();
}
SocketMessage::Close => self.focused_window()?.close()?,
SocketMessage::Minimize => self.focused_window()?.minimize(),
SocketMessage::ToggleFloat => self.toggle_float()?,
SocketMessage::ToggleMonocle => self.toggle_monocle()?,
SocketMessage::ToggleMaximize => self.toggle_maximize()?,

View File

@@ -24,7 +24,6 @@ use crate::window::RuleDebug;
use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::workspace_reconciliator;
use crate::workspace_reconciliator::ALT_TAB_HWND;
use crate::workspace_reconciliator::ALT_TAB_HWND_INSTANT;
@@ -121,11 +120,37 @@ impl WindowManager {
_ => {}
}
for monitor in self.monitors_mut() {
for workspace in monitor.workspaces_mut() {
let offset = self.work_area_offset;
for (i, monitor) in self.monitors_mut().iter_mut().enumerate() {
let work_area = *monitor.work_area_size();
let window_based_work_area_offset = (
monitor.window_based_work_area_offset_limit(),
monitor.window_based_work_area_offset(),
);
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
if let WindowManagerEvent::FocusChange(_, window) = event {
let _ = workspace.focus_changed(window.hwnd);
}
let reaped_orphans = workspace.reap_orphans()?;
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
workspace.update(&work_area, offset, window_based_work_area_offset)?;
tracing::info!(
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
reaped_orphans.0,
reaped_orphans.1,
i,
j
);
}
}
}
@@ -310,7 +335,7 @@ impl WindowManager {
let behaviour = self.window_container_behaviour;
let workspace = self.focused_workspace_mut()?;
let workspace_contains_window = workspace.contains_window(window.hwnd);
let monocle_container = workspace.monocle_container().clone();
let workspace_has_monocle_container = workspace.monocle_container().is_some();
if !workspace_contains_window && !needs_reconciliation {
match behaviour {
@@ -328,18 +353,10 @@ impl WindowManager {
}
}
if workspace_contains_window {
let mut monocle_window_event = false;
if let Some(ref monocle) = monocle_container {
if let Some(monocle_window) = monocle.focused_window() {
if monocle_window.hwnd == window.hwnd {
monocle_window_event = true;
}
}
}
if !monocle_window_event && monocle_container.is_some() {
window.hide();
if matches!(event, WindowManagerEvent::Uncloak(_, _)) {
if workspace_contains_window && workspace_has_monocle_container {
self.toggle_monocle()?;
window.focus(self.mouse_follows_focus)?;
}
}
}
@@ -571,9 +588,10 @@ impl WindowManager {
}
}
}
WindowManagerEvent::MouseCapture(..)
| WindowManagerEvent::Cloak(..)
| WindowManagerEvent::TitleUpdate(..) => {}
WindowManagerEvent::ForceUpdate(_) => {
self.update_focused_workspace(false, true)?;
}
WindowManagerEvent::MouseCapture(..) | WindowManagerEvent::Cloak(..) => {}
};
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
@@ -611,16 +629,7 @@ impl WindowManager {
border_manager::event_tx().send(border_manager::Notification)?;
stackbar_manager::event_tx().send(stackbar_manager::Notification)?;
// Too many spammy OBJECT_NAMECHANGE events from JetBrains IDEs
if !matches!(
event,
WindowManagerEvent::Show(WinEvent::ObjectNameChange, _)
) {
tracing::info!("processed: {}", event.window().to_string());
} else {
tracing::trace!("processed: {}", event.window().to_string());
}
tracing::info!("processed: {}", event.window().to_string());
Ok(())
}
}

View File

@@ -1,66 +0,0 @@
#![deny(clippy::unwrap_used, clippy::expect_used)]
use crate::border_manager;
use crate::WindowManager;
use parking_lot::Mutex;
use std::sync::Arc;
use std::time::Duration;
pub fn watch_for_orphans(wm: Arc<Mutex<WindowManager>>) {
std::thread::spawn(move || loop {
match find_orphans(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
if cfg!(debug_assertions) {
tracing::error!("restarting failed thread: {:?}", error)
} else {
tracing::error!("restarting failed thread: {}", error)
}
}
}
});
}
pub fn find_orphans(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
tracing::info!("watching");
let arc = wm.clone();
loop {
std::thread::sleep(Duration::from_secs(1));
let mut wm = arc.lock();
let offset = wm.work_area_offset;
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
let work_area = *monitor.work_area_size();
let window_based_work_area_offset = (
monitor.window_based_work_area_offset_limit(),
monitor.window_based_work_area_offset(),
);
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
let reaped_orphans = workspace.reap_orphans()?;
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
workspace.update(&work_area, offset, window_based_work_area_offset)?;
border_manager::event_tx().send(border_manager::Notification)?;
tracing::info!(
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
reaped_orphans.0,
reaped_orphans.1,
i,
j
);
}
}
}
}
}

View File

@@ -136,27 +136,6 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
continue 'receiver;
}
// Destroy any stackbars not associated with the focused workspace
let container_ids = ws
.containers()
.iter()
.map(|c| c.id().clone())
.collect::<Vec<_>>();
let mut to_remove = vec![];
for (id, stackbar) in stackbars.iter() {
if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx
&& !container_ids.contains(id)
{
stackbar.destroy()?;
to_remove.push(id.clone());
}
}
for id in &to_remove {
stackbars.remove(id);
}
let container_padding = ws
.container_padding()
.unwrap_or_else(|| DEFAULT_CONTAINER_PADDING.load_consume());

View File

@@ -229,7 +229,7 @@ impl From<&Monitor> for MonitorConfig {
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
/// The `komorebi.json` static configuration file reference for `v0.1.26`
/// The `komorebi.json` static configuration file reference for `v0.1.25`
pub struct StaticConfig {
/// DEPRECATED from v0.1.22: no longer required
#[serde(skip_serializing_if = "Option::is_none")]
@@ -322,48 +322,6 @@ pub struct StaticConfig {
pub stackbar: Option<StackbarConfig>,
}
impl StaticConfig {
pub fn aliases(raw: &str) {
let mut map = HashMap::new();
map.insert("border", ["active_window_border"]);
map.insert("border_width", ["active_window_border_width"]);
map.insert("border_offset", ["active_window_border_offset"]);
map.insert("border_colours", ["active_window_border_colours"]);
map.insert("border_style", ["active_window_border_style"]);
let mut display = false;
for (_, aliases) in &map {
for a in aliases {
if raw.contains(a) {
display = true;
}
}
}
if display {
println!("\nYour configuration file contains some options that have been renamed or deprecated:\n");
for (canonical, aliases) in map {
for alias in aliases {
if raw.contains(alias) {
println!(r#""{alias}" is now "{canonical}""#);
}
}
}
}
}
pub fn deprecated(raw: &str) {
let deprecated = ["invisible_borders"];
for option in deprecated {
if raw.contains(option) {
println!(r#""{option}" is deprecated and can be removed"#);
}
}
}
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct TabsConfig {
/// Width of a stackbar tab

View File

@@ -37,7 +37,6 @@ use komorebi_core::OperationBehaviour;
use komorebi_core::OperationDirection;
use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::StackbarLabel;
use komorebi_core::WindowContainerBehaviour;
use crate::border_manager;
@@ -48,7 +47,6 @@ use crate::load_configuration;
use crate::monitor::Monitor;
use crate::ring::Ring;
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
use crate::stackbar_manager::STACKBAR_LABEL;
use crate::stackbar_manager::STACKBAR_MODE;
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
@@ -124,7 +122,6 @@ pub struct GlobalState {
pub border_offset: i32,
pub border_width: i32,
pub stackbar_mode: StackbarMode,
pub stackbar_label: StackbarLabel,
pub stackbar_focused_text_colour: Colour,
pub stackbar_unfocused_text_colour: Colour,
pub stackbar_tab_background_colour: Colour,
@@ -167,7 +164,6 @@ impl Default for GlobalState {
border_offset: border_manager::BORDER_OFFSET.load(Ordering::SeqCst),
border_width: border_manager::BORDER_WIDTH.load(Ordering::SeqCst),
stackbar_mode: STACKBAR_MODE.load(),
stackbar_label: STACKBAR_LABEL.load(),
stackbar_focused_text_colour: Colour::Rgb(Rgb::from(
STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst),
)),

View File

@@ -27,7 +27,7 @@ pub enum WindowManagerEvent {
Manage(Window),
Unmanage(Window),
Raise(Window),
TitleUpdate(WinEvent, Window),
ForceUpdate(Window),
}
impl Display for WindowManagerEvent {
@@ -75,8 +75,8 @@ impl Display for WindowManagerEvent {
Self::Raise(window) => {
write!(f, "Raise (Window: {window})")
}
Self::TitleUpdate(winevent, window) => {
write!(f, "TitleUpdate (WinEvent: {winevent}, Window: {window})")
Self::ForceUpdate(window) => {
write!(f, "ForceUpdate (Window: {window})")
}
}
}
@@ -98,7 +98,7 @@ impl WindowManagerEvent {
| Self::Raise(window)
| Self::Manage(window)
| Self::Unmanage(window)
| Self::TitleUpdate(_, window) => window,
| Self::ForceUpdate(window) => window,
}
}
@@ -141,7 +141,7 @@ impl WindowManagerEvent {
let class = &window.class().ok()?;
let path = &window.path().ok()?;
let should_trigger_show = should_act(
let should_trigger = should_act(
title,
exe_name,
class,
@@ -151,10 +151,10 @@ impl WindowManagerEvent {
)
.is_some();
if should_trigger_show {
if should_trigger {
Option::from(Self::Show(winevent, window))
} else {
Option::from(Self::TitleUpdate(winevent, window))
None
}
}
_ => None,

View File

@@ -80,19 +80,8 @@ pub extern "system" fn win_event_hook(
Ok(event) => event,
Err(_) => return,
};
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
None => {
tracing::trace!(
"Unhandled WinEvent: {winevent} (hwnd: {}, exe: {}, title: {}, class: {})",
window.hwnd,
window.exe().unwrap_or_default(),
window.title().unwrap_or_default(),
window.class().unwrap_or_default()
);
return;
}
None => return,
Some(event) => event,
};

View File

@@ -357,7 +357,7 @@ impl Workspace {
layout.bottom -= total_height;
}
window.set_position(layout, false)?;
window.set_position(&layout, false)?;
}
}
@@ -660,10 +660,6 @@ impl Workspace {
self.set_monocle_container_restore_idx(None);
}
for c in self.containers() {
c.restore();
}
return Ok(());
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic-no-console"
version = "0.1.26"
version = "0.1.26-dev.0"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.26"
version = "0.1.26-dev.0"
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"]

View File

@@ -41,7 +41,6 @@ use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use derive_ahk::AhkFunction;
use derive_ahk::AhkLibrary;
use komorebi_client::StaticConfig;
use komorebi_core::config_generation::ApplicationConfigurationGenerator;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::Axis;
@@ -829,8 +828,6 @@ enum SubCommand {
State,
/// Show a JSON representation of the current global state
GlobalState,
/// Launch the komorebi-gui debugging tool
Gui,
/// Show a JSON representation of visible windows
VisibleWindows,
/// Query the current window manager state
@@ -1955,25 +1952,6 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
);
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
println!("* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands");
let static_config = arg.config.map_or_else(
|| {
let komorebi_json = HOME_DIR.join("komorebi.json");
if komorebi_json.is_file() {
Option::from(komorebi_json)
} else {
None
}
},
Option::from,
);
if let Some(config) = static_config {
let path = resolve_home_path(config)?;
let raw = std::fs::read_to_string(path)?;
StaticConfig::aliases(&raw);
StaticConfig::deprecated(&raw);
}
}
SubCommand::Stop(arg) => {
if arg.whkd {
@@ -2152,9 +2130,6 @@ Stop-Process -Name:komorebi -ErrorAction SilentlyContinue
SubCommand::GlobalState => {
print_query(&SocketMessage::GlobalState.as_bytes()?);
}
SubCommand::Gui => {
Command::new("komorebi-gui").spawn()?;
}
SubCommand::VisibleWindows => {
print_query(&SocketMessage::VisibleWindows.as_bytes()?);
}

View File

@@ -80,7 +80,6 @@ nav:
- cli/whkdrc.md
- cli/state.md
- cli/global-state.md
- cli/gui.md
- cli/visible-windows.md
- cli/query.md
- cli/subscribe-socket.md
@@ -125,7 +124,6 @@ nav:
- cli/cycle-monitor.md
- cli/cycle-workspace.md
- cli/move-workspace-to-monitor.md
- cli/cycle-move-workspace-to-monitor.md
- cli/swap-workspaces-with-monitor.md
- cli/new-workspace.md
- cli/resize-delta.md
@@ -142,7 +140,6 @@ nav:
- cli/flip-layout.md
- cli/promote.md
- cli/promote-focus.md
- cli/promote-window.md
- cli/retile.md
- cli/monitor-index-preference.md
- cli/display-index-preference.md

View File

@@ -1039,14 +1039,6 @@
"type": "integer",
"format": "int32"
},
"label": {
"description": "Stackbar height",
"type": "string",
"enum": [
"Process",
"Title"
]
},
"mode": {
"description": "Stackbar mode",
"type": "string",

Binary file not shown.

View File

@@ -95,9 +95,6 @@
<Component Id='binary2' Guid='*'>
<File Id='exe2' Name='komorebic-no-console.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebic-no-console.exe' KeyPath='yes' />
</Component>
<Component Id='binary3' Guid='*'>
<File Id='exe3' Name='komorebi-gui.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi-gui.exe' KeyPath='yes' />
</Component>
</Directory>
</Directory>
</Directory>
@@ -116,8 +113,6 @@
<ComponentRef Id='binary2' />
<ComponentRef Id='binary3' />
<Feature Id='Environment' Title='PATH Environment Variable' Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.' Level='1' Absent='allow'>
<ComponentRef Id='Path' />
</Feature>