refactor(windows): Monitor.id is the shared MonitorId, with the same JSON

Monitor.id was an isize HMONITOR; it is now komorebi_backend::MonitorId (the
type WindowsBackend::monitors already returned). A serde adapter
(hmonitor_json) writes it with serialize_i64(to_hmonitor()) and reads an isize
through from_hmonitor, bit for bit, so the JSON is the same plain number as
before (including negative handles); the schemars schema is declared as isize.
serial_number_id was already Option<String> (the shared shape), so it is
unchanged. monitor::new / Monitor::new keep an isize parameter (every caller
passes an HMONITOR); the 12 uses that need the raw handle call
.to_hmonitor() (WindowsApi::monitor, wallpaper, DPI, zoomed checks, ids
compared with monitor_from_window/point). MonitorInformation.id stays isize.

BEHAVIOUR (JSON-identical by construction, not proven by running): the new
unit tests of the previous revision pin the JSON against a real capture,
but the Windows crate's tests cannot run on this host. Gate needed: any
e2e scenario that reads komorebic state or saves/loads it, and monitor
reconciliation / unknown_monitor. Verified: Windows clippy -D warnings via
tools/fast-check PASS.
This commit is contained in:
LGUG2Z committed 2026-10-31 11:19:57 -07:00
1 parent 12ef908aa9
commit 9daa4d726a
5 files changed
+38 -17

No files matched your search

+2 -2
View File
@@ -741,8 +741,8 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let foreground_hwnd = WindowsApi::foreground_window().unwrap_or_default();
let foreground_monitor_id =
WindowsApi::monitor_from_window(foreground_hwnd);
let is_maximized =
foreground_monitor_id == m.id && WindowsApi::is_zoomed(foreground_hwnd);
let is_maximized = foreground_monitor_id == m.id.to_hmonitor()
&& WindowsApi::is_zoomed(foreground_hwnd);
if is_maximized {
tracing::trace!(
+24 -5
View File
@@ -13,6 +13,7 @@ use crate::border_manager::BORDER_OFFSET;
use crate::border_manager::BORDER_WIDTH;
use crate::core::Rect;
use crate::windows_backend::backend;
use komorebi_backend::MonitorId;
use komorebi_backend::WindowId;
use crate::DEFAULT_CONTAINER_PADDING;
@@ -29,10 +30,28 @@ use crate::workspace::Workspace;
use crate::workspace::WorkspaceGlobals;
use crate::workspace::WorkspaceLayer;
/// `Monitor::id` is a `MonitorId` in Rust and the same plain JSON number as the `isize` handle it
/// was before: `komorebic state` and the saved state files do not change.
mod hmonitor_json {
use komorebi_backend::MonitorId;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serializer;
pub fn serialize<S: Serializer>(id: &MonitorId, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_i64(id.to_hmonitor() as i64)
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<MonitorId, D::Error> {
Ok(MonitorId::from_hmonitor(isize::deserialize(deserializer)?))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Monitor {
pub id: isize,
#[serde(with = "hmonitor_json")]
#[cfg_attr(feature = "schemars", schemars(with = "isize"))]
pub id: MonitorId,
pub name: String,
pub device: String,
pub device_id: String,
@@ -67,7 +86,7 @@ pub struct MonitorInformation {
impl From<&Monitor> for MonitorInformation {
fn from(monitor: &Monitor) -> Self {
Self {
id: monitor.id,
id: monitor.id.to_hmonitor(),
name: monitor.name.clone(),
device: monitor.device.clone(),
device_id: monitor.device_id.clone(),
@@ -90,7 +109,7 @@ pub fn new(
workspaces.elements_mut().push_back(Workspace::default());
Monitor {
id,
id: MonitorId::from_hmonitor(id),
name,
device,
device_id,
@@ -133,7 +152,7 @@ impl Monitor {
pub fn placeholder() -> Self {
Self {
id: 0,
id: MonitorId(0),
name: "PLACEHOLDER".to_string(),
device: "".to_string(),
device_id: "".to_string(),
@@ -171,7 +190,7 @@ impl Monitor {
pub fn load_focused_workspace(&mut self, mouse_follows_focus: bool) -> eyre::Result<()> {
let focused_idx = self.focused_workspace_idx();
let hmonitor = self.id;
let hmonitor = self.id.to_hmonitor();
let monitor_wp = self.wallpaper.clone();
for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() {
if i == focused_idx {
+3 -3
View File
@@ -293,7 +293,7 @@ where
let mut should_update = false;
// Update work areas as necessary
if let Ok(reference) = WindowsApi::monitor(monitor.id)
if let Ok(reference) = WindowsApi::monitor(monitor.id.to_hmonitor())
&& reference.work_area_size != monitor.work_area_size
{
monitor.work_area_size = Rect {
@@ -325,7 +325,7 @@ where
let mut should_update = false;
// Update sizes and work areas as necessary
if let Ok(reference) = WindowsApi::monitor(monitor.id) {
if let Ok(reference) = WindowsApi::monitor(monitor.id.to_hmonitor()) {
if reference.work_area_size != monitor.work_area_size {
monitor.work_area_size = Rect {
left: reference.work_area_size.left,
@@ -1275,7 +1275,7 @@ mod tests {
assert_eq!(monitors.len(), 1, "Expected one monitor");
// hmonitor
assert_eq!(monitors[0].id, 1);
assert_eq!(monitors[0].id.to_hmonitor(), 1);
// device name
assert_eq!(monitors[0].name, String::from("DISPLAY1"));
+8 -6
View File
@@ -771,7 +771,7 @@ impl WindowManager {
let focused_workspace_idx = monitor.focused_workspace_idx();
monitor.update_workspace_globals(focused_workspace_idx, offset);
let hmonitor = monitor.id;
let hmonitor = monitor.id.to_hmonitor();
let monitor_wp = monitor.wallpaper.clone();
let workspace = monitor
.focused_workspace_mut()
@@ -1572,7 +1572,7 @@ impl WindowManager {
.get_mut(monitor_idx)
.ok_or_eyre("there is no monitor")?;
let hmonitor = monitor.id;
let hmonitor = monitor.id.to_hmonitor();
let monitor_wp = monitor.wallpaper.clone();
let workspace = monitor
@@ -2589,12 +2589,14 @@ impl WindowManager {
let a = self
.focused_monitor()
.ok_or_eyre("there is no monitor focused monitor")?
.id;
.id
.to_hmonitor();
let b = self
.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_eyre("there is no monitor at this index")?
.id;
.id
.to_hmonitor();
if !WindowsApi::monitors_have_same_dpi(a, b)? {
self.update_focused_workspace(self.mouse_follows_focus, true)?;
@@ -3766,7 +3768,7 @@ impl WindowManager {
let hmonitor = WindowsApi::monitor_from_window(window.hwnd);
for (i, monitor) in self.monitors().iter().enumerate() {
if monitor.id == hmonitor {
if monitor.id.to_hmonitor() == hmonitor {
return Option::from(i);
}
}
@@ -3789,7 +3791,7 @@ impl WindowManager {
let hmonitor = WindowsApi::monitor_from_point(WindowsApi::cursor_pos().ok()?);
for (i, monitor) in self.monitors().iter().enumerate() {
if monitor.id == hmonitor {
if monitor.id.to_hmonitor() == hmonitor {
return Option::from(i);
}
}
+1 -1
View File
@@ -110,7 +110,7 @@ impl Backend for WindowsBackend {
.map(|(_, hmonitor)| {
let monitor = WindowsApi::monitor(hmonitor).map_err(platform)?;
Ok(MonitorInfo {
id: MonitorId::from_hmonitor(monitor.id),
id: monitor.id,
size: monitor.size,
work_area: monitor.work_area_size,
name: Some(monitor.name),