use std::{ cell::RefCell, io::BufReader, process::{Child, ChildStdout}, }; use wgui::{ event::{self, EventCallback, EventListenerCollection, EventListenerKind, ListenerHandleVec}, parser::CustomAttribsInfoOwned, }; use crate::{ backend::{common::OverlaySelector, task::TaskType, wayvr::WayVRAction}, config::{save_layout, AStrSetExt}, state::AppState, }; use super::helper::read_label_from_pipe; pub(super) fn setup_custom_button( attribs: &CustomAttribsInfoOwned, listeners: &mut EventListenerCollection, listener_handles: &mut ListenerHandleVec, _app: &AppState, ) { const EVENTS: [(&str, EventListenerKind); 2] = [ ("_press", EventListenerKind::MousePress), ("_release", EventListenerKind::MouseRelease), ]; for (name, kind) in &EVENTS { let Some(action) = attribs.get_value(name) else { continue; }; let mut args = action.split_whitespace(); let Some(command) = args.next() else { continue; }; let callback: EventCallback = match command { "::DashToggle" => Box::new(move |_common, _data, app, _| { app.tasks .enqueue(TaskType::WayVR(WayVRAction::ToggleDashboard)); Ok(()) }), "::OverlayToggle" => { let Some(selector) = args.next() else { log::warn!("Missing argument for {command}"); continue; }; let selector = OverlaySelector::Name(selector.into()); Box::new(move |_common, _data, app, _| { app.tasks.enqueue(TaskType::Overlay( selector.clone(), Box::new(|app, o| { o.want_visible = !o.want_visible; if o.recenter { o.show_hide = o.want_visible; o.reset(app, false); } let mut state_dirty = false; if !o.want_visible { state_dirty |= app.session.config.show_screens.arc_rm(o.name.as_ref()); } else if o.want_visible { state_dirty |= app.session.config.show_screens.arc_set(o.name.clone()); } if state_dirty { match save_layout(&app.session.config) { Ok(()) => log::debug!("Saved state"), Err(e) => { log::error!("Failed to save state: {e:?}"); } } } }), )); Ok(()) }) } "::WatchHide" => todo!(), "::WatchSwapHand" => todo!(), // TODO #[allow(clippy::match_same_arms)] "::EditToggle" => return, // TODO #[allow(clippy::match_same_arms)] "::OscSend" => return, // shell _ => todo!(), }; listeners.register(listener_handles, attribs.widget_id, *kind, callback); } } struct ShellButtonMutableState { child: Option, reader: Option>, } struct ShellButtonState { exec: String, mut_state: RefCell, carry_over: RefCell>, } // TODO #[allow(clippy::missing_const_for_fn)] fn shell_on_action( _state: &ShellButtonState, _common: &mut event::CallbackDataCommon, _data: &mut event::CallbackData, ) { //let mut mut_state = state.mut_state.borrow_mut(); } fn shell_on_tick( state: &ShellButtonState, _common: &mut event::CallbackDataCommon, _data: &mut event::CallbackData, ) { let mut mut_state = state.mut_state.borrow_mut(); if let Some(mut child) = mut_state.child.take() { match child.try_wait() { // not exited yet Ok(None) => { if let Some(_text) = mut_state.reader.as_mut().and_then(|r| { read_label_from_pipe("child process", r, &mut state.carry_over.borrow_mut()) }) { //TODO update label } mut_state.child = Some(child); } // exited successfully Ok(Some(code)) if code.success() => { if let Some(_text) = mut_state.reader.as_mut().and_then(|r| { read_label_from_pipe("child process", r, &mut state.carry_over.borrow_mut()) }) { //TODO update label } mut_state.child = None; } // exited with failure Ok(Some(code)) => { mut_state.child = None; log::warn!("Label process exited with code {code}"); } // lost Err(_) => { mut_state.child = None; log::warn!("Label child process lost."); } } } }