use clap::{Parser, Subcommand};
use std::process;
use wasma_client::{ResourceMode, WasmaCore, WindowState};
fn init_config(output: Option<String>) -> Result<String, String> {
use std::fs;
use wasma_client::parser::{ResourceLimits, UriHandlingConfig, UserConfig, WasmaConfig};
let default_config = WasmaConfig {
uri_handling: UriHandlingConfig {
window_app_spec: "default.app".to_string(),
protocols: vec![],
multi_instances: true,
singularity_instances: false,
compilation_server: None,
},
user_config: UserConfig {
user_withed: "user".to_string(),
groups_withed: vec![],
},
resource_limits: ResourceLimits {
ip_scope: "local".to_string(),
scope_level: 1,
renderer: "cpu".to_string(),
execution_mode: None,
max_memory_mb: Some(1024),
max_vram_mb: Some(512),
cpu_cores: vec![],
},
};
let output_path = output.unwrap_or_else(|| "wasmal.conf".to_string());
let toml_content = toml::to_string_pretty(&default_config)
.map_err(|e| format!("Failed to serialize config: {}", e))?;
fs::write(&output_path, toml_content)
.map_err(|e| format!("Failed to write config file: {}", e))?;
Ok(output_path)
}
fn validate_config(config_path: Option<String>) -> Result<(), String> {
use wasma_client::ConfigParser;
let parser = ConfigParser::new(config_path);
parser.load().map_err(|e| e.to_string())?;
Ok(())
}
fn print_config_info(config_path: Option<String>) -> Result<(), String> {
use wasma_client::ConfigParser;
let parser = ConfigParser::new(config_path);
let config = parser.load().map_err(|e| e.to_string())?;
println!("WASMA Configuration:");
println!(" URI Handling:");
println!(
" Window App Spec: {}",
config.uri_handling.window_app_spec
);
println!(" Protocols: {:?}", config.uri_handling.protocols);
println!(" User Config:");
println!(" User: {}", config.user_config.user_withed);
println!(" Groups: {:?}", config.user_config.groups_withed);
println!(" Resource Limits:");
println!(" IP Scope: {}", config.resource_limits.ip_scope);
println!(" Scope Level: {}", config.resource_limits.scope_level);
println!(" Renderer: {}", config.resource_limits.renderer);
if let Some(mem) = config.resource_limits.max_memory_mb {
println!(" Max Memory: {} MB", mem);
}
if let Some(vram) = config.resource_limits.max_vram_mb {
println!(" Max VRAM: {} MB", vram);
}
println!(" CPU Cores: {:?}", config.resource_limits.cpu_cores);
Ok(())
}
#[derive(Parser)]
#[command(name = "wasma")]
#[command(author = "WASMA Project")]
#[command(version = "1.0.0")]
#[command(about = "Windows Assignment System Monitoring Architecture", long_about = None)]
struct Cli {
#[arg(short, long, value_name = "FILE")]
config: Option<String>,
#[arg(short = 'm', long, value_enum, default_value = "auto")]
resource_mode: ResourceModeArg,
#[arg(short, long)]
verbose: bool,
#[arg(long)]
wayland_debug: bool,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(clap::ValueEnum, Clone, Debug)]
enum ResourceModeArg {
Auto,
Manual,
}
impl From<ResourceModeArg> for ResourceMode {
fn from(arg: ResourceModeArg) -> Self {
match arg {
ResourceModeArg::Auto => ResourceMode::Auto,
ResourceModeArg::Manual => ResourceMode::Manual,
}
}
}
#[derive(Subcommand)]
enum Commands {
Gui {
#[arg(short, long, default_value = "1200")]
width: u32,
#[arg(short = 'h', long, default_value = "800")]
height: u32,
},
Init {
#[arg(short, long)]
output: Option<String>,
},
Validate,
Info,
Create {
#[arg(short, long)]
title: String,
#[arg(short, long)]
app_id: String,
#[arg(short, long, default_value = "800")]
width: u32,
#[arg(short = 'h', long, default_value = "600")]
height: u32,
#[arg(short, long)]
manifest: Option<String>,
},
List {
#[arg(short, long)]
detailed: bool,
},
Close {
window_id: u64,
},
Focus {
window_id: u64,
},
Resources {
window_id: u64,
},
State {
window_id: u64,
#[arg(value_enum)]
state: StateArg,
},
Cycle {
#[arg(short, long, default_value = "1")]
count: u32,
},
Launch {
#[arg(short, long)]
app: Option<String>,
#[arg(short = 'b', long)]
backend: Option<String>,
},
}
#[derive(clap::ValueEnum, Clone, Debug)]
enum StateArg {
Normal,
Minimized,
Maximized,
Fullscreen,
Hidden,
}
impl From<StateArg> for WindowState {
fn from(arg: StateArg) -> Self {
match arg {
StateArg::Normal => WindowState::Normal,
StateArg::Minimized => WindowState::Minimized,
StateArg::Maximized => WindowState::Maximized,
StateArg::Fullscreen => WindowState::Fullscreen,
StateArg::Hidden => WindowState::Hidden,
}
}
}
fn main() {
let cli = Cli::parse();
if cli.wayland_debug {
std::env::set_var("WAYLAND_DEBUG", "1");
std::env::set_var(
"WASMA_LOG",
"waylandbackend=debug,smithay=debug,wasma=debug",
);
eprintln!("[wasma] wayland-debug enabled (WAYLAND_DEBUG=1)");
}
if cli.verbose {
env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or("debug"),
)
.init();
println!("🔍 Verbose mode enabled");
} else {
env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or("info"),
)
.init();
}
match &cli.command {
Some(Commands::Init { output }) => {
handle_init(output.clone());
}
Some(Commands::Validate) => {
handle_validate(cli.config.clone());
}
Some(Commands::Info) => {
handle_info(cli.config.clone());
}
Some(Commands::Gui { width, height }) => {
handle_gui(cli.config.clone(), cli.resource_mode.clone().into(), *width, *height);
}
Some(Commands::Create {
title,
app_id,
width,
height,
manifest,
}) => {
handle_create(
cli.config.clone(),
cli.resource_mode.clone().into(),
title,
app_id,
*width,
*height,
manifest.clone(),
);
}
Some(Commands::List { detailed }) => {
handle_list(cli.config.clone(), cli.resource_mode.clone().into(), *detailed);
}
Some(Commands::Close { window_id }) => {
handle_close(cli.config.clone(), cli.resource_mode.clone().into(), *window_id);
}
Some(Commands::Focus { window_id }) => {
handle_focus(cli.config.clone(), cli.resource_mode.clone().into(), *window_id);
}
Some(Commands::Resources { window_id }) => {
handle_resources(cli.config.clone(), cli.resource_mode.clone().into(), *window_id);
}
Some(Commands::State { window_id, state }) => {
handle_state(
cli.config.clone(),
cli.resource_mode.clone().into(),
*window_id,
state.clone().into(),
);
}
Some(Commands::Cycle { count }) => {
handle_cycle(cli.config.clone(), cli.resource_mode.clone().into(), *count);
}
Some(Commands::Launch { app, backend }) => {
handle_launch(cli.config.clone(), app.clone(), backend.clone());
}
None => {
handle_gui(cli.config.clone(), cli.resource_mode.clone().into(), 1200, 800);
}
}
}
fn handle_init(output: Option<String>) {
println!("🔧 Initializing WASMA configuration...");
match init_config(output) {
Ok(path) => {
println!("✅ Configuration file created: {}", path);
println!(" Edit this file to customize your WASMA setup.");
}
Err(e) => {
eprintln!("❌ Failed to initialize config: {}", e);
process::exit(1);
}
}
}
fn handle_validate(config_path: Option<String>) {
println!("🔍 Validating configuration...");
match validate_config(config_path) {
Ok(_) => {
println!("✅ Configuration is valid!");
}
Err(e) => {
eprintln!("❌ Configuration validation failed: {}", e);
process::exit(1);
}
}
}
fn handle_info(config_path: Option<String>) {
if let Err(e) = print_config_info(config_path) {
eprintln!("❌ Failed to read config: {}", e);
process::exit(1);
}
}
fn handle_gui(
config_path: Option<String>,
resource_mode: ResourceMode,
_width: u32,
_height: u32,
) {
println!("🖥️ Launching WASMA GUI Window Manager...");
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
println!("🚀 Starting GUI with resource mode: {:?}", resource_mode);
if let Err(e) = core.launch_gui() {
eprintln!("❌ GUI failed: {}", e);
process::exit(1);
}
}
fn handle_create(
config_path: Option<String>,
resource_mode: ResourceMode,
title: &str,
app_id: &str,
width: u32,
height: u32,
_manifest: Option<String>,
) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
println!("🪟 Creating window: {}", title);
match core.create_window(title.to_string(), app_id.to_string(), width, height) {
Ok(window_id) => {
println!("✅ Window created successfully!");
println!(" Window ID: {}", window_id);
println!(" Title: {}", title);
println!(" Size: {}x{}", width, height);
println!(" Mode: {:?}", resource_mode);
}
Err(e) => {
eprintln!("❌ Failed to create window: {}", e);
process::exit(1);
}
}
}
fn handle_list(config_path: Option<String>, resource_mode: ResourceMode, detailed: bool) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
let windows = core.list_windows();
if windows.is_empty() {
println!("ℹ️ No active windows.");
return;
}
println!("╔════════════════════════════════════════════════════════════╗");
println!("║ Active Windows ║");
println!("╚════════════════════════════════════════════════════════════╝\n");
for window in &windows {
let state_icon = match window.state {
WindowState::Normal => "🟢",
WindowState::Minimized => "🟡",
WindowState::Maximized => "🔵",
WindowState::Fullscreen => "⚡",
WindowState::Hidden => "⚫",
};
let focus = if window.focused { "👁️ " } else { "" };
println!(
"{}{} Window #{}: {}",
focus, state_icon, window.id, window.title
);
println!(" App ID: {}", window.app_id);
println!(
" Geometry: {}x{} at ({}, {})",
window.geometry.width,
window.geometry.height,
window.geometry.x,
window.geometry.y
);
println!(
" Visible: {} | Focused: {}",
window.visible, window.focused
);
if detailed {
println!(" Renderer: {}", window.resource_limits.renderer);
println!(
" Execution Mode: {:?}",
window.resource_limits.execution_mode
);
if let Ok(usage) = core.get_window_resources(window.id) {
println!(
" RAM: {} MiB | VRAM: {} MiB",
usage.ram_allocated_mb, usage.vram_allocated_mb
);
println!(" CPU Cores: {:?}", usage.cpu_cores);
if let Some(ref gpu) = usage.gpu_device {
println!(" GPU: {}", gpu);
}
println!(
" Task Active: {} | GPU Active: {}",
usage.task_active, usage.gpu_active
);
if usage.remaining_lease_secs > 0 {
println!(" Lease Remaining: {}s", usage.remaining_lease_secs);
}
}
}
println!();
}
println!("Total windows: {}", windows.len());
}
fn handle_close(config_path: Option<String>, resource_mode: ResourceMode, window_id: u64) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
println!("🗑️ Closing window {}...", window_id);
match core.close_window(window_id) {
Ok(_) => println!("✅ Window {} closed successfully", window_id),
Err(e) => {
eprintln!("❌ Failed to close window: {}", e);
process::exit(1);
}
}
}
fn handle_focus(config_path: Option<String>, resource_mode: ResourceMode, window_id: u64) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
println!("👁️ Focusing window {}...", window_id);
match core.focus_window(window_id) {
Ok(_) => println!("✅ Window {} is now focused", window_id),
Err(e) => {
eprintln!("❌ Failed to focus window: {}", e);
process::exit(1);
}
}
}
fn handle_resources(config_path: Option<String>, resource_mode: ResourceMode, window_id: u64) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
match core.get_window_resources(window_id) {
Ok(usage) => {
println!("╔════════════════════════════════════════════════════════════╗");
println!(
"║ Window #{} Resource Usage ║",
window_id
);
println!("╚════════════════════════════════════════════════════════════╝\n");
println!("📊 Assignment ID: {}", usage.assignment_id);
println!("💾 RAM Allocated: {} MiB", usage.ram_allocated_mb);
println!("🎮 VRAM Allocated: {} MiB", usage.vram_allocated_mb);
println!("🔧 CPU Cores: {:?}", usage.cpu_cores);
if let Some(ref gpu) = usage.gpu_device {
println!("🎨 GPU Device: {}", gpu);
} else {
println!("🎨 GPU Device: None");
}
println!("⚙️ Execution Mode: {:?}", usage.execution_mode);
println!("🟢 Task Active: {}", usage.task_active);
println!("🎯 GPU Active: {}", usage.gpu_active);
if usage.remaining_lease_secs > 0 {
println!("⏱️ Lease Remaining: {}s", usage.remaining_lease_secs);
}
}
Err(e) => {
eprintln!("❌ Failed to get resources: {}", e);
process::exit(1);
}
}
}
fn handle_state(
config_path: Option<String>,
resource_mode: ResourceMode,
window_id: u64,
state: WindowState,
) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
println!("🔄 Setting window {} state to {:?}...", window_id, state);
match core.set_window_state(window_id, state) {
Ok(_) => println!("✅ Window state changed successfully"),
Err(e) => {
eprintln!("❌ Failed to change state: {}", e);
process::exit(1);
}
}
}
fn handle_cycle(config_path: Option<String>, resource_mode: ResourceMode, count: u32) {
let core = match build_core(config_path, Some(resource_mode)) {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Failed to initialize WASMA Core: {}", e);
process::exit(1);
}
};
if count == 0 {
println!("🔄 Running resource management cycle continuously...");
println!(" Press Ctrl+C to stop");
loop {
core.update();
std::thread::sleep(std::time::Duration::from_secs(1));
}
} else {
println!("🔄 Running {} resource management cycle(s)...", count);
for i in 1..=count {
println!(" Cycle {}/{}", i, count);
core.update();
if i < count {
std::thread::sleep(std::time::Duration::from_millis(500));
}
}
println!("✅ Resource cycles completed");
}
}
fn handle_launch(config_path: Option<String>, app_name_opt: Option<String>, backend_opt: Option<String>) {
use wasma_client::backend_selector::{Backend, BackendSelector};
use wasma_client::{parser::ConfigParser, WindowClient};
use std::io::{self, Write};
let app_name = if let Some(name) = app_name_opt {
name
} else {
print!("Enter application name: ");
io::stdout().flush().ok();
let mut input = String::new();
io::stdin().read_line(&mut input).ok();
input.trim().to_string()
};
if app_name.is_empty() {
eprintln!("❌ Application name cannot be empty");
process::exit(1);
}
let selected_backend = if let Some(backend_str) = backend_opt {
match backend_str.to_lowercase().as_str() {
"x11" => Backend::X11,
"wayland" => Backend::Wayland,
_ => {
eprintln!("❌ Invalid backend: {}. Type 'x11' or 'wayland'.", backend_str);
process::exit(1);
}
}
} else {
match BackendSelector::select_interactive() {
Ok(backend) => backend,
Err(e) => {
eprintln!("❌ Backend selection failed: {}", e);
process::exit(1);
}
}
};
let config_path = config_path.unwrap_or_else(|| "/etc/wasma/wasma.in.conf".to_string());
let parser = ConfigParser::new(Some(config_path));
let config = match parser.load() {
Ok(cfg) => cfg,
Err(e) => {
eprintln!("❌ Config could not be loaded: {}", e);
process::exit(1);
}
};
let window_client = WindowClient::new(config, 800, 600);
let window_id = 1u64;
match window_client.launch_app_with_backend(window_id, &app_name, false, Some(selected_backend)) {
Ok(_) => {
println!("✅ '{}' started (backend={})", app_name, selected_backend);
}
Err(e) => {
eprintln!("❌ Application could not be started: {}", e);
process::exit(1);
}
}
}
fn build_core(
config_path: Option<String>,
_resource_mode: Option<ResourceMode>,
) -> Result<WasmaCore, String> {
WasmaCore::new(config_path).map_err(|e| e.to_string())
}