feat: config验证 + StateMachine + WifiPlugin + ScreenPlugin

团队交付 Phase 1 第一轮:
- 张明远: config.rs 完整验证逻辑 (Display/VideoItem/Transition/Scenes/StateMachine)
- 李思琪: state_machine.rs 完整实现 (defer/ignore triggers, 加权随机, loop range)
- 王浩然: wifi/mod.rs WiFi管理插件 (scan/connect/status/ap via nmcli)
- 赵雨薇: screen/mod.rs 屏幕管理插件 (systemd-inhibit唤醒锁 + unclutter光标)

cargo check 零 warning 通过。

Co-Authored-By: GPT-5.4 <noreply@openai.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
showen
2026-03-12 05:31:21 +08:00
parent 311e4bad0e
commit 3654af5843
4 changed files with 927 additions and 29 deletions

View File

@@ -2,22 +2,106 @@
//!
//! 唤醒锁systemd-inhibit、光标隐藏unclutter
use crate::core::message::Message;
use crate::core::plugin::{Plugin, PluginContext, PluginInfo, Platform};
use crate::core::{message::Message, plugin::*};
use anyhow::Result;
use std::process::{Child, Command, Stdio};
pub struct ScreenPlugin {
ctx: Option<PluginContext>,
wake_lock_child: Option<Child>,
cursor_hidden: bool,
}
impl ScreenPlugin {
pub fn new() -> Self {
Self { ctx: None }
Self {
ctx: None,
wake_lock_child: None,
cursor_hidden: false,
}
}
#[cfg(target_os = "linux")]
fn start_wake_lock(&mut self) {
if self.wake_lock_child.is_some() {
return;
}
match Command::new("systemd-inhibit")
.arg("--what=idle")
.arg("--who=ShowenV2")
.arg("--why=Prevent screen lock during playback")
.arg("sleep")
.arg("infinity")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
{
Ok(child) => self.wake_lock_child = Some(child),
Err(err) => eprintln!("[ScreenPlugin] 启动防息屏失败: {err}"),
}
}
#[cfg(not(target_os = "linux"))]
fn start_wake_lock(&mut self) {}
#[cfg(target_os = "linux")]
fn stop_wake_lock(&mut self) {
if let Some(mut child) = self.wake_lock_child.take() {
if let Err(err) = child.kill() {
eprintln!("[ScreenPlugin] 停止防息屏失败: {err}");
}
let _ = child.wait();
}
}
#[cfg(not(target_os = "linux"))]
fn stop_wake_lock(&mut self) {}
#[cfg(target_os = "linux")]
fn set_cursor_hidden(&mut self, hidden: bool) {
if hidden == self.cursor_hidden {
return;
}
if hidden {
match Command::new("unclutter")
.arg("-idle")
.arg("0")
.arg("-root")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
{
Ok(_) => self.cursor_hidden = true,
Err(err) => eprintln!("[ScreenPlugin] 隐藏光标失败: {err}"),
}
} else {
match Command::new("pkill")
.arg("unclutter")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
{
Ok(_) => self.cursor_hidden = false,
Err(err) => eprintln!("[ScreenPlugin] 恢复光标失败: {err}"),
}
}
}
#[cfg(not(target_os = "linux"))]
fn set_cursor_hidden(&mut self, hidden: bool) {
self.cursor_hidden = hidden;
}
}
impl Plugin for ScreenPlugin {
fn id(&self) -> &'static str { "screen" }
fn id(&self) -> &'static str {
"screen"
}
fn info(&self) -> PluginInfo {
PluginInfo {
@@ -33,7 +117,42 @@ impl Plugin for ScreenPlugin {
Ok(())
}
fn start(&mut self) -> Result<()> { Ok(()) }
fn handle_message(&mut self, _msg: Message) -> Result<()> { Ok(()) }
fn stop(&mut self) -> Result<()> { Ok(()) }
fn start(&mut self) -> Result<()> {
if self
.ctx
.as_ref()
.map(|ctx| ctx.config.display.prevent_screen_lock)
.unwrap_or(false)
{
self.start_wake_lock();
}
self.set_cursor_hidden(true);
Ok(())
}
fn handle_message(&mut self, msg: Message) -> Result<()> {
match msg {
Message::ScreenLockRequest(lock) => {
if lock {
self.start_wake_lock();
} else {
self.stop_wake_lock();
}
}
Message::CursorVisibility(visible) => self.set_cursor_hidden(!visible),
Message::Shutdown => {
self.stop()?;
}
_ => {}
}
Ok(())
}
fn stop(&mut self) -> Result<()> {
self.stop_wake_lock();
self.set_cursor_hidden(false);
Ok(())
}
}