feat(wm): notify subscribers of monitor events

This commit allows notifying the subscribers of any monitor events like
display connection change or work area change.
This commit is contained in:
alex-ds13
2025-01-24 01:17:59 +00:00
committed by Jeezy
parent 4123c9a0e2
commit 5c3c3659b5
4 changed files with 44 additions and 19 deletions
+1
View File
@@ -34,6 +34,7 @@ pub use komorebi::core::StackbarMode;
pub use komorebi::core::StateQuery; pub use komorebi::core::StateQuery;
pub use komorebi::core::WindowKind; pub use komorebi::core::WindowKind;
pub use komorebi::monitor::Monitor; pub use komorebi::monitor::Monitor;
pub use komorebi::monitor_reconciliator::MonitorNotification;
pub use komorebi::ring::Ring; pub use komorebi::ring::Ring;
pub use komorebi::window::Window; pub use komorebi::window::Window;
pub use komorebi::window_manager_event::WindowManagerEvent; pub use komorebi::window_manager_event::WindowManagerEvent;
+2
View File
@@ -32,6 +32,7 @@ pub mod workspace;
pub mod workspace_reconciliator; pub mod workspace_reconciliator;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use monitor_reconciliator::MonitorNotification;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
@@ -283,6 +284,7 @@ pub fn current_virtual_desktop() -> Option<Vec<u8>> {
pub enum NotificationEvent { pub enum NotificationEvent {
WindowManager(WindowManagerEvent), WindowManager(WindowManagerEvent),
Socket(SocketMessage), Socket(SocketMessage),
Monitor(MonitorNotification),
} }
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
+7 -7
View File
@@ -114,7 +114,7 @@ impl Hidden {
"WM_POWERBROADCAST event received - resume from suspend" "WM_POWERBROADCAST event received - resume from suspend"
); );
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::ResumingFromSuspendedState, monitor_reconciliator::MonitorNotification::ResumingFromSuspendedState,
); );
LRESULT(0) LRESULT(0)
} }
@@ -124,7 +124,7 @@ impl Hidden {
"WM_POWERBROADCAST event received - entering suspended state" "WM_POWERBROADCAST event received - entering suspended state"
); );
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::EnteringSuspendedState, monitor_reconciliator::MonitorNotification::EnteringSuspendedState,
); );
LRESULT(0) LRESULT(0)
} }
@@ -137,14 +137,14 @@ impl Hidden {
tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_LOCK - screen locked"); tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_LOCK - screen locked");
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::SessionLocked, monitor_reconciliator::MonitorNotification::SessionLocked,
); );
} }
WTS_SESSION_UNLOCK => { WTS_SESSION_UNLOCK => {
tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_UNLOCK - screen unlocked"); tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_UNLOCK - screen unlocked");
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::SessionUnlocked, monitor_reconciliator::MonitorNotification::SessionUnlocked,
); );
} }
_ => {} _ => {}
@@ -165,7 +165,7 @@ impl Hidden {
); );
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::ResolutionScalingChanged, monitor_reconciliator::MonitorNotification::ResolutionScalingChanged,
); );
LRESULT(0) LRESULT(0)
} }
@@ -179,7 +179,7 @@ impl Hidden {
); );
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::WorkAreaChanged, monitor_reconciliator::MonitorNotification::WorkAreaChanged,
); );
} }
LRESULT(0) LRESULT(0)
@@ -193,7 +193,7 @@ impl Hidden {
"WM_DEVICECHANGE event received with DBT_DEVNODES_CHANGED - display added or removed" "WM_DEVICECHANGE event received with DBT_DEVNODES_CHANGED - display added or removed"
); );
monitor_reconciliator::send_notification( monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::DisplayConnectionChange, monitor_reconciliator::MonitorNotification::DisplayConnectionChange,
); );
} }
+34 -12
View File
@@ -5,13 +5,20 @@ use crate::core::Rect;
use crate::monitor; use crate::monitor;
use crate::monitor::Monitor; use crate::monitor::Monitor;
use crate::monitor_reconciliator::hidden::Hidden; use crate::monitor_reconciliator::hidden::Hidden;
use crate::notify_subscribers;
use crate::MonitorConfig; use crate::MonitorConfig;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::WindowManager; use crate::WindowManager;
use crate::WindowsApi; use crate::WindowsApi;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicConsume; use crossbeam_utils::atomic::AtomicConsume;
use parking_lot::Mutex; use parking_lot::Mutex;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -20,7 +27,9 @@ use std::sync::OnceLock;
pub mod hidden; pub mod hidden;
pub enum Notification { #[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "content")]
pub enum MonitorNotification {
ResolutionScalingChanged, ResolutionScalingChanged,
WorkAreaChanged, WorkAreaChanged,
DisplayConnectionChange, DisplayConnectionChange,
@@ -32,23 +41,24 @@ pub enum Notification {
static ACTIVE: AtomicBool = AtomicBool::new(true); static ACTIVE: AtomicBool = AtomicBool::new(true);
static CHANNEL: OnceLock<(Sender<Notification>, Receiver<Notification>)> = OnceLock::new(); static CHANNEL: OnceLock<(Sender<MonitorNotification>, Receiver<MonitorNotification>)> =
OnceLock::new();
static MONITOR_CACHE: OnceLock<Mutex<HashMap<String, MonitorConfig>>> = OnceLock::new(); static MONITOR_CACHE: OnceLock<Mutex<HashMap<String, MonitorConfig>>> = OnceLock::new();
pub fn channel() -> &'static (Sender<Notification>, Receiver<Notification>) { pub fn channel() -> &'static (Sender<MonitorNotification>, Receiver<MonitorNotification>) {
CHANNEL.get_or_init(|| crossbeam_channel::bounded(1)) CHANNEL.get_or_init(|| crossbeam_channel::bounded(1))
} }
fn event_tx() -> Sender<Notification> { fn event_tx() -> Sender<MonitorNotification> {
channel().0.clone() channel().0.clone()
} }
fn event_rx() -> Receiver<Notification> { fn event_rx() -> Receiver<MonitorNotification> {
channel().1.clone() channel().1.clone()
} }
pub fn send_notification(notification: Notification) { pub fn send_notification(notification: MonitorNotification) {
if event_tx().try_send(notification).is_err() { if event_tx().try_send(notification).is_err() {
tracing::warn!("channel is full; dropping notification") tracing::warn!("channel is full; dropping notification")
} }
@@ -125,7 +135,8 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
if !ACTIVE.load_consume() { if !ACTIVE.load_consume() {
if matches!( if matches!(
notification, notification,
Notification::ResumingFromSuspendedState | Notification::SessionUnlocked MonitorNotification::ResumingFromSuspendedState
| MonitorNotification::SessionUnlocked
) { ) {
tracing::debug!( tracing::debug!(
"reactivating reconciliator - system has resumed from suspended state or session has been unlocked" "reactivating reconciliator - system has resumed from suspended state or session has been unlocked"
@@ -140,17 +151,20 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let mut wm = wm.lock(); let mut wm = wm.lock();
let initial_state = State::from(wm.as_ref());
match notification { match notification {
Notification::EnteringSuspendedState | Notification::SessionLocked => { MonitorNotification::EnteringSuspendedState | MonitorNotification::SessionLocked => {
tracing::debug!( tracing::debug!(
"deactivating reconciliator until system resumes from suspended state or session is unlocked" "deactivating reconciliator until system resumes from suspended state or session is unlocked"
); );
ACTIVE.store(false, Ordering::SeqCst); ACTIVE.store(false, Ordering::SeqCst);
} }
Notification::ResumingFromSuspendedState | Notification::SessionUnlocked => { MonitorNotification::ResumingFromSuspendedState
| MonitorNotification::SessionUnlocked => {
// this is only handled above if the reconciliator is paused // this is only handled above if the reconciliator is paused
} }
Notification::WorkAreaChanged => { MonitorNotification::WorkAreaChanged => {
tracing::debug!("handling work area changed notification"); tracing::debug!("handling work area changed notification");
let offset = wm.work_area_offset; let offset = wm.work_area_offset;
for monitor in wm.monitors_mut() { for monitor in wm.monitors_mut() {
@@ -182,7 +196,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
} }
} }
} }
Notification::ResolutionScalingChanged => { MonitorNotification::ResolutionScalingChanged => {
tracing::debug!("handling resolution/scaling changed notification"); tracing::debug!("handling resolution/scaling changed notification");
let offset = wm.work_area_offset; let offset = wm.work_area_offset;
for monitor in wm.monitors_mut() { for monitor in wm.monitors_mut() {
@@ -229,7 +243,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
} }
} }
} }
Notification::DisplayConnectionChange => { MonitorNotification::DisplayConnectionChange => {
tracing::debug!("handling display connection change notification"); tracing::debug!("handling display connection change notification");
let mut monitor_cache = MONITOR_CACHE let mut monitor_cache = MONITOR_CACHE
.get_or_init(|| Mutex::new(HashMap::new())) .get_or_init(|| Mutex::new(HashMap::new()))
@@ -411,6 +425,14 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
} }
} }
} }
notify_subscribers(
Notification {
event: NotificationEvent::Monitor(notification),
state: wm.as_ref().into(),
},
initial_state.has_been_modified(&wm),
)?;
} }
Ok(()) Ok(())