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

View File

@@ -34,6 +34,7 @@ pub use komorebi::core::StackbarMode;
pub use komorebi::core::StateQuery;
pub use komorebi::core::WindowKind;
pub use komorebi::monitor::Monitor;
pub use komorebi::monitor_reconciliator::MonitorNotification;
pub use komorebi::ring::Ring;
pub use komorebi::window::Window;
pub use komorebi::window_manager_event::WindowManagerEvent;

View File

@@ -32,6 +32,7 @@ pub mod workspace;
pub mod workspace_reconciliator;
use lazy_static::lazy_static;
use monitor_reconciliator::MonitorNotification;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::fs::File;
@@ -283,6 +284,7 @@ pub fn current_virtual_desktop() -> Option<Vec<u8>> {
pub enum NotificationEvent {
WindowManager(WindowManagerEvent),
Socket(SocketMessage),
Monitor(MonitorNotification),
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]

View File

@@ -114,7 +114,7 @@ impl Hidden {
"WM_POWERBROADCAST event received - resume from suspend"
);
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::ResumingFromSuspendedState,
monitor_reconciliator::MonitorNotification::ResumingFromSuspendedState,
);
LRESULT(0)
}
@@ -124,7 +124,7 @@ impl Hidden {
"WM_POWERBROADCAST event received - entering suspended state"
);
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::EnteringSuspendedState,
monitor_reconciliator::MonitorNotification::EnteringSuspendedState,
);
LRESULT(0)
}
@@ -137,14 +137,14 @@ impl Hidden {
tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_LOCK - screen locked");
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::SessionLocked,
monitor_reconciliator::MonitorNotification::SessionLocked,
);
}
WTS_SESSION_UNLOCK => {
tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_UNLOCK - screen unlocked");
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::SessionUnlocked,
monitor_reconciliator::MonitorNotification::SessionUnlocked,
);
}
_ => {}
@@ -165,7 +165,7 @@ impl Hidden {
);
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::ResolutionScalingChanged,
monitor_reconciliator::MonitorNotification::ResolutionScalingChanged,
);
LRESULT(0)
}
@@ -179,7 +179,7 @@ impl Hidden {
);
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::WorkAreaChanged,
monitor_reconciliator::MonitorNotification::WorkAreaChanged,
);
}
LRESULT(0)
@@ -193,7 +193,7 @@ impl Hidden {
"WM_DEVICECHANGE event received with DBT_DEVNODES_CHANGED - display added or removed"
);
monitor_reconciliator::send_notification(
monitor_reconciliator::Notification::DisplayConnectionChange,
monitor_reconciliator::MonitorNotification::DisplayConnectionChange,
);
}

View File

@@ -5,13 +5,20 @@ use crate::core::Rect;
use crate::monitor;
use crate::monitor::Monitor;
use crate::monitor_reconciliator::hidden::Hidden;
use crate::notify_subscribers;
use crate::MonitorConfig;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::WindowManager;
use crate::WindowsApi;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicConsume;
use parking_lot::Mutex;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
@@ -20,7 +27,9 @@ use std::sync::OnceLock;
pub mod hidden;
pub enum Notification {
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "content")]
pub enum MonitorNotification {
ResolutionScalingChanged,
WorkAreaChanged,
DisplayConnectionChange,
@@ -32,23 +41,24 @@ pub enum Notification {
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();
pub fn channel() -> &'static (Sender<Notification>, Receiver<Notification>) {
pub fn channel() -> &'static (Sender<MonitorNotification>, Receiver<MonitorNotification>) {
CHANNEL.get_or_init(|| crossbeam_channel::bounded(1))
}
fn event_tx() -> Sender<Notification> {
fn event_tx() -> Sender<MonitorNotification> {
channel().0.clone()
}
fn event_rx() -> Receiver<Notification> {
fn event_rx() -> Receiver<MonitorNotification> {
channel().1.clone()
}
pub fn send_notification(notification: Notification) {
pub fn send_notification(notification: MonitorNotification) {
if event_tx().try_send(notification).is_err() {
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 matches!(
notification,
Notification::ResumingFromSuspendedState | Notification::SessionUnlocked
MonitorNotification::ResumingFromSuspendedState
| MonitorNotification::SessionUnlocked
) {
tracing::debug!(
"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 initial_state = State::from(wm.as_ref());
match notification {
Notification::EnteringSuspendedState | Notification::SessionLocked => {
MonitorNotification::EnteringSuspendedState | MonitorNotification::SessionLocked => {
tracing::debug!(
"deactivating reconciliator until system resumes from suspended state or session is unlocked"
);
ACTIVE.store(false, Ordering::SeqCst);
}
Notification::ResumingFromSuspendedState | Notification::SessionUnlocked => {
MonitorNotification::ResumingFromSuspendedState
| MonitorNotification::SessionUnlocked => {
// this is only handled above if the reconciliator is paused
}
Notification::WorkAreaChanged => {
MonitorNotification::WorkAreaChanged => {
tracing::debug!("handling work area changed notification");
let offset = wm.work_area_offset;
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");
let offset = wm.work_area_offset;
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");
let mut monitor_cache = MONITOR_CACHE
.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(())