feat(bar): dpi-awareness + position hotloading

This commit ensures that the komorebi-bar.exe process is DPI aware and
applies DPI compensation to viewport.position and viewport.inner size
both on launch and on configuration reload.  viewport.position changes
are now hotloaded wihtout having to restart the process.

re #1024
This commit is contained in:
LGUG2Z
2024-10-04 15:57:34 -07:00
parent 2d1613b4d9
commit 20f370a51d
4 changed files with 72 additions and 13 deletions

7
Cargo.lock generated
View File

@@ -501,6 +501,12 @@ 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"
@@ -2705,6 +2711,7 @@ dependencies = [
name = "komorebi-bar"
version = "0.1.30"
dependencies = [
"atomic_float",
"chrono",
"clap",
"color-eyre",

View File

@@ -9,6 +9,7 @@ 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 }

View File

@@ -4,6 +4,7 @@ use crate::komorebi::Komorebi;
use crate::komorebi::KomorebiNotificationState;
use crate::widget::BarWidget;
use crate::widget::WidgetConfig;
use crate::DPI;
use crate::MAX_LABEL_WIDTH;
use crossbeam_channel::Receiver;
use eframe::egui::Align;
@@ -17,6 +18,7 @@ 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;
@@ -145,12 +147,14 @@ impl Komobar {
}
if let Some(viewport) = &config.viewport {
if let Some(inner_size) = viewport.inner_size {
let mut vec2 = Vec2::new(inner_size.x, inner_size.y * 2.0);
if self.scale_factor != 1.0 {
vec2 = Vec2::new(inner_size.x / self.scale_factor, inner_size.y * 2.0);
}
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));
}
if let Some(position) = viewport.inner_size {
let vec2 = Vec2::new(position.x / dpi, position.y / dpi);
ctx.send_viewport_cmd(ViewportCommand::InnerSize(vec2));
}
}

View File

@@ -14,7 +14,9 @@ mod widget;
use crate::bar::Komobar;
use crate::config::KomobarConfig;
use crate::config::Position;
use atomic_float::AtomicF32;
use clap::Parser;
use color_eyre::eyre::bail;
use eframe::egui::ViewportBuilder;
use font_loader::system_fonts;
use hotwatch::EventKind;
@@ -25,12 +27,16 @@ use std::io::BufReader;
use std::io::Read;
use std::path::PathBuf;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use tracing_subscriber::EnvFilter;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
pub static WIDGET_SPACING: f32 = 10.0;
pub static MAX_LABEL_WIDTH: AtomicI32 = AtomicI32::new(400);
pub static DPI: AtomicF32 = AtomicF32::new(1.0);
#[derive(Parser)]
#[clap(author, about, version)]
@@ -49,7 +55,39 @@ struct Opts {
quickstart: bool,
}
macro_rules! as_ptr {
($value:expr) => {
$value as *mut core::ffi::c_void
};
}
pub fn dpi_for_monitor(hmonitor: isize) -> color_eyre::Result<f32> {
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();
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),
}
}
#[allow(clippy::cast_precision_loss)]
Ok(dpi_y as f32 / 96.0)
}
fn main() -> color_eyre::Result<()> {
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }?;
let opts: Opts = Opts::parse();
if opts.schema {
@@ -157,30 +195,39 @@ fn main() -> color_eyre::Result<()> {
&SocketMessage::State,
)?)?;
let dpi = dpi_for_monitor(state.monitors.elements()[config.monitor.index].id())?;
DPI.store(dpi, 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,
y: state.monitors.elements()[config.monitor.index].size().top as f32,
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,
y: 20.0,
x: state.monitors.elements()[config.monitor.index].size().right as f32 / dpi,
y: 50.0 / dpi,
}
});
if let Some(viewport) = &config.viewport {
if let Some(position) = &viewport.position {
if let Some(mut position) = &viewport.position {
position.x /= dpi;
position.y /= dpi;
let b = viewport_builder.clone();
viewport_builder = b.with_position(*position);
viewport_builder = b.with_position(position);
}
if let Some(inner_size) = &viewport.inner_size {
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);
viewport_builder = b.with_inner_size(inner_size);
}
}