feat: add window management (list, minimize-all, focus, maximize)

This commit is contained in:
Helios 2026-03-02 20:00:41 +01:00
parent c9643c8543
commit 9f06d84f28
No known key found for this signature in database
GPG key ID: C8259547CD8309B5
7 changed files with 277 additions and 1 deletions

View file

@ -14,6 +14,7 @@ use helios_common::{ClientMessage, ServerMessage};
mod shell;
mod screenshot;
mod input;
mod windows_mgmt;
#[derive(Debug, Serialize, Deserialize)]
struct Config {
@ -266,6 +267,50 @@ async fn handle_message(
}
}
ServerMessage::ListWindowsRequest { request_id } => {
info!("ListWindows");
match windows_mgmt::list_windows() {
Ok(windows) => ClientMessage::ListWindowsResponse { request_id, windows },
Err(e) => {
error!("ListWindows failed: {e}");
ClientMessage::Error { request_id, message: e }
}
}
}
ServerMessage::MinimizeAllRequest { request_id } => {
info!("MinimizeAll");
match windows_mgmt::minimize_all() {
Ok(()) => ClientMessage::Ack { request_id },
Err(e) => {
error!("MinimizeAll failed: {e}");
ClientMessage::Error { request_id, message: e }
}
}
}
ServerMessage::FocusWindowRequest { request_id, window_id } => {
info!("FocusWindow: {window_id}");
match windows_mgmt::focus_window(window_id) {
Ok(()) => ClientMessage::Ack { request_id },
Err(e) => {
error!("FocusWindow failed: {e}");
ClientMessage::Error { request_id, message: e }
}
}
}
ServerMessage::MaximizeAndFocusRequest { request_id, window_id } => {
info!("MaximizeAndFocus: {window_id}");
match windows_mgmt::maximize_and_focus(window_id) {
Ok(()) => ClientMessage::Ack { request_id },
Err(e) => {
error!("MaximizeAndFocus failed: {e}");
ClientMessage::Error { request_id, message: e }
}
}
}
ServerMessage::Ack { request_id } => {
info!("Server ack for {request_id}");
// Nothing to do - server acked something we sent

View file

@ -0,0 +1,130 @@
use helios_common::protocol::WindowInfo;
// ── Windows implementation ──────────────────────────────────────────────────
#[cfg(windows)]
mod win_impl {
use super::*;
use std::sync::Mutex;
use windows::Win32::Foundation::{BOOL, HWND, LPARAM};
use windows::Win32::UI::WindowsAndMessaging::{
BringWindowToTop, EnumWindows, GetWindowTextW, IsWindowVisible, SetForegroundWindow,
ShowWindow, SW_MAXIMIZE, SW_MINIMIZE, SHOW_WINDOW_CMD,
};
// Collect HWNDs via EnumWindows callback
unsafe extern "system" fn enum_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
let list = &mut *(lparam.0 as *mut Vec<HWND>);
list.push(hwnd);
BOOL(1)
}
fn get_all_hwnds() -> Vec<HWND> {
let mut list: Vec<HWND> = Vec::new();
unsafe {
let _ = EnumWindows(
Some(enum_callback),
LPARAM(&mut list as *mut Vec<HWND> as isize),
);
}
list
}
fn hwnd_title(hwnd: HWND) -> String {
let mut buf = [0u16; 512];
let len = unsafe { GetWindowTextW(hwnd, &mut buf) };
String::from_utf16_lossy(&buf[..len as usize])
}
pub fn list_windows() -> Result<Vec<WindowInfo>, String> {
let hwnds = get_all_hwnds();
let mut windows = Vec::new();
for hwnd in hwnds {
let visible = unsafe { IsWindowVisible(hwnd).as_bool() };
let title = hwnd_title(hwnd);
if title.is_empty() {
continue;
}
windows.push(WindowInfo {
id: hwnd.0 as u64,
title,
visible,
});
}
Ok(windows)
}
pub fn minimize_all() -> Result<(), String> {
let hwnds = get_all_hwnds();
for hwnd in hwnds {
let visible = unsafe { IsWindowVisible(hwnd).as_bool() };
let title = hwnd_title(hwnd);
if visible && !title.is_empty() {
unsafe {
let _ = ShowWindow(hwnd, SW_MINIMIZE);
}
}
}
Ok(())
}
pub fn focus_window(window_id: u64) -> Result<(), String> {
let hwnd = HWND(window_id as isize);
unsafe {
BringWindowToTop(hwnd).map_err(|e| format!("BringWindowToTop failed: {e}"))?;
SetForegroundWindow(hwnd);
}
Ok(())
}
pub fn maximize_and_focus(window_id: u64) -> Result<(), String> {
let hwnd = HWND(window_id as isize);
unsafe {
ShowWindow(hwnd, SW_MAXIMIZE);
BringWindowToTop(hwnd).map_err(|e| format!("BringWindowToTop failed: {e}"))?;
SetForegroundWindow(hwnd);
}
Ok(())
}
}
// ── Non-Windows stubs ───────────────────────────────────────────────────────
#[cfg(not(windows))]
mod win_impl {
use super::*;
pub fn list_windows() -> Result<Vec<WindowInfo>, String> {
Err("Window management is only supported on Windows".to_string())
}
pub fn minimize_all() -> Result<(), String> {
Err("Window management is only supported on Windows".to_string())
}
pub fn focus_window(_window_id: u64) -> Result<(), String> {
Err("Window management is only supported on Windows".to_string())
}
pub fn maximize_and_focus(_window_id: u64) -> Result<(), String> {
Err("Window management is only supported on Windows".to_string())
}
}
// ── Public API ──────────────────────────────────────────────────────────────
pub fn list_windows() -> Result<Vec<WindowInfo>, String> {
win_impl::list_windows()
}
pub fn minimize_all() -> Result<(), String> {
win_impl::minimize_all()
}
pub fn focus_window(window_id: u64) -> Result<(), String> {
win_impl::focus_window(window_id)
}
pub fn maximize_and_focus(window_id: u64) -> Result<(), String> {
win_impl::maximize_and_focus(window_id)
}