less refcells; haptics
This commit is contained in:
@@ -100,6 +100,7 @@ pub struct CallbackData<'a> {
|
|||||||
pub node_id: taffy::NodeId,
|
pub node_id: taffy::NodeId,
|
||||||
pub dirty_nodes: &'a mut Vec<taffy::NodeId>,
|
pub dirty_nodes: &'a mut Vec<taffy::NodeId>,
|
||||||
pub needs_redraw: bool,
|
pub needs_redraw: bool,
|
||||||
|
pub trigger_haptics: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WidgetCallback<'a> for CallbackData<'a> {
|
impl<'a> WidgetCallback<'a> for CallbackData<'a> {
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ pub type BoxWidget = Arc<Mutex<WidgetState>>;
|
|||||||
pub type WidgetMap = HopSlotMap<slotmap::DefaultKey, BoxWidget>;
|
pub type WidgetMap = HopSlotMap<slotmap::DefaultKey, BoxWidget>;
|
||||||
|
|
||||||
struct PushEventState<'a> {
|
struct PushEventState<'a> {
|
||||||
pub needs_redraw: bool,
|
|
||||||
pub animations: &'a mut Vec<animation::Animation>,
|
pub animations: &'a mut Vec<animation::Animation>,
|
||||||
pub transform_stack: &'a mut TransformStack,
|
pub transform_stack: &'a mut TransformStack,
|
||||||
|
pub needs_redraw: bool,
|
||||||
|
pub trigger_haptics: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
@@ -40,6 +41,7 @@ pub struct Layout {
|
|||||||
pub content_size: Vec2,
|
pub content_size: Vec2,
|
||||||
|
|
||||||
pub needs_redraw: bool,
|
pub needs_redraw: bool,
|
||||||
|
pub haptics_triggered: bool,
|
||||||
|
|
||||||
pub animations: Animations,
|
pub animations: Animations,
|
||||||
}
|
}
|
||||||
@@ -148,6 +150,7 @@ impl Layout {
|
|||||||
tree: &self.tree,
|
tree: &self.tree,
|
||||||
animations: state.animations,
|
animations: state.animations,
|
||||||
needs_redraw: &mut state.needs_redraw,
|
needs_redraw: &mut state.needs_redraw,
|
||||||
|
trigger_haptics: &mut state.trigger_haptics,
|
||||||
node_id,
|
node_id,
|
||||||
style,
|
style,
|
||||||
taffy_layout: l,
|
taffy_layout: l,
|
||||||
@@ -185,15 +188,25 @@ impl Layout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_toggle_haptics_triggered(&mut self) -> bool {
|
||||||
|
if self.haptics_triggered {
|
||||||
|
self.haptics_triggered = false;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push_event(&mut self, event: &event::Event) -> anyhow::Result<()> {
|
pub fn push_event(&mut self, event: &event::Event) -> anyhow::Result<()> {
|
||||||
let mut transform_stack = TransformStack::new();
|
let mut transform_stack = TransformStack::new();
|
||||||
let mut animations_to_add = Vec::<animation::Animation>::new();
|
let mut animations_to_add = Vec::<animation::Animation>::new();
|
||||||
let mut dirty_nodes = Vec::new();
|
let mut dirty_nodes = Vec::new();
|
||||||
|
|
||||||
let mut state = PushEventState {
|
let mut state = PushEventState {
|
||||||
needs_redraw: false,
|
|
||||||
transform_stack: &mut transform_stack,
|
transform_stack: &mut transform_stack,
|
||||||
animations: &mut animations_to_add,
|
animations: &mut animations_to_add,
|
||||||
|
needs_redraw: false,
|
||||||
|
trigger_haptics: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.push_event_widget(&mut state, self.root_node, event, &mut dirty_nodes)?;
|
self.push_event_widget(&mut state, self.root_node, event, &mut dirty_nodes)?;
|
||||||
@@ -202,6 +215,10 @@ impl Layout {
|
|||||||
self.tree.mark_dirty(node)?;
|
self.tree.mark_dirty(node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state.trigger_haptics {
|
||||||
|
self.haptics_triggered = true;
|
||||||
|
}
|
||||||
|
|
||||||
if state.needs_redraw {
|
if state.needs_redraw {
|
||||||
self.needs_redraw = true;
|
self.needs_redraw = true;
|
||||||
}
|
}
|
||||||
@@ -242,6 +259,7 @@ impl Layout {
|
|||||||
widget_node_map,
|
widget_node_map,
|
||||||
widget_states,
|
widget_states,
|
||||||
needs_redraw: true,
|
needs_redraw: true,
|
||||||
|
haptics_triggered: false,
|
||||||
animations: Animations::default(),
|
animations: Animations::default(),
|
||||||
assets,
|
assets,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ pub struct EventParams<'a> {
|
|||||||
pub transform_stack: &'a TransformStack,
|
pub transform_stack: &'a TransformStack,
|
||||||
pub animations: &'a mut Vec<animation::Animation>,
|
pub animations: &'a mut Vec<animation::Animation>,
|
||||||
pub needs_redraw: &'a mut bool,
|
pub needs_redraw: &'a mut bool,
|
||||||
|
pub trigger_haptics: &'a mut bool,
|
||||||
pub dirty_nodes: &'a mut Vec<taffy::NodeId>,
|
pub dirty_nodes: &'a mut Vec<taffy::NodeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,8 +344,12 @@ impl WidgetState {
|
|||||||
widget_id,
|
widget_id,
|
||||||
node_id,
|
node_id,
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
|
trigger_haptics: false,
|
||||||
};
|
};
|
||||||
callback(&mut data);
|
callback(&mut data);
|
||||||
|
if data.trigger_haptics {
|
||||||
|
*params.trigger_haptics = true;
|
||||||
|
}
|
||||||
if data.needs_redraw {
|
if data.needs_redraw {
|
||||||
*params.needs_redraw = true;
|
*params.needs_redraw = true;
|
||||||
}
|
}
|
||||||
@@ -361,8 +366,12 @@ impl WidgetState {
|
|||||||
widget_id,
|
widget_id,
|
||||||
node_id,
|
node_id,
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
|
trigger_haptics: false,
|
||||||
};
|
};
|
||||||
callback(&mut data);
|
callback(&mut data);
|
||||||
|
if data.trigger_haptics {
|
||||||
|
*params.trigger_haptics = true;
|
||||||
|
}
|
||||||
if data.needs_redraw {
|
if data.needs_redraw {
|
||||||
*params.needs_redraw = true;
|
*params.needs_redraw = true;
|
||||||
}
|
}
|
||||||
@@ -379,8 +388,12 @@ impl WidgetState {
|
|||||||
widget_id,
|
widget_id,
|
||||||
node_id,
|
node_id,
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
|
trigger_haptics: false,
|
||||||
};
|
};
|
||||||
callback(&mut data, button);
|
callback(&mut data, button);
|
||||||
|
if data.trigger_haptics {
|
||||||
|
*params.trigger_haptics = true;
|
||||||
|
}
|
||||||
if data.needs_redraw {
|
if data.needs_redraw {
|
||||||
*params.needs_redraw = true;
|
*params.needs_redraw = true;
|
||||||
}
|
}
|
||||||
@@ -397,8 +410,12 @@ impl WidgetState {
|
|||||||
widget_id,
|
widget_id,
|
||||||
node_id,
|
node_id,
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
|
trigger_haptics: false,
|
||||||
};
|
};
|
||||||
callback(&mut data, button);
|
callback(&mut data, button);
|
||||||
|
if data.trigger_haptics {
|
||||||
|
*params.trigger_haptics = true;
|
||||||
|
}
|
||||||
if data.needs_redraw {
|
if data.needs_redraw {
|
||||||
*params.needs_redraw = true;
|
*params.needs_redraw = true;
|
||||||
}
|
}
|
||||||
@@ -414,8 +431,12 @@ impl WidgetState {
|
|||||||
widget_id,
|
widget_id,
|
||||||
node_id,
|
node_id,
|
||||||
needs_redraw: false,
|
needs_redraw: false,
|
||||||
|
trigger_haptics: false,
|
||||||
};
|
};
|
||||||
callback(&mut data);
|
callback(&mut data);
|
||||||
|
if data.trigger_haptics {
|
||||||
|
*params.trigger_haptics = true;
|
||||||
|
}
|
||||||
if data.needs_redraw {
|
if data.needs_redraw {
|
||||||
*params.needs_redraw = true;
|
*params.needs_redraw = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,11 +248,10 @@ where
|
|||||||
if extent_dirty && !create_ran {
|
if extent_dirty && !create_ran {
|
||||||
let extent = wl.get_desktop_extent();
|
let extent = wl.get_desktop_extent();
|
||||||
let origin = wl.get_desktop_origin();
|
let origin = wl.get_desktop_origin();
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
app.hid_provider
|
||||||
hid_provider
|
|
||||||
.inner
|
.inner
|
||||||
.set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32));
|
.set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32));
|
||||||
hid_provider
|
app.hid_provider
|
||||||
.inner
|
.inner
|
||||||
.set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32));
|
.set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ pub struct PointerHit {
|
|||||||
pub dist: f32,
|
pub dist: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Haptics {
|
pub struct Haptics {
|
||||||
pub intensity: f32,
|
pub intensity: f32,
|
||||||
pub duration: f32,
|
pub duration: f32,
|
||||||
@@ -403,10 +403,7 @@ where
|
|||||||
log::trace!("Hit: {} {:?}", hovered.state.name, hit);
|
log::trace!("Hit: {} {:?}", hovered.state.name, hit);
|
||||||
|
|
||||||
if pointer.now.grab && !pointer.before.grab && hovered.state.grabbable {
|
if pointer.now.grab && !pointer.before.grab && hovered.state.grabbable {
|
||||||
{
|
update_focus(&mut app.hid_provider.keyboard_focus, &hovered.state);
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
|
||||||
update_focus(&mut hid_provider.keyboard_focus, &hovered.state);
|
|
||||||
}
|
|
||||||
pointer.start_grab(hovered, &mut app.tasks);
|
pointer.start_grab(hovered, &mut app.tasks);
|
||||||
return (
|
return (
|
||||||
hit.dist,
|
hit.dist,
|
||||||
@@ -455,10 +452,7 @@ where
|
|||||||
|
|
||||||
if pointer.now.click && !pointer.before.click {
|
if pointer.now.click && !pointer.before.click {
|
||||||
pointer.interaction.clicked_id = Some(hit.overlay);
|
pointer.interaction.clicked_id = Some(hit.overlay);
|
||||||
{
|
update_focus(&mut app.hid_provider.keyboard_focus, &hovered.state);
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
|
||||||
update_focus(&mut hid_provider.keyboard_focus, &hovered.state);
|
|
||||||
}
|
|
||||||
hovered.backend.on_pointer(app, &hit, true);
|
hovered.backend.on_pointer(app, &hit, true);
|
||||||
} else if !pointer.now.click && pointer.before.click {
|
} else if !pointer.now.click && pointer.before.click {
|
||||||
if let Some(clicked_id) = pointer.interaction.clicked_id.take() {
|
if let Some(clicked_id) = pointer.interaction.clicked_id.take() {
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ pub fn openvr_run(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.hid_provider.borrow_mut().inner.commit();
|
state.hid_provider.inner.commit();
|
||||||
let mut buffers = CommandBuffers::default();
|
let mut buffers = CommandBuffers::default();
|
||||||
|
|
||||||
lines.update(universe.clone(), &mut overlay_mgr, &mut state)?;
|
lines.update(universe.clone(), &mut overlay_mgr, &mut state)?;
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ pub fn openxr_run(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.hid_provider.borrow_mut().inner.commit();
|
app.hid_provider.inner.commit();
|
||||||
|
|
||||||
let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic
|
let watch = overlays.mut_by_id(watch_id).unwrap(); // want panic
|
||||||
let watch_transform = watch.state.transform;
|
let watch_transform = watch.state.transform;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ impl InteractionHandler for GuiPanel {
|
|||||||
pos: hit.uv * self.layout.content_size,
|
pos: hit.uv * self.layout.content_size,
|
||||||
device: hit.pointer,
|
device: hit.pointer,
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap(); // want panic
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
||||||
@@ -92,15 +92,21 @@ impl InteractionHandler for GuiPanel {
|
|||||||
pos: hit.uv * self.layout.content_size,
|
pos: hit.uv * self.layout.content_size,
|
||||||
device: hit.pointer,
|
device: hit.pointer,
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap(); // want panic
|
||||||
|
|
||||||
None
|
self.layout
|
||||||
|
.check_toggle_haptics_triggered()
|
||||||
|
.then_some(Haptics {
|
||||||
|
intensity: 0.1,
|
||||||
|
duration: 0.01,
|
||||||
|
frequency: 5.0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
|
fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
|
||||||
self.layout
|
self.layout
|
||||||
.push_event(&WguiEvent::MouseLeave(MouseLeaveEvent { device: pointer }))
|
.push_event(&WguiEvent::MouseLeave(MouseLeaveEvent { device: pointer }))
|
||||||
.unwrap();
|
.unwrap(); // want panic
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_pointer(&mut self, _app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
fn on_pointer(&mut self, _app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||||
@@ -118,7 +124,7 @@ impl InteractionHandler for GuiPanel {
|
|||||||
button,
|
button,
|
||||||
device: hit.pointer,
|
device: hit.pointer,
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap(); // want panic
|
||||||
} else {
|
} else {
|
||||||
self.layout
|
self.layout
|
||||||
.push_event(&WguiEvent::MouseUp(MouseUpEvent {
|
.push_event(&WguiEvent::MouseUp(MouseUpEvent {
|
||||||
@@ -126,7 +132,7 @@ impl InteractionHandler for GuiPanel {
|
|||||||
button,
|
button,
|
||||||
device: hit.pointer,
|
device: hit.pointer,
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap(); // want panic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +183,7 @@ impl OverlayRenderer for GuiPanel {
|
|||||||
let mut cmd_buf = app
|
let mut cmd_buf = app
|
||||||
.gfx
|
.gfx
|
||||||
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)
|
||||||
.unwrap();
|
.unwrap(); // want panic
|
||||||
|
|
||||||
cmd_buf.begin_rendering(tgt)?;
|
cmd_buf.begin_rendering(tgt)?;
|
||||||
let primitives = wgui::drawing::draw(&self.layout)?;
|
let primitives = wgui::drawing::draw(&self.layout)?;
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
KEYBOARD_NAME, KeyButtonData, KeyState, KeyboardBackend, KeyboardState, handle_press,
|
KEYBOARD_NAME, KeyButtonData, KeyState, KeyboardBackend, KeyboardState,
|
||||||
handle_release,
|
|
||||||
layout::{self, AltModifier, KeyCapType},
|
layout::{self, AltModifier, KeyCapType},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,8 +38,7 @@ where
|
|||||||
{
|
{
|
||||||
let layout = layout::Layout::load_from_disk();
|
let layout = layout::Layout::load_from_disk();
|
||||||
let state = Rc::new(RefCell::new(KeyboardState {
|
let state = Rc::new(RefCell::new(KeyboardState {
|
||||||
hid: app.hid_provider.clone(),
|
invoke_action: None,
|
||||||
audio: app.audio_provider.clone(),
|
|
||||||
modifiers: 0,
|
modifiers: 0,
|
||||||
alt_modifier: match layout.alt_modifier {
|
alt_modifier: match layout.alt_modifier {
|
||||||
AltModifier::Shift => SHIFT,
|
AltModifier::Shift => SHIFT,
|
||||||
@@ -185,6 +183,7 @@ where
|
|||||||
EventListener::MouseEnter(Box::new({
|
EventListener::MouseEnter(Box::new({
|
||||||
let (k, kb) = (key_state.clone(), state.clone());
|
let (k, kb) = (key_state.clone(), state.clone());
|
||||||
move |data| {
|
move |data| {
|
||||||
|
data.trigger_haptics = true;
|
||||||
on_enter_anim(k.clone(), kb.clone(), data);
|
on_enter_anim(k.clone(), kb.clone(), data);
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
@@ -194,6 +193,7 @@ where
|
|||||||
EventListener::MouseLeave(Box::new({
|
EventListener::MouseLeave(Box::new({
|
||||||
let (k, kb) = (key_state.clone(), state.clone());
|
let (k, kb) = (key_state.clone(), state.clone());
|
||||||
move |data| {
|
move |data| {
|
||||||
|
data.trigger_haptics = true;
|
||||||
on_leave_anim(k.clone(), kb.clone(), data);
|
on_leave_anim(k.clone(), kb.clone(), data);
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
@@ -203,7 +203,11 @@ where
|
|||||||
EventListener::MousePress(Box::new({
|
EventListener::MousePress(Box::new({
|
||||||
let (k, kb) = (key_state.clone(), state.clone());
|
let (k, kb) = (key_state.clone(), state.clone());
|
||||||
move |data, button| {
|
move |data, button| {
|
||||||
handle_press(k.clone(), kb.clone(), button);
|
kb.borrow_mut().invoke_action = Some(super::InvokeAction {
|
||||||
|
key: k.clone(),
|
||||||
|
button,
|
||||||
|
pressed: true,
|
||||||
|
});
|
||||||
on_press_anim(k.clone(), data);
|
on_press_anim(k.clone(), data);
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
@@ -213,7 +217,12 @@ where
|
|||||||
EventListener::MouseRelease(Box::new({
|
EventListener::MouseRelease(Box::new({
|
||||||
let (k, kb) = (key_state.clone(), state.clone());
|
let (k, kb) = (key_state.clone(), state.clone());
|
||||||
move |data, button| {
|
move |data, button| {
|
||||||
if handle_release(k.clone(), kb.clone(), button) {
|
kb.borrow_mut().invoke_action = Some(super::InvokeAction {
|
||||||
|
key: k.clone(),
|
||||||
|
button,
|
||||||
|
pressed: false,
|
||||||
|
});
|
||||||
|
if !matches!(&k.button_state, KeyButtonData::Modifier { sticky, .. } if sticky.get()) {
|
||||||
on_release_anim(k.clone(), data);
|
on_release_anim(k.clone(), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,13 +236,9 @@ where
|
|||||||
let (k, kb) = (key_state.clone(), state.clone());
|
let (k, kb) = (key_state.clone(), state.clone());
|
||||||
move |data| {
|
move |data| {
|
||||||
if (kb.borrow().modifiers & modifier) != 0 {
|
if (kb.borrow().modifiers & modifier) != 0 {
|
||||||
if !k.drawn_state.get() {
|
|
||||||
on_press_anim(k.clone(), data);
|
on_press_anim(k.clone(), data);
|
||||||
k.drawn_state.set(true);
|
} else {
|
||||||
}
|
|
||||||
} else if k.drawn_state.get() {
|
|
||||||
on_release_anim(k.clone(), data);
|
on_release_anim(k.clone(), data);
|
||||||
k.drawn_state.set(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
@@ -312,14 +317,22 @@ fn on_leave_anim(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_press_anim(_: Rc<KeyState>, data: &mut event::CallbackData) {
|
fn on_press_anim(key_state: Rc<KeyState>, data: &mut event::CallbackData) {
|
||||||
|
if key_state.drawn_state.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let rect = data.obj.get_as_mut::<Rectangle>();
|
let rect = data.obj.get_as_mut::<Rectangle>();
|
||||||
rect.params.border_color = Color::new(1.0, 1.0, 1.0, 1.0);
|
rect.params.border_color = Color::new(1.0, 1.0, 1.0, 1.0);
|
||||||
data.needs_redraw = true;
|
data.needs_redraw = true;
|
||||||
|
key_state.drawn_state.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_release_anim(key_state: Rc<KeyState>, data: &mut event::CallbackData) {
|
fn on_release_anim(key_state: Rc<KeyState>, data: &mut event::CallbackData) {
|
||||||
|
if !key_state.drawn_state.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let rect = data.obj.get_as_mut::<Rectangle>();
|
let rect = data.obj.get_as_mut::<Rectangle>();
|
||||||
rect.params.border_color = key_state.border_color;
|
rect.params.border_color = key_state.border_color;
|
||||||
data.needs_redraw = true;
|
data.needs_redraw = true;
|
||||||
|
key_state.drawn_state.set(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,13 @@ use wgui::{drawing, event::MouseButton};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{
|
backend::{
|
||||||
input::InteractionHandler,
|
input::{Haptics, InteractionHandler, PointerHit},
|
||||||
overlay::{FrameMeta, OverlayBackend, OverlayRenderer, ShouldRender},
|
overlay::{FrameMeta, OverlayBackend, OverlayRenderer, ShouldRender},
|
||||||
},
|
},
|
||||||
graphics::CommandBuffers,
|
graphics::CommandBuffers,
|
||||||
gui::panel::GuiPanel,
|
gui::panel::GuiPanel,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::{
|
subsystem::hid::{ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey},
|
||||||
audio::{AudioOutput, AudioRole},
|
|
||||||
hid::{ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey},
|
|
||||||
input::HidWrapper,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
@@ -34,6 +30,21 @@ struct KeyboardBackend {
|
|||||||
state: Rc<RefCell<KeyboardState>>,
|
state: Rc<RefCell<KeyboardState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KeyboardBackend {
|
||||||
|
fn handle_invoke(&mut self, app: &mut AppState) {
|
||||||
|
let mut keyboard = self.state.borrow_mut();
|
||||||
|
let Some(action) = keyboard.invoke_action.take() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if action.pressed {
|
||||||
|
handle_press(app, &action.key, &mut keyboard, action.button);
|
||||||
|
} else {
|
||||||
|
handle_release(app, &action.key, &mut keyboard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OverlayBackend for KeyboardBackend {
|
impl OverlayBackend for KeyboardBackend {
|
||||||
fn set_interaction(&mut self, interaction: Box<dyn crate::backend::input::InteractionHandler>) {
|
fn set_interaction(&mut self, interaction: Box<dyn crate::backend::input::InteractionHandler>) {
|
||||||
self.panel.set_interaction(interaction);
|
self.panel.set_interaction(interaction);
|
||||||
@@ -44,35 +55,20 @@ impl OverlayBackend for KeyboardBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InteractionHandler for KeyboardBackend {
|
impl InteractionHandler for KeyboardBackend {
|
||||||
fn on_pointer(
|
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||||
&mut self,
|
|
||||||
app: &mut AppState,
|
|
||||||
hit: &crate::backend::input::PointerHit,
|
|
||||||
pressed: bool,
|
|
||||||
) {
|
|
||||||
self.panel.on_pointer(app, hit, pressed);
|
self.panel.on_pointer(app, hit, pressed);
|
||||||
let _ = self
|
let _ = self
|
||||||
.panel
|
.panel
|
||||||
.layout
|
.layout
|
||||||
.push_event(&wgui::event::Event::InternalStateChange);
|
.push_event(&wgui::event::Event::InternalStateChange);
|
||||||
}
|
}
|
||||||
fn on_scroll(
|
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) {
|
||||||
&mut self,
|
|
||||||
app: &mut AppState,
|
|
||||||
hit: &crate::backend::input::PointerHit,
|
|
||||||
delta_y: f32,
|
|
||||||
delta_x: f32,
|
|
||||||
) {
|
|
||||||
self.panel.on_scroll(app, hit, delta_y, delta_x);
|
self.panel.on_scroll(app, hit, delta_y, delta_x);
|
||||||
}
|
}
|
||||||
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
|
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
|
||||||
self.panel.on_left(app, pointer);
|
self.panel.on_left(app, pointer);
|
||||||
}
|
}
|
||||||
fn on_hover(
|
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
||||||
&mut self,
|
|
||||||
app: &mut AppState,
|
|
||||||
hit: &crate::backend::input::PointerHit,
|
|
||||||
) -> Option<crate::backend::input::Haptics> {
|
|
||||||
self.panel.on_hover(app, hit)
|
self.panel.on_hover(app, hit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,20 +94,26 @@ impl OverlayRenderer for KeyboardBackend {
|
|||||||
}
|
}
|
||||||
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||||
self.state.borrow_mut().modifiers = 0;
|
self.state.borrow_mut().modifiers = 0;
|
||||||
app.hid_provider.borrow_mut().set_modifiers_routed(0);
|
app.hid_provider.set_modifiers_routed(0);
|
||||||
self.panel.pause(app)
|
self.panel.pause(app)
|
||||||
}
|
}
|
||||||
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||||
self.panel.resume(app)?;
|
self.panel.resume(app)?;
|
||||||
self.panel
|
self.panel
|
||||||
.layout
|
.layout
|
||||||
.push_event(&wgui::event::Event::InternalStateChange)
|
.push_event(&wgui::event::Event::InternalStateChange)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct InvokeAction {
|
||||||
|
key: Rc<KeyState>,
|
||||||
|
button: MouseButton,
|
||||||
|
pressed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
struct KeyboardState {
|
struct KeyboardState {
|
||||||
hid: Rc<RefCell<HidWrapper>>,
|
invoke_action: Option<InvokeAction>,
|
||||||
audio: Rc<RefCell<AudioOutput>>,
|
|
||||||
modifiers: KeyModifier,
|
modifiers: KeyModifier,
|
||||||
alt_modifier: KeyModifier,
|
alt_modifier: KeyModifier,
|
||||||
processes: Vec<Child>,
|
processes: Vec<Child>,
|
||||||
@@ -119,6 +121,10 @@ struct KeyboardState {
|
|||||||
|
|
||||||
const KEY_AUDIO_WAV: &[u8] = include_bytes!("../../res/421581.wav");
|
const KEY_AUDIO_WAV: &[u8] = include_bytes!("../../res/421581.wav");
|
||||||
|
|
||||||
|
fn play_key_click(app: &mut AppState) {
|
||||||
|
app.audio_provider.play(KEY_AUDIO_WAV);
|
||||||
|
}
|
||||||
|
|
||||||
struct KeyState {
|
struct KeyState {
|
||||||
button_state: KeyButtonData,
|
button_state: KeyButtonData,
|
||||||
color: drawing::Color,
|
color: drawing::Color,
|
||||||
@@ -127,6 +133,7 @@ struct KeyState {
|
|||||||
drawn_state: Cell<bool>,
|
drawn_state: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum KeyButtonData {
|
enum KeyButtonData {
|
||||||
Key {
|
Key {
|
||||||
vk: VirtualKey,
|
vk: VirtualKey,
|
||||||
@@ -147,15 +154,12 @@ enum KeyButtonData {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_key_click(keyboard: &KeyboardState) {
|
fn handle_press(
|
||||||
keyboard
|
app: &mut AppState,
|
||||||
.audio
|
key: &KeyState,
|
||||||
.borrow_mut()
|
keyboard: &mut KeyboardState,
|
||||||
.play(AudioRole::Keyboard, KEY_AUDIO_WAV);
|
button: MouseButton,
|
||||||
}
|
) {
|
||||||
|
|
||||||
fn handle_press(key: Rc<KeyState>, keyboard: Rc<RefCell<KeyboardState>>, button: MouseButton) {
|
|
||||||
let mut keyboard = keyboard.borrow_mut();
|
|
||||||
match &key.button_state {
|
match &key.button_state {
|
||||||
KeyButtonData::Key { vk, pressed } => {
|
KeyButtonData::Key { vk, pressed } => {
|
||||||
keyboard.modifiers |= match button {
|
keyboard.modifiers |= match button {
|
||||||
@@ -164,29 +168,22 @@ fn handle_press(key: Rc<KeyState>, keyboard: Rc<RefCell<KeyboardState>>, button:
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
app.hid_provider.set_modifiers_routed(keyboard.modifiers);
|
||||||
let mut hid = keyboard.hid.borrow_mut();
|
app.hid_provider.send_key_routed(*vk, true);
|
||||||
hid.set_modifiers_routed(keyboard.modifiers);
|
|
||||||
hid.send_key_routed(*vk, true);
|
|
||||||
}
|
|
||||||
pressed.set(true);
|
pressed.set(true);
|
||||||
play_key_click(&keyboard);
|
play_key_click(app);
|
||||||
}
|
}
|
||||||
KeyButtonData::Modifier { modifier, sticky } => {
|
KeyButtonData::Modifier { modifier, sticky } => {
|
||||||
sticky.set(keyboard.modifiers & *modifier == 0);
|
sticky.set(keyboard.modifiers & *modifier == 0);
|
||||||
keyboard.modifiers |= *modifier;
|
keyboard.modifiers |= *modifier;
|
||||||
keyboard
|
app.hid_provider.set_modifiers_routed(keyboard.modifiers);
|
||||||
.hid
|
play_key_click(app);
|
||||||
.borrow_mut()
|
|
||||||
.set_modifiers_routed(keyboard.modifiers);
|
|
||||||
play_key_click(&keyboard);
|
|
||||||
}
|
}
|
||||||
KeyButtonData::Macro { verbs } => {
|
KeyButtonData::Macro { verbs } => {
|
||||||
let hid = keyboard.hid.borrow_mut();
|
|
||||||
for (vk, press) in verbs {
|
for (vk, press) in verbs {
|
||||||
hid.send_key_routed(*vk, *press);
|
app.hid_provider.send_key_routed(*vk, *press);
|
||||||
}
|
}
|
||||||
play_key_click(&keyboard);
|
play_key_click(app);
|
||||||
}
|
}
|
||||||
KeyButtonData::Exec { program, args, .. } => {
|
KeyButtonData::Exec { program, args, .. } => {
|
||||||
// Reap previous processes
|
// Reap previous processes
|
||||||
@@ -197,17 +194,12 @@ fn handle_press(key: Rc<KeyState>, keyboard: Rc<RefCell<KeyboardState>>, button:
|
|||||||
if let Ok(child) = Command::new(program).args(args).spawn() {
|
if let Ok(child) = Command::new(program).args(args).spawn() {
|
||||||
keyboard.processes.push(child);
|
keyboard.processes.push(child);
|
||||||
}
|
}
|
||||||
play_key_click(&keyboard);
|
play_key_click(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_release(
|
fn handle_release(app: &mut AppState, key: &KeyState, keyboard: &mut KeyboardState) {
|
||||||
key: Rc<KeyState>,
|
|
||||||
keyboard: Rc<RefCell<KeyboardState>>,
|
|
||||||
_button: MouseButton,
|
|
||||||
) -> bool {
|
|
||||||
let mut keyboard = keyboard.borrow_mut();
|
|
||||||
match &key.button_state {
|
match &key.button_state {
|
||||||
KeyButtonData::Key { vk, pressed } => {
|
KeyButtonData::Key { vk, pressed } => {
|
||||||
pressed.set(false);
|
pressed.set(false);
|
||||||
@@ -217,21 +209,14 @@ fn handle_release(
|
|||||||
keyboard.modifiers &= !*m;
|
keyboard.modifiers &= !*m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut hid = keyboard.hid.borrow_mut();
|
app.hid_provider.send_key_routed(*vk, false);
|
||||||
hid.send_key_routed(*vk, false);
|
app.hid_provider.set_modifiers_routed(keyboard.modifiers);
|
||||||
hid.set_modifiers_routed(keyboard.modifiers);
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
KeyButtonData::Modifier { modifier, sticky } => {
|
KeyButtonData::Modifier { modifier, sticky } => {
|
||||||
if !sticky.get() {
|
if !sticky.get() {
|
||||||
keyboard.modifiers &= !*modifier;
|
keyboard.modifiers &= !*modifier;
|
||||||
keyboard
|
app.hid_provider.set_modifiers_routed(keyboard.modifiers);
|
||||||
.hid
|
|
||||||
.borrow_mut()
|
|
||||||
.set_modifiers_routed(keyboard.modifiers);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
KeyButtonData::Exec {
|
KeyButtonData::Exec {
|
||||||
release_program,
|
release_program,
|
||||||
@@ -248,8 +233,7 @@ fn handle_release(
|
|||||||
keyboard.processes.push(child);
|
keyboard.processes.push(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
_ => true,
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ impl InteractionHandler for ScreenInteractionHandler {
|
|||||||
|| app.input_state.pointers[hit.pointer].now.move_mouse)
|
|| app.input_state.pointers[hit.pointer].now.move_mouse)
|
||||||
{
|
{
|
||||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||||
app.hid_provider.borrow_mut().inner.mouse_move(pos);
|
app.hid_provider.inner.mouse_move(pos);
|
||||||
set_next_move(u64::from(app.session.config.mouse_move_interval_ms));
|
set_next_move(u64::from(app.session.config.mouse_move_interval_ms));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -147,19 +147,16 @@ impl InteractionHandler for ScreenInteractionHandler {
|
|||||||
set_next_move(u64::from(app.session.config.click_freeze_time_ms));
|
set_next_move(u64::from(app.session.config.click_freeze_time_ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
app.hid_provider.inner.send_button(btn, pressed);
|
||||||
|
|
||||||
hid_provider.inner.send_button(btn, pressed);
|
|
||||||
|
|
||||||
if !pressed {
|
if !pressed {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||||
hid_provider.inner.mouse_move(pos);
|
app.hid_provider.inner.mouse_move(pos);
|
||||||
}
|
}
|
||||||
fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta_y: f32, delta_x: f32) {
|
fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta_y: f32, delta_x: f32) {
|
||||||
app.hid_provider
|
app.hid_provider
|
||||||
.borrow_mut()
|
|
||||||
.inner
|
.inner
|
||||||
.wheel((delta_y * 64.) as i32, (delta_x * 64.) as i32);
|
.wheel((delta_y * 64.) as i32, (delta_x * 64.) as i32);
|
||||||
}
|
}
|
||||||
@@ -892,11 +889,10 @@ pub fn create_screens_wayland(wl: &mut WlxClientAlias, app: &mut AppState) -> Sc
|
|||||||
let extent = wl.get_desktop_extent();
|
let extent = wl.get_desktop_extent();
|
||||||
let origin = wl.get_desktop_origin();
|
let origin = wl.get_desktop_origin();
|
||||||
|
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
app.hid_provider
|
||||||
hid_provider
|
|
||||||
.inner
|
.inner
|
||||||
.set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32));
|
.set_desktop_extent(vec2(extent.0 as f32, extent.1 as f32));
|
||||||
hid_provider
|
app.hid_provider
|
||||||
.inner
|
.inner
|
||||||
.set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32));
|
.set_desktop_origin(vec2(origin.0 as f32, origin.1 as f32));
|
||||||
|
|
||||||
@@ -995,9 +991,8 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
app.hid_provider.inner.set_desktop_extent(extent);
|
||||||
hid_provider.inner.set_desktop_extent(extent);
|
app.hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
|
||||||
hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
|
|
||||||
|
|
||||||
Ok(ScreenCreateData { screens })
|
Ok(ScreenCreateData { screens })
|
||||||
}
|
}
|
||||||
@@ -1054,9 +1049,8 @@ pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateDat
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut hid_provider = app.hid_provider.borrow_mut();
|
app.hid_provider.inner.set_desktop_extent(extent);
|
||||||
hid_provider.inner.set_desktop_extent(extent);
|
app.hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
|
||||||
hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
|
|
||||||
|
|
||||||
Ok(ScreenCreateData { screens })
|
Ok(ScreenCreateData { screens })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
gui::panel::GuiPanel,
|
gui::panel::GuiPanel,
|
||||||
state::{AppState, LeftRight},
|
state::{AppState, LeftRight},
|
||||||
subsystem::audio::AudioRole,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FONT_SIZE: isize = 16;
|
const FONT_SIZE: isize = 16;
|
||||||
@@ -94,10 +93,8 @@ impl Toast {
|
|||||||
|
|
||||||
let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout));
|
let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout));
|
||||||
|
|
||||||
if self.sound {
|
if self.sound && app.session.config.notifications_sound_enabled {
|
||||||
app.audio_provider
|
app.audio_provider.play(app.toast_sound);
|
||||||
.borrow_mut()
|
|
||||||
.play(AudioRole::Notification, app.toast_sound);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop any toast that was created before us.
|
// drop any toast that was created before us.
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ pub struct AppState {
|
|||||||
|
|
||||||
pub gfx: Arc<WGfx>,
|
pub gfx: Arc<WGfx>,
|
||||||
pub gfx_extras: WGfxExtras,
|
pub gfx_extras: WGfxExtras,
|
||||||
pub hid_provider: Rc<RefCell<HidWrapper>>,
|
pub hid_provider: HidWrapper,
|
||||||
pub audio_provider: Rc<RefCell<AudioOutput>>,
|
pub audio_provider: AudioOutput,
|
||||||
|
|
||||||
pub input_state: InputState,
|
pub input_state: InputState,
|
||||||
pub screens: SmallVec<[ScreenMeta; 8]>,
|
pub screens: SmallVec<[ScreenMeta; 8]>,
|
||||||
@@ -71,15 +71,13 @@ impl AppState {
|
|||||||
include_bytes!("res/557297.wav"),
|
include_bytes!("res/557297.wav"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let audio_provider = AudioOutput::new(&session.config);
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
session,
|
session,
|
||||||
tasks,
|
tasks,
|
||||||
gfx,
|
gfx,
|
||||||
gfx_extras,
|
gfx_extras,
|
||||||
hid_provider: Rc::new(RefCell::new(HidWrapper::new())),
|
hid_provider: HidWrapper::new(),
|
||||||
audio_provider: Rc::new(RefCell::new(audio_provider)),
|
audio_provider: AudioOutput::new(),
|
||||||
input_state: InputState::new(),
|
input_state: InputState::new(),
|
||||||
screens: smallvec![],
|
screens: smallvec![],
|
||||||
anchor: Affine3A::IDENTITY,
|
anchor: Affine3A::IDENTITY,
|
||||||
|
|||||||
@@ -1,31 +1,15 @@
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use rodio::{Decoder, OutputStream, OutputStreamHandle, Source};
|
use rodio::{Decoder, OutputStream, OutputStreamHandle, Source};
|
||||||
use strum::EnumCount;
|
|
||||||
|
|
||||||
use crate::config::GeneralConfig;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, EnumCount)]
|
|
||||||
#[repr(usize)]
|
|
||||||
pub enum AudioRole {
|
|
||||||
Notification,
|
|
||||||
Keyboard,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AudioOutput {
|
pub struct AudioOutput {
|
||||||
muted_roles: [bool; AudioRole::COUNT],
|
|
||||||
audio_stream: Option<(OutputStream, OutputStreamHandle)>,
|
audio_stream: Option<(OutputStream, OutputStreamHandle)>,
|
||||||
first_try: bool,
|
first_try: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioOutput {
|
impl AudioOutput {
|
||||||
pub const fn new(config: &GeneralConfig) -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
muted_roles: [
|
|
||||||
//TODO: improve this
|
|
||||||
!config.keyboard_sound_enabled,
|
|
||||||
!config.notifications_sound_enabled,
|
|
||||||
],
|
|
||||||
audio_stream: None,
|
audio_stream: None,
|
||||||
first_try: true,
|
first_try: true,
|
||||||
}
|
}
|
||||||
@@ -44,10 +28,7 @@ impl AudioOutput {
|
|||||||
self.audio_stream.as_ref().map(|(_, h)| h)
|
self.audio_stream.as_ref().map(|(_, h)| h)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn play(&mut self, role: AudioRole, wav_bytes: &'static [u8]) {
|
pub fn play(&mut self, wav_bytes: &'static [u8]) {
|
||||||
if self.muted_roles[role as usize] {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Some(handle) = self.get_handle() else {
|
let Some(handle) = self.get_handle() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user