From 097dd5299829b8a58310f43c724601b5ea08bf4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Geel=20Weirs=C3=B8e?= Date: Wed, 6 May 2026 09:38:35 +0200 Subject: [PATCH] refactor: wire ahfail-gtklock to use ahfail-ui for animation/audio/display Co-Authored-By: Claude Sonnet 4.6 --- Cargo.lock | 1 + crates/ahfail-gtklock/Cargo.toml | 1 + crates/ahfail-gtklock/src/config.rs | 38 +--------------- crates/ahfail-gtklock/src/handler.rs | 67 +++------------------------- crates/ahfail-gtklock/src/lib.rs | 55 ++--------------------- 5 files changed, 12 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7b97dd..dd83245 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,7 @@ version = "0.1.0" name = "ahfail-gtklock" version = "0.1.0" dependencies = [ + "ahfail-ui", "gdk", "gdk-pixbuf", "gio", diff --git a/crates/ahfail-gtklock/Cargo.toml b/crates/ahfail-gtklock/Cargo.toml index 5581e4b..08d2181 100644 --- a/crates/ahfail-gtklock/Cargo.toml +++ b/crates/ahfail-gtklock/Cargo.toml @@ -8,6 +8,7 @@ name = "ahfail_module" crate-type = ["staticlib", "rlib"] [dependencies] +ahfail-ui = { path = "../ahfail-ui" } gtk = { version = "0.15", package = "gtk", features = ["v3_24"] } gdk = { version = "0.15", package = "gdk", features = ["v3_24"] } gstreamer = { version = "0.18", package = "gstreamer", features = ["v1_18"] } diff --git a/crates/ahfail-gtklock/src/config.rs b/crates/ahfail-gtklock/src/config.rs index 797fdff..18ab10f 100644 --- a/crates/ahfail-gtklock/src/config.rs +++ b/crates/ahfail-gtklock/src/config.rs @@ -1,37 +1 @@ -use gtk::{glib, gdk}; -use std::ffi::CStr; -use std::ptr; - -// Storage for the command line argument string -pub static mut DEADZONE_ARG: *mut std::os::raw::c_char = ptr::null_mut(); - -pub const DEADZONE_LONG: &[u8] = b"deadzone\0"; -pub const DEADZONE_DESC: &[u8] = b"Area to avoid spawning sprites (x,y,w,h)\0"; -pub const DEADZONE_ARG_DESC: &[u8] = b"x,y,w,h\0"; - -pub struct ModuleConfig { - pub deadzone: Option, -} - -impl ModuleConfig { - pub unsafe fn from_args() -> Self { - let mut deadzone = None; - if !DEADZONE_ARG.is_null() { - let c_str = CStr::from_ptr(DEADZONE_ARG); - if let Ok(s) = c_str.to_str() { - let parts: Vec<&str> = s.split(',').collect(); - if parts.len() == 4 { - if let (Ok(x), Ok(y), Ok(w), Ok(h)) = (parts[0].parse(), parts[1].parse(), parts[2].parse(), parts[3].parse()) { - deadzone = Some(gdk::Rectangle::new(x, y, w, h)); - println!("[ahfail] Configured deadzone: {:?}", deadzone); - } else { - eprintln!("[ahfail] Invalid numbers in deadzone argument: {}", s); - } - } else { - eprintln!("[ahfail] Invalid format for deadzone argument (expected x,y,w,h): {}", s); - } - } - } - Self { deadzone } - } -} +pub use ahfail_ui::config::*; diff --git a/crates/ahfail-gtklock/src/handler.rs b/crates/ahfail-gtklock/src/handler.rs index fc1717d..a8b2ee9 100644 --- a/crates/ahfail-gtklock/src/handler.rs +++ b/crates/ahfail-gtklock/src/handler.rs @@ -1,15 +1,8 @@ use gtk::prelude::*; -use gtk::{glib, gdk, gdk_pixbuf, gio}; -use gstreamer as gst; -use gstreamer_player as gst_player; -use rand::Rng; use crate::state::{MODULE_STATE, WindowData}; use crate::context::WindowContext; -const SPRITE_MARGIN: i32 = 100; -const SPRITE_SCALE: f64 = 0.6; const PLAYER_POOL_SIZE: usize = 3; -const RETRY_ATTEMPTS: usize = 10; pub struct WindowHandler; @@ -39,7 +32,7 @@ impl WindowHandler { MODULE_STATE.with(|state| { if let Some(audio_uri) = &state.borrow().audio_uri { for _ in 0..PLAYER_POOL_SIZE { - ready_players.push(Self::create_player(audio_uri)); + ready_players.push(ahfail_ui::audio::create_player(audio_uri)); } } }); @@ -68,58 +61,21 @@ impl WindowHandler { if let (Some(animation), Some(audio_uri)) = (&state.animation, &state.audio_uri) { let data = unsafe { &mut *(ptr_addr as *mut WindowData) }; - let image = gtk::Image::from_animation(animation); - image.show(); - - let sprite_w = animation.width(); - let sprite_h = animation.height(); - - let mut rng = rand::thread_rng(); - - let safe_w = screen_w - SPRITE_MARGIN; - let safe_h = screen_h - SPRITE_MARGIN; - - let max_x = if safe_w > sprite_w { safe_w - sprite_w } else { 0 }; - let max_y = if safe_h > sprite_h { safe_h - sprite_h } else { 0 }; - - let mut x = 0; - let mut y = 0; - let mut found_safe_spot = false; - - for _ in 0..RETRY_ATTEMPTS { - x = rng.gen_range(0..=max_x); - y = rng.gen_range(0..=max_y); - - if let Some(deadzone) = &state.config.deadzone { - let sprite_rect = gdk::Rectangle::new(x, y, sprite_w, sprite_h); - if deadzone.intersect(&sprite_rect).is_none() { - found_safe_spot = true; - break; - } - } else { - found_safe_spot = true; - break; - } - } - - if !found_safe_spot { - println!("[ahfail] Could not find safe spot after retries, placing anyway"); - } - - println!("[ahfail] Placing sprite at ({}, {})", x, y); - data.fixed.put(&image, x, y); + let image = ahfail_ui::display::place_sprite( + &data.fixed, animation, screen_w, screen_h, &state.config + ); data.sprites.push(image); let player = if let Some(p) = data.ready_players.pop() { p } else { - Self::create_player(audio_uri) + ahfail_ui::audio::create_player(audio_uri) }; player.play(); data.active_players.push(player); - let new_player = Self::create_player(audio_uri); + let new_player = ahfail_ui::audio::create_player(audio_uri); data.ready_players.push(new_player); } }); @@ -150,16 +106,5 @@ impl WindowHandler { } } - fn create_player(uri: &str) -> gst_player::Player { - let player = gst_player::Player::new(None, None); - player.set_uri(Some(uri)); - player.connect_end_of_stream(glib::clone!(@weak player => move |_| { - player.seek(gst::ClockTime::from_seconds(0)); - })); - player.connect_error(|_, err| { - eprintln!("[ahfail] GStreamer Player Error: {}", err); - }); - player - } } diff --git a/crates/ahfail-gtklock/src/lib.rs b/crates/ahfail-gtklock/src/lib.rs index d18837d..dcf7ada 100644 --- a/crates/ahfail-gtklock/src/lib.rs +++ b/crates/ahfail-gtklock/src/lib.rs @@ -4,11 +4,9 @@ pub mod state; pub mod handler; use gtk::prelude::*; -use gtk::{glib, gdk_pixbuf, gio}; -use gtk::gdk_pixbuf::InterpType; +use gtk::glib; use gstreamer as gst; -use std::ffi::{c_void, CStr}; -use glib::translate::from_glib_none; +use std::ffi::c_void; use std::os::raw::{c_char, c_int, c_uint}; use std::ptr; @@ -18,9 +16,6 @@ pub use context::{Window, GtkLock, WindowContext, __IncompleteArrayField}; pub use state::{MODULE_STATE, WindowData, ModuleState}; pub use handler::WindowHandler; -// Scale factor to reduce the image size and avoid cropping/overlap -const SPRITE_SCALE: f64 = 0.6; - #[no_mangle] pub static module_name: [c_char; 7] = [ b'a' as c_char, @@ -53,10 +48,6 @@ pub static mut module_entries: [glib::ffi::GOptionEntry; 3] = [ glib::ffi::GOptionEntry { long_name: ptr::null(), short_name: 0, flags: 0, arg: 0, arg_data: ptr::null_mut(), description: ptr::null(), arg_description: ptr::null() }, ]; -extern "C" { - fn ahfail_get_resource() -> *mut gio::ffi::GResource; -} - // Helper for tests to inspect window data /// # Safety /// `ctx` must be a valid Window pointer. @@ -83,47 +74,7 @@ pub unsafe extern "C" fn on_activation(_gtklock: *mut GtkLock, _id: c_int) { return; } - let resource_ptr = ahfail_get_resource(); - if !resource_ptr.is_null() { - let resource = from_glib_none::<_, gio::Resource>(resource_ptr); - gio::resources_register(&resource); - } - - // Load frames - let mut loaded_frames: Vec = Vec::new(); - match gio::resources_enumerate_children("/ahfail/sprites", gio::ResourceLookupFlags::NONE) { - Ok(mut frames) => { - frames.sort(); - for frame_path in frames { - let full_path = format!("/ahfail/sprites/{}", frame_path); - match gdk_pixbuf::Pixbuf::from_resource(&full_path) { - Ok(pixbuf) => { - let w = (pixbuf.width() as f64 * SPRITE_SCALE) as i32; - let h = (pixbuf.height() as f64 * SPRITE_SCALE) as i32; - if let Some(scaled) = pixbuf.scale_simple(w, h, InterpType::Bilinear) { - loaded_frames.push(scaled); - } else { - loaded_frames.push(pixbuf); - } - }, - Err(e) => eprintln!("Failed to load sprite frame {}: {}", full_path, e), - } - } - }, - Err(e) => eprintln!("Failed to enumerate sprites: {}", e), - } - - let anim_opt = if !loaded_frames.is_empty() { - let first = &loaded_frames[0]; - let anim = gdk_pixbuf::PixbufSimpleAnim::new(first.width(), first.height(), 12.0); - anim.set_loop(true); - for frame in loaded_frames { - anim.add_frame(&frame); - } - Some(anim.upcast()) - } else { - None - }; + let anim_opt = unsafe { ahfail_ui::animation::load_animation() }; let config = ModuleConfig::from_args();