diff --git a/Cargo.lock b/Cargo.lock index 66aee854..88356659 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -501,12 +501,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomic_float" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" - [[package]] name = "atspi" version = "0.19.0" @@ -2711,7 +2705,6 @@ dependencies = [ name = "komorebi-bar" version = "0.1.30" dependencies = [ - "atomic_float", "chrono", "clap", "color-eyre", diff --git a/komorebi-bar/Cargo.toml b/komorebi-bar/Cargo.toml index 98a564fb..802e79fc 100644 --- a/komorebi-bar/Cargo.toml +++ b/komorebi-bar/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" komorebi-client = { path = "../komorebi-client" } komorebi-themes = { path = "../komorebi-themes" } -atomic_float = "1" chrono = { workspace = true } clap = { workspace = true } color-eyre = { workspace = true } diff --git a/komorebi-bar/src/bar.rs b/komorebi-bar/src/bar.rs index d9c0a967..698a94a8 100644 --- a/komorebi-bar/src/bar.rs +++ b/komorebi-bar/src/bar.rs @@ -1,11 +1,17 @@ use crate::config::KomobarConfig; use crate::config::KomobarTheme; +use crate::config::Position; +use crate::config::PositionConfig; use crate::komorebi::Komorebi; use crate::komorebi::KomorebiNotificationState; +use crate::process_hwnd; use crate::widget::BarWidget; use crate::widget::WidgetConfig; -use crate::DPI; +use crate::BAR_HEIGHT; use crate::MAX_LABEL_WIDTH; +use crate::MONITOR_LEFT; +use crate::MONITOR_RIGHT; +use crate::MONITOR_TOP; use crossbeam_channel::Receiver; use eframe::egui::Align; use eframe::egui::CentralPanel; @@ -18,11 +24,8 @@ use eframe::egui::FontId; use eframe::egui::Frame; use eframe::egui::Layout; use eframe::egui::Margin; -use eframe::egui::Pos2; use eframe::egui::Style; use eframe::egui::TextStyle; -use eframe::egui::Vec2; -use eframe::egui::ViewportCommand; use font_loader::system_fonts; use font_loader::system_fonts::FontPropertyBuilder; use komorebi_client::KomorebiTheme; @@ -146,16 +149,43 @@ impl Komobar { Self::add_custom_font(ctx, font_family); } - if let Some(viewport) = &config.viewport { - let dpi = DPI.load(Ordering::SeqCst); - if let Some(position) = viewport.position { - let pos2 = Pos2::new(position.x / dpi, position.y / dpi); - ctx.send_viewport_cmd(ViewportCommand::OuterPosition(pos2)); - } + let position = config.position.clone().unwrap_or(PositionConfig { + start: Some(Position { + x: MONITOR_LEFT.load(Ordering::SeqCst) as f32, + y: MONITOR_TOP.load(Ordering::SeqCst) as f32, + }), + end: Some(Position { + x: MONITOR_RIGHT.load(Ordering::SeqCst) as f32, + y: BAR_HEIGHT, + }), + }); - if let Some(position) = viewport.inner_size { - let vec2 = Vec2::new(position.x / dpi, position.y / dpi); - ctx.send_viewport_cmd(ViewportCommand::InnerSize(vec2)); + if let Some(hwnd) = process_hwnd() { + let start = position.start.unwrap_or(Position { + x: MONITOR_LEFT.load(Ordering::SeqCst) as f32, + y: MONITOR_TOP.load(Ordering::SeqCst) as f32, + }); + + let end = position.end.unwrap_or(Position { + x: MONITOR_RIGHT.load(Ordering::SeqCst) as f32, + y: BAR_HEIGHT, + }); + + let rect = komorebi_client::Rect { + left: start.x as i32, + top: start.y as i32, + right: end.x as i32, + bottom: end.y as i32, + }; + + let window = komorebi_client::Window::from(hwnd); + match window.set_position(&rect, false) { + Ok(_) => { + tracing::info!("updated bar position"); + } + Err(error) => { + tracing::error!("{}", error.to_string()) + } } } @@ -294,6 +324,8 @@ impl Komobar { scale_factor: cc.egui_ctx.native_pixels_per_point().unwrap_or(1.0), }; + komobar.apply_config(&cc.egui_ctx, &config, None); + // needs a double apply the first time for some reason komobar.apply_config(&cc.egui_ctx, &config, None); komobar diff --git a/komorebi-bar/src/config.rs b/komorebi-bar/src/config.rs index dae9fc9d..8d59f6fb 100644 --- a/komorebi-bar/src/config.rs +++ b/komorebi-bar/src/config.rs @@ -7,13 +7,15 @@ use komorebi_client::Rect; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; +use std::collections::HashMap; use std::path::PathBuf; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] /// The `komorebi.bar.json` configuration file reference for `v0.1.30` pub struct KomobarConfig { - /// Viewport options (see: https://docs.rs/egui/latest/egui/viewport/struct.ViewportBuilder.html) - pub viewport: Option, + /// Bar positioning options + #[serde(alias = "viewport")] + pub position: Option, /// Frame options (see: https://docs.rs/egui/latest/egui/containers/struct.Frame.html) pub frame: Option, /// Monitor options @@ -32,12 +34,43 @@ pub struct KomobarConfig { pub right_widgets: Vec, } +impl KomobarConfig { + pub fn aliases(raw: &str) { + let mut map = HashMap::new(); + map.insert("position", ["viewport"]); + map.insert("end", ["inner_frame"]); + + let mut display = false; + + for aliases in map.values() { + for a in aliases { + if raw.contains(a) { + display = true; + } + } + } + + if display { + println!("\nYour bar 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}""#); + } + } + } + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct ViewportConfig { +pub struct PositionConfig { /// The desired starting position of the bar (0,0 = top left of the screen) - pub position: Option, + #[serde(alias = "position")] + pub start: Option, /// The desired size of the bar from the starting position (usually monitor width x desired height) - pub inner_size: Option, + #[serde(alias = "inner_size")] + pub end: Option, } #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] diff --git a/komorebi-bar/src/main.rs b/komorebi-bar/src/main.rs index 7474a651..c7f3aa26 100644 --- a/komorebi-bar/src/main.rs +++ b/komorebi-bar/src/main.rs @@ -14,9 +14,8 @@ mod widget; use crate::bar::Komobar; use crate::config::KomobarConfig; use crate::config::Position; -use atomic_float::AtomicF32; +use crate::config::PositionConfig; use clap::Parser; -use color_eyre::eyre::bail; use eframe::egui::ViewportBuilder; use font_loader::system_fonts; use hotwatch::EventKind; @@ -31,12 +30,23 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use std::time::Duration; use tracing_subscriber::EnvFilter; +use windows::Win32::Foundation::BOOL; +use windows::Win32::Foundation::HWND; +use windows::Win32::Foundation::LPARAM; +use windows::Win32::System::Threading::GetCurrentProcessId; +use windows::Win32::System::Threading::GetCurrentThreadId; use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext; use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; +use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows; +use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId; pub static WIDGET_SPACING: f32 = 10.0; + pub static MAX_LABEL_WIDTH: AtomicI32 = AtomicI32::new(400); -pub static DPI: AtomicF32 = AtomicF32::new(1.0); +pub static MONITOR_LEFT: AtomicI32 = AtomicI32::new(0); +pub static MONITOR_TOP: AtomicI32 = AtomicI32::new(0); +pub static MONITOR_RIGHT: AtomicI32 = AtomicI32::new(0); +pub static BAR_HEIGHT: f32 = 50.0; #[derive(Parser)] #[clap(author, about, version)] @@ -53,36 +63,41 @@ struct Opts { /// Write an example komorebi.bar.json to disk #[clap(long)] quickstart: bool, + /// Print a list of aliases that can be renamed to canonical variants + #[clap(long)] + #[clap(hide = true)] + aliases: bool, } -macro_rules! as_ptr { - ($value:expr) => { - $value as *mut core::ffi::c_void - }; -} - -pub fn dpi_for_monitor(hmonitor: isize) -> color_eyre::Result { - use windows::Win32::Graphics::Gdi::HMONITOR; - use windows::Win32::UI::HiDpi::GetDpiForMonitor; - use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI; - - let mut dpi_x = u32::default(); - let mut dpi_y = u32::default(); - +extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL { unsafe { - match GetDpiForMonitor( - HMONITOR(as_ptr!(hmonitor)), - MDT_EFFECTIVE_DPI, - std::ptr::addr_of_mut!(dpi_x), - std::ptr::addr_of_mut!(dpi_y), - ) { - Ok(_) => {} - Err(error) => bail!(error), + let mut process_id = 0; + GetWindowThreadProcessId(hwnd, Some(&mut process_id)); + + if process_id == GetCurrentProcessId() { + *(lparam.0 as *mut HWND) = hwnd; + BOOL::from(false) // Stop enumeration + } else { + BOOL::from(true) // Continue enumeration } } +} - #[allow(clippy::cast_precision_loss)] - Ok(dpi_y as f32 / 96.0) +fn process_hwnd() -> Option { + unsafe { + let mut hwnd = HWND::default(); + let _ = EnumThreadWindows( + GetCurrentThreadId(), + Some(enum_window), + LPARAM(&mut hwnd as *mut HWND as isize), + ); + + if hwnd.0 as isize == 0 { + None + } else { + Some(hwnd.0 as isize) + } + } } fn main() -> color_eyre::Result<()> { @@ -166,7 +181,7 @@ fn main() -> color_eyre::Result<()> { Option::from, ); - let config = match config_path { + let mut config = match config_path { None => { let komorebi_bar_json = include_str!("../../docs/komorebi.bar.example.json").to_string(); @@ -180,10 +195,12 @@ fn main() -> color_eyre::Result<()> { KomobarConfig::read(&default_config_path)? } Some(ref config) => { - tracing::info!( - "found configuration file: {}", - config.as_path().to_string_lossy() - ); + if !opts.aliases { + tracing::info!( + "found configuration file: {}", + config.as_path().to_string_lossy() + ); + } KomobarConfig::read(config)? } @@ -191,46 +208,65 @@ fn main() -> color_eyre::Result<()> { let config_path = config_path.unwrap_or(default_config_path); + if opts.aliases { + KomobarConfig::aliases(&std::fs::read_to_string(&config_path)?); + std::process::exit(0); + } + let state = serde_json::from_str::(&komorebi_client::send_query( &SocketMessage::State, )?)?; - let dpi = dpi_for_monitor(state.monitors.elements()[config.monitor.index].id())?; - DPI.store(dpi, Ordering::SeqCst); + MONITOR_RIGHT.store( + state.monitors.elements()[config.monitor.index].size().right, + Ordering::SeqCst, + ); - let mut viewport_builder = ViewportBuilder::default() - .with_decorations(false) - // .with_transparent(config.transparent) - .with_taskbar(false) - .with_position(Position { - x: state.monitors.elements()[config.monitor.index].size().left as f32 / dpi, - y: state.monitors.elements()[config.monitor.index].size().top as f32 / dpi, - }) - .with_inner_size({ - Position { - x: state.monitors.elements()[config.monitor.index].size().right as f32 / dpi, - y: 50.0 / dpi, - } - }); + MONITOR_TOP.store( + state.monitors.elements()[config.monitor.index].size().top, + Ordering::SeqCst, + ); - if let Some(viewport) = &config.viewport { - if let Some(mut position) = &viewport.position { - position.x /= dpi; - position.y /= dpi; + MONITOR_TOP.store( + state.monitors.elements()[config.monitor.index].size().left, + Ordering::SeqCst, + ); - let b = viewport_builder.clone(); - viewport_builder = b.with_position(position); + match config.position { + None => { + config.position = Some(PositionConfig { + start: Some(Position { + x: state.monitors.elements()[config.monitor.index].size().left as f32, + y: state.monitors.elements()[config.monitor.index].size().top as f32, + }), + end: Some(Position { + x: state.monitors.elements()[config.monitor.index].size().right as f32, + y: 50.0, + }), + }) } + Some(ref mut position) => { + if position.start.is_none() { + position.start = Some(Position { + x: state.monitors.elements()[config.monitor.index].size().left as f32, + y: state.monitors.elements()[config.monitor.index].size().top as f32, + }); + } - if let Some(mut inner_size) = &viewport.inner_size { - inner_size.x /= dpi; - inner_size.y /= dpi; - - let b = viewport_builder.clone(); - viewport_builder = b.with_inner_size(inner_size); + if position.end.is_none() { + position.end = Some(Position { + x: state.monitors.elements()[config.monitor.index].size().right as f32, + y: 50.0, + }) + } } } + let viewport_builder = ViewportBuilder::default() + .with_decorations(false) + // .with_transparent(config.transparent) + .with_taskbar(false); + let native_options = eframe::NativeOptions { viewport: viewport_builder, ..Default::default() diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 12e188b4..b914944a 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -2050,7 +2050,7 @@ if (!(Get-Process komorebi-bar -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 static_config = arg.config.clone().map_or_else( || { let komorebi_json = HOME_DIR.join("komorebi.json"); if komorebi_json.is_file() { @@ -2062,12 +2062,30 @@ if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue)) Option::from, ); + let bar_config = arg.config.map_or_else( + || { + let bar_json = HOME_DIR.join("komorebi.bar.json"); + if bar_json.is_file() { + Option::from(bar_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); } + + if bar_config.is_some() { + let output = Command::new("komorebi-bar.exe").arg("--aliases").output()?; + let stdout = String::from_utf8(output.stdout)?; + println!("{stdout}"); + } } SubCommand::Stop(arg) => { if arg.whkd {