feat(player): keep the display awake while a stream is playing

Currently the Windows shell never tells the OS not to dim the screen
or sleep, so a long uninterrupted playback can hit a screensaver or
display-off timeout mid-movie.

Observe mpv's `pause` property from the player event thread and call
SetThreadExecutionState with ES_DISPLAY_REQUIRED|ES_SYSTEM_REQUIRED
while playing, dropping back to ES_CONTINUOUS only on pause and on
Shutdown. The state is bound to the event thread's lifetime, so OS
auto-resets it when the shell exits.

Test: cargo fmt --all -- --check, cargo clippy --all -- -D warnings.
This commit is contained in:
Claude 2026-05-10 18:04:44 +00:00
parent bbbe882faf
commit f790985541
No known key found for this signature in database

View file

@ -1,13 +1,15 @@
use crate::stremio_app::ipc;
use crate::stremio_app::RPCResponse;
use flume::{Receiver, Sender};
use libmpv2::{events::Event, events::EventContext, Format, Mpv, SetData};
use libmpv2::{events::Event, events::EventContext, events::PropertyData, Format, Mpv, SetData};
use native_windows_gui::{self as nwg, PartialUi};
use std::{
sync::Arc,
thread::{self, JoinHandle},
};
use winapi::shared::windef::HWND;
use winapi::um::winbase::SetThreadExecutionState;
use winapi::um::winnt::{ES_CONTINUOUS, ES_DISPLAY_REQUIRED, ES_SYSTEM_REQUIRED};
use crate::stremio_app::stremio_player::{
CmdVal, InMsg, InMsgArgs, InMsgFn, PlayerEnded, PlayerEvent, PlayerProprChange, PlayerResponse,
@ -92,6 +94,10 @@ fn create_event_thread(
event_context
.disable_deprecated_events()
.expect("failed to disable deprecated MPV events");
// Shell-owned observer so the display stays awake while playing.
if let Err(error) = event_context.observe_property("pause", Format::Flag, 0) {
eprintln!("failed to observe pause: {error:?}");
}
// -- Event handler loop --
@ -115,18 +121,26 @@ fn create_event_thread(
// even if you don't do anything with the events, it is still necessary to empty the event loop
let player_response = match event {
Event::PropertyChange { name, change, .. } => PlayerResponse(
"mpv-prop-change",
PlayerEvent::PropChange(PlayerProprChange::from_name_value(
name.to_string(),
change,
)),
),
Event::PropertyChange { name, change, .. } => {
if name == "pause" {
if let PropertyData::Flag(paused) = change {
set_sleep_inhibit(!paused);
}
}
PlayerResponse(
"mpv-prop-change",
PlayerEvent::PropChange(PlayerProprChange::from_name_value(
name.to_string(),
change,
)),
)
}
Event::EndFile(reason) => PlayerResponse(
"mpv-event-ended",
PlayerEvent::End(PlayerEnded::from_end_reason(reason)),
),
Event::Shutdown => {
set_sleep_inhibit(false);
break;
}
_ => continue,
@ -139,6 +153,17 @@ fn create_event_thread(
})
}
fn set_sleep_inhibit(playing: bool) {
let flags = if playing {
ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED
} else {
ES_CONTINUOUS
};
unsafe {
SetThreadExecutionState(flags);
}
}
fn create_message_thread(
mpv: Arc<Mpv>,
observe_property_sender: Sender<ObserveProperty>,