SetSprite → SetImage, remove fifo & shell label
This commit is contained in:
@@ -52,7 +52,7 @@ pub struct WlxHapticsParams {
|
|||||||
pub enum WlxModifyPanelCommand {
|
pub enum WlxModifyPanelCommand {
|
||||||
SetText(String),
|
SetText(String),
|
||||||
SetColor(String),
|
SetColor(String),
|
||||||
SetSprite(String),
|
SetImage(String),
|
||||||
SetVisible(bool),
|
SetVisible(bool),
|
||||||
SetStickyState(bool),
|
SetStickyState(bool),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
|
|||||||
let command = match command {
|
let command = match command {
|
||||||
SubcommandPanelModify::SetText { text } => packet_client::WlxModifyPanelCommand::SetText(text),
|
SubcommandPanelModify::SetText { text } => packet_client::WlxModifyPanelCommand::SetText(text),
|
||||||
SubcommandPanelModify::SetColor { hex_color } => packet_client::WlxModifyPanelCommand::SetColor(hex_color),
|
SubcommandPanelModify::SetColor { hex_color } => packet_client::WlxModifyPanelCommand::SetColor(hex_color),
|
||||||
SubcommandPanelModify::SetSprite { absolute_path } => packet_client::WlxModifyPanelCommand::SetSprite(absolute_path),
|
SubcommandPanelModify::SetImage { absolute_path } => packet_client::WlxModifyPanelCommand::SetImage(absolute_path),
|
||||||
SubcommandPanelModify::SetVisible { visible_0_or_1 } => packet_client::WlxModifyPanelCommand::SetVisible(visible_0_or_1 != 0),
|
SubcommandPanelModify::SetVisible { visible_0_or_1 } => packet_client::WlxModifyPanelCommand::SetVisible(visible_0_or_1 != 0),
|
||||||
SubcommandPanelModify::SetStickyState { sticky_state_0_or_1 } => packet_client::WlxModifyPanelCommand::SetStickyState(sticky_state_0_or_1 != 0),
|
SubcommandPanelModify::SetStickyState { sticky_state_0_or_1 } => packet_client::WlxModifyPanelCommand::SetStickyState(sticky_state_0_or_1 != 0),
|
||||||
};
|
};
|
||||||
@@ -266,11 +266,11 @@ enum SubcommandPanelModify {
|
|||||||
hex_color: String,
|
hex_color: String,
|
||||||
},
|
},
|
||||||
/// Set the content of a <sprite> or <image>. Max size for <sprite> is 256x256.
|
/// Set the content of a <sprite> or <image>. Max size for <sprite> is 256x256.
|
||||||
SetSprite {
|
SetImage {
|
||||||
/// Absolute path to a svg, gif, png, jpeg or webp image.
|
/// Absolute path to a svg, gif, png, jpeg or webp image.
|
||||||
absolute_path: String,
|
absolute_path: String,
|
||||||
},
|
},
|
||||||
/// Set the visibility of a <div>, <rectangle>, <label> or <sprite>
|
/// Set the visibility of a <div>, <rectangle>, <label>, <sprite> or <image>
|
||||||
SetVisible {
|
SetVisible {
|
||||||
visible_0_or_1: u8,
|
visible_0_or_1: u8,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub enum PlayspaceTask {
|
|||||||
pub enum ModifyPanelCommand {
|
pub enum ModifyPanelCommand {
|
||||||
SetText(String),
|
SetText(String),
|
||||||
SetColor(String),
|
SetColor(String),
|
||||||
SetSprite(String),
|
SetImage(String),
|
||||||
SetVisible(bool),
|
SetVisible(bool),
|
||||||
SetStickyState(bool),
|
SetStickyState(bool),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -495,8 +495,8 @@ impl Connection {
|
|||||||
packet_client::WlxModifyPanelCommand::SetText(text) => {
|
packet_client::WlxModifyPanelCommand::SetText(text) => {
|
||||||
ModifyPanelCommand::SetText(text)
|
ModifyPanelCommand::SetText(text)
|
||||||
}
|
}
|
||||||
packet_client::WlxModifyPanelCommand::SetSprite(sprite) => {
|
packet_client::WlxModifyPanelCommand::SetImage(sprite) => {
|
||||||
ModifyPanelCommand::SetSprite(sprite)
|
ModifyPanelCommand::SetImage(sprite)
|
||||||
}
|
}
|
||||||
packet_client::WlxModifyPanelCommand::SetStickyState(sticky) => {
|
packet_client::WlxModifyPanelCommand::SetStickyState(sticky) => {
|
||||||
ModifyPanelCommand::SetStickyState(sticky)
|
ModifyPanelCommand::SetStickyState(sticky)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Custom UI Elements
|
# Custom UI Elements
|
||||||
|
|
||||||
|
Elements on custom panels may be modified at runtime using `wayvrctl`.
|
||||||
|
|
||||||
|
For more, refer to: `wayvrctl panel-modify --help`
|
||||||
|
|
||||||
### Labels
|
### Labels
|
||||||
|
|
||||||
#### Clock label
|
#### Clock label
|
||||||
@@ -14,43 +18,6 @@ See the Custom Timezones section for more info on timezones. Skip `_timezone` to
|
|||||||
<label _source="clock" _display="time" _timezone="0" [...] />
|
<label _source="clock" _display="time" _timezone="0" [...] />
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Fifo label
|
|
||||||
|
|
||||||
Fifo label creates a fifo on your system that other programs can pipe output into.
|
|
||||||
|
|
||||||
- The label will look for the last complete line to use as its text.
|
|
||||||
- If the pipe breaks due to an IO error, re-creation is attempted after 15 seconds.
|
|
||||||
- `_path` supports environment variables, but not `~`!
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<label _source="fifo" _path="$XDG_RUNTIME_DIR/my-test-label" [...] />
|
|
||||||
```
|
|
||||||
|
|
||||||
Example script to test with:
|
|
||||||
```bash
|
|
||||||
for i in {0..99}; do echo "i is $i" > $XDG_RUNTIME_DIR/my-test-label; sleep 1; done
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Shell Exec label
|
|
||||||
|
|
||||||
This label executes a shell script using the `sh` shell.
|
|
||||||
|
|
||||||
- Write lines to the script's stdout to update the label text.
|
|
||||||
- The label will look for the last complete line to use as its text.
|
|
||||||
- Long-running scripts are allowed, but the label is only updated while the HMD is active.
|
|
||||||
- If the script exits successfully (code 0), it will be re-ran on the next frame. Otherwise, it will be re-ran in 15s.
|
|
||||||
- Control the pacing from inside the script itself. For example, adding a sleep 5 will make the script execute at most once per 5 seconds.
|
|
||||||
- `_exec` supports everything that `sh` supports!
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<label _source="shell" _exec="$HOME/.local/bin/my-test-script.sh" [...] />
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/usr/bin/bash
|
|
||||||
echo "This is my script's output!"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Battery label
|
#### Battery label
|
||||||
|
|
||||||
This is a label type that's used internally to display battery states.
|
This is a label type that's used internally to display battery states.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
process::{Command, Stdio},
|
process::{Child, Command, Stdio},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{Arc, atomic::Ordering},
|
sync::{Arc, atomic::Ordering},
|
||||||
@@ -359,15 +359,13 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||||||
carry_over: RefCell::new(None),
|
carry_over: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
let piped = attribs.get_value("_update_label").is_some_and(|s| s == "1");
|
|
||||||
|
|
||||||
layout.add_event_listener::<AppState, S>(
|
layout.add_event_listener::<AppState, S>(
|
||||||
attribs.widget_id,
|
attribs.widget_id,
|
||||||
EventListenerKind::InternalStateChange,
|
EventListenerKind::InternalStateChange,
|
||||||
Box::new({
|
Box::new({
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
move |common, _data, _, _| {
|
move |_, _, _, _| {
|
||||||
shell_on_tick(&state, common, piped);
|
shell_on_tick(&state);
|
||||||
Ok(EventResult::Pass)
|
Ok(EventResult::Pass)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -429,8 +427,7 @@ pub(super) fn setup_custom_button<S: 'static>(
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ShellButtonMutableState {
|
struct ShellButtonMutableState {
|
||||||
reader: Option<PipeReaderThread>,
|
child: Option<Child>,
|
||||||
pid: Option<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ShellButtonState {
|
struct ShellButtonState {
|
||||||
@@ -443,17 +440,18 @@ struct ShellButtonState {
|
|||||||
fn shell_on_action(state: &ShellButtonState) -> anyhow::Result<()> {
|
fn shell_on_action(state: &ShellButtonState) -> anyhow::Result<()> {
|
||||||
let mut mut_state = state.mut_state.borrow_mut();
|
let mut mut_state = state.mut_state.borrow_mut();
|
||||||
|
|
||||||
if mut_state.reader.as_ref().is_some_and(|r| !r.is_finished())
|
if let Some(child) = mut_state.child.as_mut()
|
||||||
&& let Some(pid) = mut_state.pid.as_ref()
|
&& let Ok(None) = child.try_wait()
|
||||||
{
|
{
|
||||||
log::info!("ShellExec triggered while child is still running; sending SIGUSR1");
|
log::info!("ShellExec triggered while child is still running; sending SIGUSR1");
|
||||||
let _ = Command::new("kill")
|
let _ = Command::new("kill")
|
||||||
.arg("-s")
|
.arg("-s")
|
||||||
.arg("USR1")
|
.arg("USR1")
|
||||||
.arg(pid.to_string())
|
.arg(child.id().to_string())
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.wait();
|
.wait();
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,26 +462,19 @@ fn shell_on_action(state: &ShellButtonState) -> anyhow::Result<()> {
|
|||||||
.spawn()
|
.spawn()
|
||||||
.with_context(|| format!("Failed to run shell script: '{}'", &state.exec))?;
|
.with_context(|| format!("Failed to run shell script: '{}'", &state.exec))?;
|
||||||
|
|
||||||
mut_state.pid = Some(child.id());
|
mut_state.child = Some(child);
|
||||||
mut_state.reader = Some(PipeReaderThread::new_from_child(child));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shell_on_tick(state: &ShellButtonState, common: &mut event::CallbackDataCommon, piped: bool) {
|
fn shell_on_tick(state: &ShellButtonState) {
|
||||||
let mut mut_state = state.mut_state.borrow_mut();
|
let mut mut_state = state.mut_state.borrow_mut();
|
||||||
|
|
||||||
let Some(reader) = mut_state.reader.as_mut() else {
|
let Some(child) = mut_state.child.as_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if piped && let Some(text) = reader.get_last_line() {
|
if let Ok(Some(_)) = child.try_wait() {
|
||||||
state
|
mut_state.child = None;
|
||||||
.button
|
|
||||||
.set_text(common, Translation::from_raw_text(&text));
|
|
||||||
}
|
|
||||||
|
|
||||||
if reader.is_finished() {
|
|
||||||
mut_state.reader = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
use regex::Regex;
|
|
||||||
use std::{
|
|
||||||
fs,
|
|
||||||
io::{BufRead, BufReader},
|
|
||||||
process::Child,
|
|
||||||
sync::{
|
|
||||||
Arc, LazyLock,
|
|
||||||
mpsc::{self, Receiver},
|
|
||||||
},
|
|
||||||
thread::JoinHandle,
|
|
||||||
};
|
|
||||||
|
|
||||||
static ENV_VAR_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
|
||||||
Regex::new(r"\$\{([A-Z_][A-Z0-9_]*)}|\$([A-Z_][A-Z0-9_]*)").unwrap() // want panic
|
|
||||||
});
|
|
||||||
|
|
||||||
pub(super) fn expand_env_vars(template: &str) -> String {
|
|
||||||
ENV_VAR_REGEX
|
|
||||||
.replace_all(template, |caps: ®ex::Captures| {
|
|
||||||
let var_name = caps.get(1).or_else(|| caps.get(2)).unwrap().as_str();
|
|
||||||
std::env::var(var_name)
|
|
||||||
.inspect_err(|e| log::warn!("Unable to substitute env var {var_name}: {e:?}"))
|
|
||||||
.unwrap_or_default()
|
|
||||||
})
|
|
||||||
.into_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct PipeReaderThread {
|
|
||||||
receiver: Receiver<String>,
|
|
||||||
handle: JoinHandle<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipeReaderThread {
|
|
||||||
pub fn new_from_child(mut c: Child) -> Self {
|
|
||||||
const BUF_LEN: usize = 128;
|
|
||||||
let (sender, receiver) = mpsc::sync_channel::<String>(4);
|
|
||||||
|
|
||||||
let handle = std::thread::spawn({
|
|
||||||
move || {
|
|
||||||
let stdout = c.stdout.take().unwrap();
|
|
||||||
let mut reader = BufReader::new(stdout);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut buf = String::with_capacity(BUF_LEN);
|
|
||||||
match reader.read_line(&mut buf) {
|
|
||||||
Ok(0) => {
|
|
||||||
// EOF reached
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = sender.try_send(buf);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("Error reading pipe: {e:?}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.wait()
|
|
||||||
.inspect_err(|e| log::error!("Failed to wait for child process: {e:?}"))
|
|
||||||
.is_ok_and(|c| c.success())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self { receiver, handle }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_from_fifo(path: Arc<str>) -> Self {
|
|
||||||
const BUF_LEN: usize = 128;
|
|
||||||
let (sender, receiver) = mpsc::sync_channel::<String>(4);
|
|
||||||
|
|
||||||
let handle = std::thread::spawn({
|
|
||||||
move || {
|
|
||||||
let Ok(mut reader) = fs::File::open(&*path)
|
|
||||||
.inspect_err(|e| {
|
|
||||||
log::warn!("Failed to open fifo: {e:?}");
|
|
||||||
})
|
|
||||||
.map(|r| BufReader::new(r))
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut buf = String::with_capacity(BUF_LEN);
|
|
||||||
match reader.read_line(&mut buf) {
|
|
||||||
Ok(0) => {
|
|
||||||
// EOF reached
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = sender.try_send(buf);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("Error reading fifo: {e:?}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self { receiver, handle }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_last_line(&mut self) -> Option<String> {
|
|
||||||
self.receiver.try_iter().last()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_finished(&self) -> bool {
|
|
||||||
self.handle.is_finished()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_success(self) -> bool {
|
|
||||||
self.handle.join().unwrap_or(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,7 @@
|
|||||||
use std::{
|
use std::rc::Rc;
|
||||||
cell::RefCell,
|
|
||||||
fs,
|
|
||||||
os::unix::fs::FileTypeExt,
|
|
||||||
process::{Command, Stdio},
|
|
||||||
rc::Rc,
|
|
||||||
sync::Arc,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use interprocess::os::unix::fifo_file::create_fifo;
|
|
||||||
use wgui::{
|
use wgui::{
|
||||||
drawing,
|
drawing,
|
||||||
event::{self, EventCallback},
|
event::{self, EventCallback},
|
||||||
@@ -21,9 +11,7 @@ use wgui::{
|
|||||||
widget::{EventResult, label::WidgetLabel},
|
widget::{EventResult, label::WidgetLabel},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{gui::panel::helper::PipeReaderThread, state::AppState};
|
use crate::state::AppState;
|
||||||
|
|
||||||
use super::helper::expand_env_vars;
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub(super) fn setup_custom_label<S: 'static>(
|
pub(super) fn setup_custom_label<S: 'static>(
|
||||||
@@ -37,42 +25,6 @@ pub(super) fn setup_custom_label<S: 'static>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let callback: EventCallback<AppState, S> = match source {
|
let callback: EventCallback<AppState, S> = match source {
|
||||||
"shell" => {
|
|
||||||
let Some(exec) = attribs.get_value("_exec") else {
|
|
||||||
log::warn!("label with shell source but no exec attribute!");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let state = ShellLabelState {
|
|
||||||
exec: exec.to_string(),
|
|
||||||
mut_state: RefCell::new(PipeLabelMutableState {
|
|
||||||
reader: None,
|
|
||||||
next_try: Instant::now(),
|
|
||||||
}),
|
|
||||||
carry_over: RefCell::new(None),
|
|
||||||
};
|
|
||||||
Box::new(move |common, data, _, _| {
|
|
||||||
let _ = shell_on_tick(&state, common, data).inspect_err(|e| log::error!("{e:?}"));
|
|
||||||
Ok(EventResult::Pass)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"fifo" => {
|
|
||||||
let Some(path) = attribs.get_value("_path") else {
|
|
||||||
log::warn!("label with fifo source but no path attribute!");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let state = FifoLabelState {
|
|
||||||
path: expand_env_vars(path).into(),
|
|
||||||
carry_over: RefCell::new(None),
|
|
||||||
mut_state: RefCell::new(PipeLabelMutableState {
|
|
||||||
reader: None,
|
|
||||||
next_try: Instant::now(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
Box::new(move |common, data, _, _| {
|
|
||||||
fifo_on_tick(&state, common, data);
|
|
||||||
Ok(EventResult::Pass)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"battery" => {
|
"battery" => {
|
||||||
let Some(device) = attribs
|
let Some(device) = attribs
|
||||||
.get_value("_device")
|
.get_value("_device")
|
||||||
@@ -186,126 +138,6 @@ pub(super) fn setup_custom_label<S: 'static>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PipeLabelMutableState {
|
|
||||||
reader: Option<PipeReaderThread>,
|
|
||||||
next_try: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ShellLabelState {
|
|
||||||
exec: String,
|
|
||||||
mut_state: RefCell<PipeLabelMutableState>,
|
|
||||||
carry_over: RefCell<Option<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shell_on_tick(
|
|
||||||
state: &ShellLabelState,
|
|
||||||
common: &mut event::CallbackDataCommon,
|
|
||||||
data: &mut event::CallbackData,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let mut mut_state = state.mut_state.borrow_mut();
|
|
||||||
|
|
||||||
if let Some(reader) = mut_state.reader.as_mut() {
|
|
||||||
if let Some(text) = reader.get_last_line() {
|
|
||||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
|
||||||
label.set_text(common, Translation::from_raw_text(&text));
|
|
||||||
}
|
|
||||||
|
|
||||||
if reader.is_finished() && !mut_state.reader.take().unwrap().check_success() {
|
|
||||||
mut_state.next_try = Instant::now() + Duration::from_secs(15);
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
} else if mut_state.next_try > Instant::now() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = Command::new("sh")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(&state.exec)
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.with_context(|| format!("Failed to run shell script: '{}'", &state.exec))?;
|
|
||||||
|
|
||||||
mut_state.reader = Some(PipeReaderThread::new_from_child(child));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FifoLabelState {
|
|
||||||
path: Arc<str>,
|
|
||||||
mut_state: RefCell<PipeLabelMutableState>,
|
|
||||||
carry_over: RefCell<Option<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FifoLabelState {
|
|
||||||
fn try_remove_fifo(&self) -> anyhow::Result<()> {
|
|
||||||
let meta = match fs::metadata(&*self.path) {
|
|
||||||
Ok(meta) => meta,
|
|
||||||
Err(e) => {
|
|
||||||
if fs::exists(&*self.path).unwrap_or(true) {
|
|
||||||
anyhow::bail!("Could not stat existing file at {}: {e:?}", &self.path);
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !meta.file_type().is_fifo() {
|
|
||||||
anyhow::bail!("Existing file at {} is not a FIFO", &self.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = fs::remove_file(&*self.path) {
|
|
||||||
anyhow::bail!("Unable to remove existing FIFO at {}: {e:?}", &self.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for FifoLabelState {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Err(e) = self.try_remove_fifo() {
|
|
||||||
log::debug!("{e:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fifo_on_tick(
|
|
||||||
state: &FifoLabelState,
|
|
||||||
common: &mut event::CallbackDataCommon,
|
|
||||||
data: &mut event::CallbackData,
|
|
||||||
) {
|
|
||||||
let mut mut_state = state.mut_state.borrow_mut();
|
|
||||||
|
|
||||||
let Some(reader) = mut_state.reader.as_mut() else {
|
|
||||||
if mut_state.next_try > Instant::now() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = state.try_remove_fifo() {
|
|
||||||
mut_state.next_try = Instant::now() + Duration::from_secs(15);
|
|
||||||
log::warn!("Requested FIFO path is taken: {e:?}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = create_fifo(&*state.path, 0o777) {
|
|
||||||
mut_state.next_try = Instant::now() + Duration::from_secs(15);
|
|
||||||
log::warn!("Failed to create FIFO: {e:?}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mut_state.reader = Some(PipeReaderThread::new_from_fifo(state.path.clone()));
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(text) = reader.get_last_line() {
|
|
||||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
|
||||||
label.set_text(common, Translation::from_raw_text(&text));
|
|
||||||
}
|
|
||||||
|
|
||||||
if reader.is_finished() && !mut_state.reader.take().unwrap().check_success() {
|
|
||||||
mut_state.next_try = Instant::now() + Duration::from_secs(15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const BAT_LOW: drawing::Color = drawing::Color::new(0.69, 0.38, 0.38, 1.);
|
const BAT_LOW: drawing::Color = drawing::Color::new(0.69, 0.38, 0.38, 1.);
|
||||||
const BAT_NORMAL: drawing::Color = drawing::Color::new(0.55, 0.84, 0.79, 1.);
|
const BAT_NORMAL: drawing::Color = drawing::Color::new(0.55, 0.84, 0.79, 1.);
|
||||||
const BAT_CHARGING: drawing::Color = drawing::Color::new(0.38, 0.50, 0.62, 1.);
|
const BAT_CHARGING: drawing::Color = drawing::Color::new(0.38, 0.50, 0.62, 1.);
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ fn apply_custom_command(
|
|||||||
anyhow::bail!("No <label> or <Button> with such id.");
|
anyhow::bail!("No <label> or <Button> with such id.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ModifyPanelCommand::SetSprite(path) => {
|
ModifyPanelCommand::SetImage(path) => {
|
||||||
if let Ok(pair) = panel
|
if let Ok(pair) = panel
|
||||||
.parser_state
|
.parser_state
|
||||||
.fetch_widget(&panel.layout.state, element)
|
.fetch_widget(&panel.layout.state, element)
|
||||||
|
|||||||
Reference in New Issue
Block a user