Files
gtk-ahfail/crates/ahfail-gtklock/src/lib.rs
Asger Geel Weirsøe 74e0f544a0 feat: add volume save/restore on failure/unload
On the first failed unlock attempt, save the current system volume and
mute state then set volume to maximum unmuted; restore on g_module_unload.
A lock file under XDG_RUNTIME_DIR prevents double-acquisition when
multiple gtklock windows are active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 09:50:49 +02:00

147 lines
4.4 KiB
Rust

pub mod config;
pub mod context;
pub mod state;
pub mod handler;
use gtk::glib;
use gstreamer as gst;
use std::ffi::c_void;
use std::os::raw::{c_char, c_int, c_uint};
use std::ptr;
// Re-export types for external use (e.g. integration tests)
pub use config::{ModuleConfig, DEADZONE_ARG, DEADZONE_LONG, DEADZONE_DESC, DEADZONE_ARG_DESC};
pub use context::{Window, GtkLock, WindowContext, __IncompleteArrayField};
pub use state::{MODULE_STATE, WindowData, ModuleState};
pub use handler::WindowHandler;
#[no_mangle]
pub static module_name: [c_char; 7] = [
b'a' as c_char,
b'h' as c_char,
b'f' as c_char,
b'a' as c_char,
b'i' as c_char,
b'l' as c_char,
0,
];
#[no_mangle]
pub static module_major_version: c_uint = 4;
#[no_mangle]
pub static module_minor_version: c_uint = 0;
#[no_mangle]
pub static mut module_entries: [glib::ffi::GOptionEntry; 3] = [
glib::ffi::GOptionEntry {
long_name: DEADZONE_LONG.as_ptr() as *const c_char,
short_name: 0,
flags: 0,
arg: glib::ffi::G_OPTION_ARG_STRING,
arg_data: &raw mut DEADZONE_ARG as *mut _,
description: DEADZONE_DESC.as_ptr() as *const c_char,
arg_description: DEADZONE_ARG_DESC.as_ptr() as *const c_char
},
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() },
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() },
];
// Helper for tests to inspect window data
/// # Safety
/// `ctx` must be a valid Window pointer.
pub unsafe fn get_window_data_ref(ctx: *mut Window) -> Option<&'static state::WindowData> {
let ptr = *(*ctx).module_data.as_ptr();
if ptr.is_null() {
None
} else {
Some(&*(ptr as *mut state::WindowData))
}
}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn on_activation(_gtklock: *mut GtkLock, _id: c_int) {
println!("[ahfail] on_activation called");
if let Err(e) = gtk::init() {
eprintln!("Failed to initialize GTK bindings: {}", e);
return;
}
if let Err(e) = gst::init() {
eprintln!("Failed to initialize GStreamer: {}", e);
return;
}
let anim_opt = unsafe { ahfail_ui::animation::load_animation() };
let config = ModuleConfig::from_args();
MODULE_STATE.with(|state| {
let mut state = state.borrow_mut();
state.animation = anim_opt;
state.audio_uri = Some("resource:///ahfail/audio/magic-word.mp3".to_string());
state.config = config;
});
}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn on_window_create(_gtklock: *mut GtkLock, ctx: *mut Window) {
if let Some(win_ctx) = WindowContext::new(ctx) {
WindowHandler::create(&win_ctx);
}
}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn on_window_destroy(_gtklock: *mut GtkLock, ctx: *mut Window) {
if let Some(win_ctx) = WindowContext::new(ctx) {
WindowHandler::destroy(&win_ctx);
}
}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn on_idle_hide(gtklock: *mut GtkLock) {
if gtklock.is_null() { return; }
let garray = (*gtklock).windows;
if !garray.is_null() {
let len = (*garray).len;
let data_ptr = (*garray).data as *mut *mut Window;
let windows = std::slice::from_raw_parts(data_ptr, len as usize);
for &window_ptr in windows {
on_window_destroy(ptr::null_mut(), window_ptr);
}
}
}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn on_focus_change(_gtklock: *mut GtkLock, _win: *mut Window, _old: *mut Window) {}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn on_idle_show(_gtklock: *mut GtkLock) {}
/// # Safety
/// This function is called by the C host.
#[no_mangle]
pub unsafe extern "C" fn g_module_unload(_module: *mut c_void) {
MODULE_STATE.with(|state| {
let mut state = state.borrow_mut();
if let Some(vs) = state.volume_state.take() {
ahfail_ui::volume::restore(vs);
}
state.animation = None;
state.audio_uri = None;
state.config.deadzone = None;
state.volume_state = None;
});
}