refactor: wire ahfail-gtklock to use ahfail-ui for animation/audio/display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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"] }
|
||||
|
||||
@@ -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<gdk::Rectangle>,
|
||||
}
|
||||
|
||||
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::*;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<gdk_pixbuf::Pixbuf> = 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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user