From d929f8e2f1cf526489ff68b266d2fe5a1bbfe69a Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 15 May 2026 11:45:32 +0100 Subject: [PATCH] implements PLN-0060 focused window ownership API --- .../prometeu-system/src/os/facades/window.rs | 5 + .../src/services/windows/window_manager.rs | 99 +++++++++++++++++++ discussion/index.ndjson | 2 +- ...-0060-plan-focused-window-ownership-api.md | 12 +-- 4 files changed, 111 insertions(+), 7 deletions(-) diff --git a/crates/console/prometeu-system/src/os/facades/window.rs b/crates/console/prometeu-system/src/os/facades/window.rs index 5e6c8c90..db08e9a3 100644 --- a/crates/console/prometeu-system/src/os/facades/window.rs +++ b/crates/console/prometeu-system/src/os/facades/window.rs @@ -1,4 +1,5 @@ use crate::os::SystemOS; +use crate::task::TaskId; use crate::windows::{Window, WindowId, WindowOwner}; use prometeu_hal::color::Color; use prometeu_hal::primitives::Rect; @@ -26,6 +27,10 @@ impl<'a> WindowFacade<'a> { self.os.window_manager.focused } + pub fn focused_window_belongs_to_task(&self, task_id: TaskId) -> bool { + self.os.window_manager.focused_window_belongs_to_task(task_id) + } + pub fn close_window(&mut self, id: WindowId) { self.os.window_manager.remove_window(id); } diff --git a/crates/console/prometeu-system/src/services/windows/window_manager.rs b/crates/console/prometeu-system/src/services/windows/window_manager.rs index 51099ecd..70e475fe 100644 --- a/crates/console/prometeu-system/src/services/windows/window_manager.rs +++ b/crates/console/prometeu-system/src/services/windows/window_manager.rs @@ -1,4 +1,5 @@ use crate::services::windows::window::{Window, WindowId}; +use crate::task::TaskId; use crate::windows::WindowOwner; use prometeu_hal::color::Color; use prometeu_hal::primitives::Rect; @@ -51,6 +52,17 @@ impl WindowManager { window.has_focus = window.id == id; } } + + pub fn focused_window_belongs_to_task(&self, task_id: TaskId) -> bool { + let Some(focused_id) = self.focused else { + return false; + }; + + self.windows + .iter() + .find(|window| window.id == focused_id) + .is_some_and(|window| window.owner == WindowOwner::Task(task_id)) + } } #[cfg(test)] @@ -103,4 +115,91 @@ mod tests { assert_eq!(wm.windows.len(), 0); assert_eq!(wm.focused, None); } + + #[test] + fn focused_window_belongs_to_task_returns_false_without_focus() { + let mut wm = WindowManager::new(); + wm.add_window( + "Task Window".to_string(), + WindowOwner::Task(crate::task::TaskId(1)), + Rect { x: 0, y: 0, w: 10, h: 10 }, + Color::WHITE, + ); + + assert!(!wm.focused_window_belongs_to_task(crate::task::TaskId(1))); + } + + #[test] + fn focused_window_belongs_to_task_matches_focused_task_owner() { + let mut wm = WindowManager::new(); + let id = wm.add_window( + "Task Window".to_string(), + WindowOwner::Task(crate::task::TaskId(1)), + Rect { x: 0, y: 0, w: 10, h: 10 }, + Color::WHITE, + ); + + wm.set_focus(id); + + assert!(wm.focused_window_belongs_to_task(crate::task::TaskId(1))); + } + + #[test] + fn focused_window_belongs_to_task_rejects_other_task_owner() { + let mut wm = WindowManager::new(); + let id = wm.add_window( + "Task Window".to_string(), + WindowOwner::Task(crate::task::TaskId(2)), + Rect { x: 0, y: 0, w: 10, h: 10 }, + Color::WHITE, + ); + + wm.set_focus(id); + + assert!(!wm.focused_window_belongs_to_task(crate::task::TaskId(1))); + } + + #[test] + fn focused_window_belongs_to_task_rejects_hub_owner() { + let mut wm = WindowManager::new(); + let id = wm.add_window( + "Hub Window".to_string(), + WindowOwner::Hub, + Rect { x: 0, y: 0, w: 10, h: 10 }, + Color::WHITE, + ); + + wm.set_focus(id); + + assert!(!wm.focused_window_belongs_to_task(crate::task::TaskId(1))); + } + + #[test] + fn focused_window_belongs_to_task_rejects_overlay_owner() { + let mut wm = WindowManager::new(); + let id = wm.add_window( + "Overlay Window".to_string(), + WindowOwner::Overlay, + Rect { x: 0, y: 0, w: 10, h: 10 }, + Color::WHITE, + ); + + wm.set_focus(id); + + assert!(!wm.focused_window_belongs_to_task(crate::task::TaskId(1))); + } + + #[test] + fn focused_window_belongs_to_task_rejects_stale_focus() { + let mut wm = WindowManager::new(); + wm.add_window( + "Task Window".to_string(), + WindowOwner::Task(crate::task::TaskId(1)), + Rect { x: 0, y: 0, w: 10, h: 10 }, + Color::WHITE, + ); + wm.focused = Some(WindowId(999)); + + assert!(!wm.focused_window_belongs_to_task(crate::task::TaskId(1))); + } } diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 22674046..8a419f18 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,5 +1,5 @@ {"type":"meta","next_id":{"DSC":36,"AGD":36,"DEC":28,"PLN":62,"LSN":44,"CLSN":1}} -{"type":"discussion","id":"DSC-0035","status":"in_progress","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[{"id":"AGD-0035","file":"AGD-0035-agenda-task-owned-shell-windows.md","status":"accepted","created_at":"2026-05-15","updated_at":"2026-05-15"}],"decisions":[{"id":"DEC-0027","file":"DEC-0027-decision-task-owned-shell-windows.md","status":"accepted","created_at":"2026-05-15","updated_at":"2026-05-15","ref_agenda":"AGD-0035"}],"plans":[{"id":"PLN-0059","file":"PLN-0059-plan-task-window-contract-specs.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15","ref_decisions":["DEC-0027"]},{"id":"PLN-0060","file":"PLN-0060-plan-focused-window-ownership-api.md","status":"open","created_at":"2026-05-15","updated_at":"2026-05-15","ref_decisions":["DEC-0027"]},{"id":"PLN-0061","file":"PLN-0061-plan-shell-running-lifecycle-integration.md","status":"open","created_at":"2026-05-15","updated_at":"2026-05-15","ref_decisions":["DEC-0027"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0035","status":"in_progress","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[{"id":"AGD-0035","file":"AGD-0035-agenda-task-owned-shell-windows.md","status":"accepted","created_at":"2026-05-15","updated_at":"2026-05-15"}],"decisions":[{"id":"DEC-0027","file":"DEC-0027-decision-task-owned-shell-windows.md","status":"accepted","created_at":"2026-05-15","updated_at":"2026-05-15","ref_agenda":"AGD-0035"}],"plans":[{"id":"PLN-0059","file":"PLN-0059-plan-task-window-contract-specs.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15","ref_decisions":["DEC-0027"]},{"id":"PLN-0060","file":"PLN-0060-plan-focused-window-ownership-api.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15","ref_decisions":["DEC-0027"]},{"id":"PLN-0061","file":"PLN-0061-plan-shell-running-lifecycle-integration.md","status":"open","created_at":"2026-05-15","updated_at":"2026-05-15","ref_decisions":["DEC-0027"]}],"lessons":[]} {"type":"discussion","id":"DSC-0034","status":"done","ticket":"system-os-domain-facades","title":"Agenda - SystemOS Domain Facades","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","services","api-surface","lifecycle","fs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0043","file":"discussion/lessons/DSC-0034-system-os-domain-facades/LSN-0043-systemos-domain-facades.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"discussion/lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"discussion/lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} diff --git a/discussion/workflow/plans/PLN-0060-plan-focused-window-ownership-api.md b/discussion/workflow/plans/PLN-0060-plan-focused-window-ownership-api.md index 0d8b38d6..0f624c43 100644 --- a/discussion/workflow/plans/PLN-0060-plan-focused-window-ownership-api.md +++ b/discussion/workflow/plans/PLN-0060-plan-focused-window-ownership-api.md @@ -2,9 +2,9 @@ id: PLN-0060 ticket: task-owned-shell-windows title: Plan - Focused Window Ownership API -status: open +status: done created: 2026-05-15 -completed: +completed: 2026-05-15 ref_decisions: [DEC-0027] tags: [runtime, os, task, window-manager, shell, lifecycle, code] --- @@ -81,12 +81,12 @@ Included: ## Criterios de Aceite -- [ ] `os.windows().focused_window_belongs_to_task(task_id)` compiles. -- [ ] The predicate returns `true` only for the currently focused +- [x] `os.windows().focused_window_belongs_to_task(task_id)` compiles. +- [x] The predicate returns `true` only for the currently focused `WindowOwner::Task(task_id)`. -- [ ] `Hub`, `Overlay`, absent focus, stale focus, and other task owners return +- [x] `Hub`, `Overlay`, absent focus, stale focus, and other task owners return `false`. -- [ ] No firmware behavior changes are included in this plan. +- [x] No firmware behavior changes are included in this plan. ## Tests / Validacao