Files
psysonic/src-tauri/crates/psysonic-audio/src/power_notify_win.rs
T
Psychotoxical 41e75663f1 refactor: extract psysonic-audio crate (M3/7)
Moves all audio playback code (Symphonia decode, rodio output, HTTP
streaming, gapless, previews, and the seven stream/ source-type
submodules from the prior split) out of the top crate into a new
psysonic-audio crate.

  crates/psysonic-audio/                  new lib crate, depends on
                                          psysonic-core + psysonic-analysis
    src/{engine,helpers,decode,…}.rs      flattened layout (no more
                                          extra audio/ namespace level)
    src/stream/                           seven submodules from M0
    src/lib.rs                            re-exports macros from
                                          psysonic-core and the public
                                          API surface

The audio↔analysis edges identified in the dep survey are now real
crate deps (audio depends on analysis directly: AnalysisCache reads,
recommended_gain_for_target, submit_analysis_cpu_seed). Only the
analysis→audio back-edge goes through the PlaybackQueryHandle port
registered in M2.

Cross-crate ref migrations applied via batch sed:
  crate::audio::*               → crate::*       (intra-crate)
  crate::analysis_cache::*      → psysonic_analysis::analysis_cache::*
  crate::submit_analysis_cpu_seed → psysonic_analysis::analysis_runtime::*
  crate::subsonic_wire_user_agent → psysonic_core::user_agent::*

Top crate keeps `crate::audio::*` paths working via
`pub use psysonic_audio as audio;` — lib_commands/cli callers untouched.
`stop_audio_engine` (mac process-exit cleanup) moved into the audio
crate as `pub fn stop_audio_engine` since it reaches AudioEngine
internals; tray.rs now re-exports the moved fn.

Two small visibility promotions in engine.rs:
  pub(crate) fn analysis_track_id_is_current_playback   → pub
  pub(crate) fn ranged_loudness_backfill_should_defer    → pub

Behaviour preserving. Cargo check + clippy --workspace clean.
2026-05-09 13:33:44 +02:00

83 lines
2.6 KiB
Rust

//! Windows: `PowerRegisterSuspendResumeNotification` — resume from sleep without a default-device rename.
use std::ffi::c_void;
use tauri::AppHandle;
use windows::Win32::{
Foundation::{ERROR_SUCCESS, HANDLE},
System::Power::{PowerRegisterSuspendResumeNotification, DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS},
UI::WindowsAndMessaging::{
DEVICE_NOTIFY_CALLBACK, PBT_APMRESUMEAUTOMATIC, PBT_APMRESUMESUSPEND, PBT_APMRESUMESTANDBY,
},
};
use super::power_resume::{debounce_allow_resume_reopen, reopen_audio_after_system_resume};
unsafe extern "system" fn power_suspend_resume_callback(
context: *const c_void,
event_type: u32,
_setting: *const c_void,
) -> u32 {
if context.is_null() {
return 0;
}
if !matches!(
event_type,
PBT_APMRESUMEAUTOMATIC | PBT_APMRESUMESUSPEND | PBT_APMRESUMESTANDBY
) {
return 0;
}
if !debounce_allow_resume_reopen() {
return 0;
}
let app = unsafe { &*(context as *const AppHandle) }.clone();
tauri::async_runtime::spawn(async move {
reopen_audio_after_system_resume(&app).await;
});
0
}
pub fn register(app: AppHandle) {
// Intentionally leaked for process lifetime: Win32 callback receives this pointer
// on each suspend/resume notification and may outlive this function scope.
let app_leak = Box::into_raw(Box::new(app));
let params = Box::new(DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
Callback: Some(power_suspend_resume_callback),
Context: app_leak as *mut c_void,
});
// Intentionally leaked for process lifetime: the power subsystem keeps the
// subscribe-parameters pointer after successful registration.
let params_ptr = Box::into_raw(params);
let mut registration: *mut c_void = std::ptr::null_mut();
let err = unsafe {
PowerRegisterSuspendResumeNotification(
DEVICE_NOTIFY_CALLBACK,
HANDLE(params_ptr as *mut _),
&mut registration as *mut *mut c_void,
)
};
if err != ERROR_SUCCESS {
crate::app_eprintln!(
"[psysonic] PowerRegisterSuspendResumeNotification failed: {:?} — post-sleep audio recovery disabled",
err
);
unsafe {
drop(Box::from_raw(params_ptr));
drop(Box::from_raw(app_leak));
}
return;
}
crate::app_eprintln!("[psysonic] Windows power suspend/resume notifications registered for audio");
// `registration` is an opaque handle returned by Win32 API. It does not own
// Rust resources, so dropping the local copy is fine; callback context is
// intentionally leaked above for process-lifetime notifications.
}