diff --git a/crates/komorebi-bar-core/src/lib.rs b/crates/komorebi-bar-core/src/lib.rs index b65fd141..9088d849 100644 --- a/crates/komorebi-bar-core/src/lib.rs +++ b/crates/komorebi-bar-core/src/lib.rs @@ -16,3 +16,5 @@ pub mod config; pub mod render; pub mod widgets; + +pub mod platform; diff --git a/crates/komorebi-bar-core/src/platform.rs b/crates/komorebi-bar-core/src/platform.rs new file mode 100644 index 00000000..635e5dc3 --- /dev/null +++ b/crates/komorebi-bar-core/src/platform.rs @@ -0,0 +1,48 @@ +//! What the widgets need from the operating system and what differs between the bars. +//! +//! A shell implements [`BarPlatform`] and calls [`install`] first thing in `main`; the widgets +//! reach it through [`platform`] when they are built or clicked. + +use std::io; +use std::sync::OnceLock; + +/// A tool the bar opens when a widget is clicked. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SystemTool { + /// The task manager (Task Manager, Activity Monitor) + TaskManager, + /// The operating system's power and battery settings + PowerSettings, + /// The operating system's network settings + NetworkSettings, + /// A URL, in the default browser + Url(String), +} + +pub trait BarPlatform: Send + Sync { + /// Opens a tool; the result is whether it could be started. + fn open_system_tool(&self, tool: SystemTool) -> io::Result<()>; + + /// The GitHub API URL that says which release of this platform's komorebi is the latest. + fn latest_release_api_url(&self) -> &'static str; + + /// The glyph the date widget shows as its calendar icon. + fn calendar_icon(&self) -> &'static str; +} + +static PLATFORM: OnceLock<&'static dyn BarPlatform> = OnceLock::new(); + +/// Sets the platform of the process. The first call wins; a second one is ignored. +pub fn install(platform: &'static dyn BarPlatform) { + let _ = PLATFORM.set(platform); +} + +/// # Panics +/// +/// Panics if [`install`] has not been called: a shell must install its platform before it builds +/// a widget. +pub fn platform() -> &'static dyn BarPlatform { + *PLATFORM + .get() + .expect("komorebi_bar_core::platform::install must be called first thing in main") +} diff --git a/komorebi-bar/src/main.rs b/komorebi-bar/src/main.rs index 4ca843f5..89b25c77 100644 --- a/komorebi-bar/src/main.rs +++ b/komorebi-bar/src/main.rs @@ -1,5 +1,7 @@ mod bar; mod config; +mod os; +pub use komorebi_bar_core::platform; pub use komorebi_bar_core::render; pub use komorebi_bar_core::selected_frame; pub use komorebi_bar_core::ui; @@ -96,6 +98,8 @@ pub enum KomorebiEvent { } fn main() -> color_eyre::Result<()> { + komorebi_bar_core::platform::install(&os::WindowsBar); + unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }?; let opts: Opts = Opts::parse(); diff --git a/komorebi-bar/src/os.rs b/komorebi-bar/src/os.rs new file mode 100644 index 00000000..2fca94c4 --- /dev/null +++ b/komorebi-bar/src/os.rs @@ -0,0 +1,29 @@ +use egui_phosphor::regular::CALENDAR_DOTS; +use komorebi_bar_core::platform::BarPlatform; +use komorebi_bar_core::platform::SystemTool; +use std::io; +use std::process::Command; + +pub struct WindowsBar; + +impl BarPlatform for WindowsBar { + fn open_system_tool(&self, tool: SystemTool) -> io::Result<()> { + match tool { + SystemTool::TaskManager => Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn(), + SystemTool::PowerSettings => Command::new("cmd.exe") + .args(["/C", "start", "ms-settings:batterysaver"]) + .spawn(), + SystemTool::NetworkSettings => Command::new("cmd.exe").args(["/C", "ncpa"]).spawn(), + SystemTool::Url(url) => Command::new("explorer.exe").args([url]).spawn(), + } + .map(|_| ()) + } + + fn latest_release_api_url(&self) -> &'static str { + "https://api.github.com/repos/LGUG2Z/komorebi/releases/latest" + } + + fn calendar_icon(&self) -> &'static str { + CALENDAR_DOTS + } +} diff --git a/komorebi-for-mac/komorebi-bar/src/main.rs b/komorebi-for-mac/komorebi-bar/src/main.rs index 857092c2..c08f444c 100644 --- a/komorebi-for-mac/komorebi-bar/src/main.rs +++ b/komorebi-for-mac/komorebi-bar/src/main.rs @@ -1,5 +1,7 @@ mod bar; mod config; +mod os; +pub use komorebi_bar_core::platform; pub use komorebi_bar_core::render; pub use komorebi_bar_core::selected_frame; pub use komorebi_bar_core::ui; @@ -63,6 +65,8 @@ pub enum KomorebiEvent { } fn main() -> eyre::Result<()> { + komorebi_bar_core::platform::install(&os::MacBar); + let opts: Opts = Opts::parse(); #[cfg(feature = "schemars")] diff --git a/komorebi-for-mac/komorebi-bar/src/os.rs b/komorebi-for-mac/komorebi-bar/src/os.rs new file mode 100644 index 00000000..c3df076a --- /dev/null +++ b/komorebi-for-mac/komorebi-bar/src/os.rs @@ -0,0 +1,35 @@ +use egui_phosphor::regular::CALENDAR; +use komorebi_bar_core::platform::BarPlatform; +use komorebi_bar_core::platform::SystemTool; +use std::io; +use std::process::Command; + +pub struct MacBar; + +impl BarPlatform for MacBar { + fn open_system_tool(&self, tool: SystemTool) -> io::Result<()> { + match tool { + SystemTool::TaskManager => Command::new("open") + .args(["-a", "Activity Monitor"]) + .spawn(), + // cmd.exe does not exist on macOS: this is what the battery widget does today + SystemTool::PowerSettings => Command::new("cmd.exe") + .args(["/C", "start", "ms-settings:batterysaver"]) + .spawn(), + SystemTool::NetworkSettings => Command::new("open") + .args(["x-apple.systempreferences:com.apple.Network-Settings.extension"]) + .spawn(), + // explorer.exe does not exist on macOS: this is what the update widget does today + SystemTool::Url(url) => Command::new("explorer.exe").args([url]).spawn(), + } + .map(|_| ()) + } + + fn latest_release_api_url(&self) -> &'static str { + "https://api.github.com/repos/LGUG2Z/komorebi-for-mac/releases/latest" + } + + fn calendar_icon(&self) -> &'static str { + CALENDAR + } +}