diff --git a/Cargo.lock b/Cargo.lock index 02e62d4..7429747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1130,6 +1130,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1965,6 +1976,15 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.4" @@ -4321,6 +4341,7 @@ dependencies = [ "chrono-tz", "cstr", "ctrlc", + "dbus", "flexi_logger", "fontconfig-rs", "freetype-rs", diff --git a/Cargo.toml b/Cargo.toml index 5676118..e9279db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ chrono = "0.4.29" chrono-tz = "0.8.5" cstr = "0.2.11" ctrlc = { version = "3.4.2", features = ["termination"] } +dbus = { version = "0.9.7", optional = true } flexi_logger = "0.27.4" fontconfig-rs = "0.1.1" freetype-rs = "0.32.0" @@ -49,10 +50,11 @@ wlx-capture = { git = "https://github.com/galister/wlx-capture" } xdg = "2.5.2" [features] +default = ["openvr", "openxr", "osc", "x11", "wayland", "notifications"] +notifications = ["dep:dbus"] openvr = ["dep:ovr_overlay", "dep:json"] openxr = ["dep:openxr"] osc = ["dep:rosc"] x11 = ["wlx-capture/xshm"] wayland = ["wlx-capture/pipewire", "wlx-capture/wlr"] -default = ["openvr", "openxr", "osc", "x11", "wayland"] diff --git a/src/backend/common.rs b/src/backend/common.rs index ff54791..41d9714 100644 --- a/src/backend/common.rs +++ b/src/backend/common.rs @@ -14,7 +14,6 @@ use thiserror::Error; use crate::{ overlays::{ keyboard::create_keyboard, - toast::Toast, watch::{create_watch, WATCH_NAME, WATCH_SCALE}, }, state::AppState, @@ -58,6 +57,7 @@ where }; let mut watch = create_watch::(app, &screens)?; + log::info!("Watch Rotation: {:?}", watch.state.spawn_rotation); watch.state.want_visible = true; overlays.insert(watch.state.id, watch); @@ -68,9 +68,9 @@ where let mut show_screens = app.session.config.show_screens.clone(); if show_screens.is_empty() { - screens.first().map(|s| { + if let Some(s) = screens.first() { show_screens.push(s.state.name.clone()); - }); + } } for mut screen in screens { @@ -173,7 +173,7 @@ impl PartialEq for AppTask { } impl PartialOrd for AppTask { fn partial_cmp(&self, other: &Self) -> Option { - Some(self.not_before.cmp(&other.not_before).reverse()) + Some(self.cmp(other)) } } impl Eq for AppTask {} @@ -194,7 +194,6 @@ pub enum TaskType { Box Option<(OverlayState, Box)> + Send>, ), DropOverlay(OverlaySelector), - Toast(Toast), } pub struct TaskContainer { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 90531f4..f1c4a81 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,9 +1,16 @@ pub mod common; pub mod input; + +#[cfg(feature = "notifications")] +pub mod notifications; + #[cfg(feature = "openvr")] pub mod openvr; + #[cfg(feature = "openxr")] pub mod openxr; + #[cfg(feature = "osc")] pub mod osc; + pub mod overlay; diff --git a/src/backend/notifications.rs b/src/backend/notifications.rs new file mode 100644 index 0000000..8c55049 --- /dev/null +++ b/src/backend/notifications.rs @@ -0,0 +1,153 @@ +use dbus::{ + blocking::Connection, + channel::{MatchingReceiver, Token}, + message::MatchRule, +}; +use serde::Deserialize; +use std::sync::{mpsc, Arc}; + +use crate::{overlays::toast::Toast, state::AppState}; + +pub struct NotificationManager { + rx_toast: mpsc::Receiver, + tx_toast: mpsc::SyncSender, + dbus_data: Option<(Connection, Token)>, +} + +impl NotificationManager { + pub fn new() -> Self { + let (tx_toast, rx_toast) = mpsc::sync_channel(10); + Self { + rx_toast, + tx_toast, + dbus_data: None, + } + } + + pub fn submit_pending(&self, app: &mut AppState) { + self.rx_toast.try_iter().for_each(|toast| { + toast.submit(app); + }); + } + + pub fn run_dbus(&mut self) { + let Ok(c) = Connection::new_session() else { + log::error!("Failed to connect to dbus. Desktop notifications will not work."); + return; + }; + + let mut rule = MatchRule::new_method_call(); + rule.member = Some("Notify".into()); + rule.interface = Some("org.freedesktop.Notifications".into()); + rule.path = Some("/org/freedesktop/Notifications".into()); + rule.eavesdrop = true; + + let sender = self.tx_toast.clone(); + + let token = c.start_receive( + rule, + Box::new(move |msg, _| { + if let Ok(toast) = parse_dbus(&msg) { + match sender.try_send(toast) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to send notification: {:?}", e); + } + } + } + true + }), + ); + + self.dbus_data = Some((c, token)); + } + + pub fn run_udp(&mut self) { + let sender = self.tx_toast.clone(); + // NOTE: We're detaching the thread, as there's no simple way to gracefully stop it other than app shutdown. + let _ = std::thread::spawn(move || { + let socket = match std::net::UdpSocket::bind("127.0.0.1:42069") { + Ok(s) => s, + Err(e) => { + log::error!("Failed to bind notification socket: {:?}", e); + return; + } + }; + let mut buf = [0u8; 1500]; + + loop { + if let Ok((num_bytes, _)) = socket.recv_from(&mut buf) { + let json_str = match std::str::from_utf8(&buf[..num_bytes]) { + Ok(s) => s, + Err(e) => { + log::error!("Failed to receive notification message: {:?}", e); + continue; + } + }; + let msg = match serde_json::from_str::(json_str) { + Ok(m) => m, + Err(e) => { + log::error!("Failed to parse notification message: {:?}", e); + continue; + } + }; + + let toast = Toast::new(msg.title, msg.content.unwrap_or_else(|| "".into())) + .with_timeout(msg.timeout) + .with_volume(msg.volume) + .with_opacity(msg.opacity); + + match sender.try_send(toast) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to send notification: {:?}", e); + } + } + } + } + }); + } +} + +impl Drop for NotificationManager { + fn drop(&mut self) { + if let Some((c, token)) = self.dbus_data.take() { + let _ = c.stop_receive(token); + } + } +} + +fn parse_dbus(msg: &dbus::Message) -> anyhow::Result { + let mut args = msg.iter_init(); + let app_name: String = args.read()?; + let _replaces_id: u32 = args.read()?; + let _app_icon: String = args.read()?; + let summary: String = args.read()?; + let body: String = args.read()?; + + let title = if summary.is_empty() { + app_name + } else { + summary + }; + + Ok(Toast::new(title.into(), body.into())) +} + +#[allow(non_snake_case)] +#[derive(Debug, Deserialize)] +struct XsoMessage { + messageType: i32, + index: i32, + volume: f32, + audioPath: Arc, + timeout: f32, + title: Arc, + content: Option>, + icon: Option>, + height: f32, + opacity: f32, + useBase64Icon: bool, + sourceApp: Option>, + alwaysShow: bool, +} diff --git a/src/backend/openvr/input.rs b/src/backend/openvr/input.rs index 07f33be..6ae7592 100644 --- a/src/backend/openvr/input.rs +++ b/src/backend/openvr/input.rs @@ -129,12 +129,12 @@ impl OpenVrInputSource { app: &mut AppState, ) { let aas = ActiveActionSet(ovr_overlay::sys::VRActiveActionSet_t { - ulActionSet: self.set_hnd.0, - ulRestrictedToDevice: 0, - ulSecondaryActionSet: 0, - unPadding: 0, - nPriority: 0, - }); + ulActionSet: self.set_hnd.0, + ulRestrictedToDevice: 0, + ulSecondaryActionSet: 0, + unPadding: 0, + nPriority: 0, + }); let _ = input.update_actions(&mut [aas]); @@ -156,7 +156,6 @@ impl OpenVrInputSource { .map(|pose| { app_hand.pose = pose.0.pose.mDeviceToAbsoluteTracking.to_affine(); hand.has_pose = true; - }); app_hand.now.click = input @@ -235,10 +234,9 @@ impl OpenVrInputSource { _ => continue, }; - get_tracked_device(system, device, role).map(|device| { + if let Some(device) = get_tracked_device(system, device, role) { app.input_state.devices.push(device); - - }); + } } app.input_state.devices.sort_by(|a, b| { diff --git a/src/backend/openvr/manifest.rs b/src/backend/openvr/manifest.rs index 148d198..a5e7055 100644 --- a/src/backend/openvr/manifest.rs +++ b/src/backend/openvr/manifest.rs @@ -18,7 +18,7 @@ pub(super) fn install_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::Res if let Ok(true) = app_mgr.is_application_installed(APP_KEY) { if let Ok(mut file) = File::open(&manifest_path) { let mut buf = String::new(); - if let Ok(_) = file.read_to_string(&mut buf) { + if file.read_to_string(&mut buf).is_ok() { let manifest: json::JsonValue = json::parse(&buf)?; if manifest["applications"][0]["binary_path_linux"] == executable_path { log::info!("Manifest already up to date"); @@ -50,16 +50,20 @@ pub(super) fn install_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::Res bail!("Failed to create manifest file at {:?}", manifest_path); }; - let Ok(()) = manifest.write(&mut file) else { - bail!("Failed to write manifest file at {:?}", manifest_path); + if let Err(e) = manifest.write(&mut file) { + bail!( + "Failed to write manifest file at {:?}: {:?}", + manifest_path, + e + ); }; - let Ok(()) = app_mgr.add_application_manifest(&manifest_path, false) else { - bail!("Failed to add manifest to OpenVR"); + if let Err(e) = app_mgr.add_application_manifest(&manifest_path, false) { + bail!("Failed to add manifest to OpenVR: {}", e.description()); }; - let Ok(()) = app_mgr.set_application_auto_launch(APP_KEY, true) else { - bail!("Failed to set auto launch"); + if let Err(e) = app_mgr.set_application_auto_launch(APP_KEY, true) { + bail!("Failed to set auto launch: {}", e.description()); }; Ok(()) @@ -69,8 +73,8 @@ pub(super) fn uninstall_manifest(app_mgr: &mut ApplicationsManager) -> anyhow::R let manifest_path = CONFIG_ROOT_PATH.join("wlx-overlay-s.vrmanifest"); if let Ok(true) = app_mgr.is_application_installed(APP_KEY) { - let Ok(()) = app_mgr.remove_application_manifest(&manifest_path) else { - bail!("Failed to remove manifest from OpenVR"); + if let Err(e) = app_mgr.remove_application_manifest(&manifest_path) { + bail!("Failed to remove manifest from OpenVR: {}", e.description()); }; log::info!("Uninstalled manifest"); } diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index be33882..2f94e8f 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -21,6 +21,7 @@ use vulkano::{ use crate::{ backend::{ input::interact, + notifications::NotificationManager, openvr::{ input::{set_action_manifest, OpenVrInputSource}, lines::LinePool, @@ -91,6 +92,9 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { let _ = install_manifest(&mut app_mgr); let mut overlays = OverlayContainer::::new(&mut state)?; + let mut notifications = NotificationManager::new(); + notifications.run_dbus(); + notifications.run_udp(); let mut space_mover = playspace::PlayspaceMover::new(); #[cfg(feature = "osc")] @@ -149,6 +153,8 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { next_device_update = Instant::now() + Duration::from_secs(30); } + notifications.submit_pending(&mut state); + state.tasks.retrieve_due(&mut due_tasks); while let Some(task) = due_tasks.pop_front() { match task { @@ -167,6 +173,8 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { continue; }; + log::info!("Creating overlay: {}", state.name); + overlays.add(OverlayData { state, backend, @@ -175,14 +183,11 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { } TaskType::DropOverlay(sel) => { if let Some(o) = overlays.mut_by_selector(&sel) { + log::info!("Dropping overlay: {}", o.state.name); o.destroy(&mut overlay_mngr); overlays.drop_by_selector(&sel); } } - TaskType::Toast(t) => { - // TODO toasts - log::info!("Toast: {} {}", t.title, t.body); - } } } @@ -249,8 +254,6 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { // close font handles? - // playspace moved end frame - state.hid_provider.on_new_frame(); let mut seconds_since_vsync = 0f32; diff --git a/src/backend/openxr/helpers.rs b/src/backend/openxr/helpers.rs index bbb76f9..ff2e459 100644 --- a/src/backend/openxr/helpers.rs +++ b/src/backend/openxr/helpers.rs @@ -111,7 +111,7 @@ pub(super) unsafe fn create_overlay_session( } } -pub(super) fn hmd_pose_from_views(views: &Vec) -> Affine3A { +pub(super) fn hmd_pose_from_views(views: &[xr::View]) -> Affine3A { let pos = { let pos0: Vec3 = unsafe { std::mem::transmute(views[0].pose.position) }; let pos1: Vec3 = unsafe { std::mem::transmute(views[1].pose.position) }; diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index 43c02cf..a394e29 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -15,6 +15,7 @@ use crate::{ backend::{ common::{OverlayContainer, TaskType}, input::interact, + notifications::NotificationManager, openxr::{input::DoubleClickCounter, lines::LinePool, overlay::OpenXrOverlayData}, overlay::OverlayData, }, @@ -63,6 +64,10 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { let mut overlays = OverlayContainer::::new(&mut app_state)?; let mut lines = LinePool::new(app_state.graphics.clone())?; + let mut notifications = NotificationManager::new(); + notifications.run_dbus(); + notifications.run_udp(); + #[cfg(feature = "osc")] let mut osc_sender = crate::backend::osc::OscSender::new(app_state.session.config.osc_out_port).ok(); @@ -179,6 +184,8 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { continue 'main_loop; } + notifications.submit_pending(&mut app_state); + app_state.tasks.retrieve_due(&mut due_tasks); while let Some(task) = due_tasks.pop_front() { match task { @@ -206,10 +213,6 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { TaskType::DropOverlay(sel) => { overlays.drop_by_selector(&sel); } - TaskType::Toast(t) => { - // TODO toasts - log::info!("Toast: {} {}", t.title, t.body); - } } } @@ -221,7 +224,9 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { .input_state .pointers .iter() - .any(|p| p.now.show_hide && !p.before.show_hide) && show_hide_counter.click() { + .any(|p| p.now.show_hide && !p.before.show_hide) + && show_hide_counter.click() + { overlays.show_hide(&mut app_state); } diff --git a/src/backend/openxr/overlay.rs b/src/backend/openxr/overlay.rs index 9f207c4..3c4a605 100644 --- a/src/backend/openxr/overlay.rs +++ b/src/backend/openxr/overlay.rs @@ -36,20 +36,23 @@ impl OverlayData { }; let extent = my_view.image().extent(); - let data = self.data.swapchain.get_or_insert_with(|| { - let srd = - create_swapchain_render_data(xr, command_buffer.graphics.clone(), extent).unwrap(); //TODO - - log::info!( - "{}: Created swapchain {}x{}, {} images, {} MB", - self.state.name, - extent[0], - extent[1], - srd.images.len(), - extent[0] * extent[1] * 4 * srd.images.len() as u32 / 1024 / 1024 - ); - srd - }); + let data = match self.data.swapchain { + Some(ref mut data) => data, + None => { + let srd = + create_swapchain_render_data(xr, command_buffer.graphics.clone(), extent)?; + log::debug!( + "{}: Created swapchain {}x{}, {} images, {} MB", + self.state.name, + extent[0], + extent[1], + srd.images.len(), + extent[0] * extent[1] * 4 * srd.images.len() as u32 / 1024 / 1024 + ); + self.data.swapchain = Some(srd); + self.data.swapchain.as_mut().unwrap() //safe + } + }; let sub_image = data.acquire_present_release(command_buffer, my_view, self.state.alpha)?; let posef = helpers::transform_to_posef(&self.state.transform); diff --git a/src/backend/overlay.rs b/src/backend/overlay.rs index d431045..7e8c6bb 100644 --- a/src/backend/overlay.rs +++ b/src/backend/overlay.rs @@ -101,9 +101,7 @@ impl OverlayState { self.transform = parent * Affine3A::from_scale_rotation_translation( Vec3::ONE * self.spawn_scale, - self.spawn_rotation - * Quat::from_rotation_x(f32::to_radians(-180.0)) - * Quat::from_rotation_z(f32::to_radians(180.0)), + self.spawn_rotation, point.into(), ); diff --git a/src/graphics.rs b/src/graphics.rs index 654c5a6..01dee16 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -837,7 +837,7 @@ impl WlxCommandBuffer { ) -> anyhow::Result<()> { self.command_buffer.begin_render_pass( RenderPassBeginInfo { - clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())], + clear_values: vec![Some([0.0, 0.0, 0.0, 0.0].into())], ..RenderPassBeginInfo::framebuffer(pipeline.data.framebuffer.clone()) }, SubpassBeginInfo { @@ -854,7 +854,7 @@ impl WlxCommandBuffer { color_attachments: vec![Some(RenderingAttachmentInfo { load_op: AttachmentLoadOp::Clear, store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + clear_value: Some([0.0, 0.0, 0.0, 0.0].into()), ..RenderingAttachmentInfo::image_view(render_target.clone()) })], ..Default::default() @@ -923,8 +923,7 @@ impl WlxCommandBuffer { let info = reader.info(); let width = info.width; let height = info.height; - let mut image_data = Vec::new(); - image_data.resize((info.width * info.height * 4) as usize, 0); + let mut image_data = vec![0; (info.width * info.height * 4) as usize]; reader.next_frame(&mut image_data)?; self.texture2d(width, height, Format::R8G8B8A8_UNORM, &image_data) } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 806dbc3..329559f 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -147,7 +147,7 @@ impl CanvasBuilder { y: f32, w: f32, h: f32, - label: &Vec, + label: &[String], ) -> &mut Control { let idx = self.canvas.controls.len(); self.canvas.interactive_set_idx(x, y, w, h, idx); diff --git a/src/overlays/toast.rs b/src/overlays/toast.rs index 1067258..22784e3 100644 --- a/src/overlays/toast.rs +++ b/src/overlays/toast.rs @@ -1,11 +1,31 @@ -use std::sync::Arc; +use std::{ + ops::Add, + sync::{atomic::AtomicUsize, Arc}, +}; + +use glam::vec3a; + +use crate::{ + backend::{ + common::{OverlaySelector, TaskType}, + overlay::{OverlayBackend, OverlayState, RelativeTo}, + }, + gui::{color_parse, CanvasBuilder}, + state::AppState, +}; + +const FONT_SIZE: isize = 16; +const PADDING: (f32, f32) = (25., 7.); +const PIXELS_TO_METERS: f32 = 1. / 2000.; + +static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0); pub struct Toast { pub title: Arc, pub body: Arc, pub opacity: f32, + pub volume: f32, pub timeout: f32, - pub sound: bool, } #[allow(dead_code)] @@ -16,7 +36,7 @@ impl Toast { body, opacity: 1.0, timeout: 3.0, - sound: false, + volume: 0.0, } } pub fn with_timeout(mut self, timeout: f32) -> Self { @@ -27,8 +47,94 @@ impl Toast { self.opacity = opacity; self } - pub fn with_sound(mut self) -> Self { - self.sound = true; + pub fn with_volume(mut self, volume: f32) -> Self { + self.volume = volume; self } + pub fn submit(self, app: &mut AppState) { + let auto_increment = AUTO_INCREMENT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let name: Arc = format!("toast-{}", auto_increment).into(); + let selector = OverlaySelector::Name(name.clone()); + + let destroy_at = + std::time::Instant::now().add(std::time::Duration::from_secs_f32(self.timeout)); + + app.tasks.enqueue(TaskType::CreateOverlay( + selector.clone(), + Box::new(move |app| new_toast(self, name, app)), + )); + + app.tasks + .enqueue_at(TaskType::DropOverlay(selector), destroy_at); + } +} + +fn new_toast( + toast: Toast, + name: Arc, + app: &mut AppState, +) -> Option<(OverlayState, Box)> { + let title = if toast.title.len() > 0 { + toast.title + } else { + "Notification".into() + }; + + let mut size = if toast.body.len() > 0 { + let (w0, _) = app + .fc + .get_text_size(&title, FONT_SIZE, app.graphics.clone()) + .ok()?; + let (w1, h1) = app + .fc + .get_text_size(&toast.body, FONT_SIZE, app.graphics.clone()) + .ok()?; + (w0.max(w1), h1 + 50.) + } else { + app.fc + .get_text_size(&title, FONT_SIZE, app.graphics.clone()) + .ok()? + }; + + let og_width = size.0; + size.0 += PADDING.0 * 2.; + + let mut canvas = CanvasBuilder::<(), ()>::new( + size.0 as _, + size.1 as _, + app.graphics.clone(), + app.graphics.native_format, + (), + ) + .ok()?; + + canvas.font_size = FONT_SIZE; + canvas.fg_color = color_parse("#aaaaaa").unwrap(); // want panic + canvas.bg_color = color_parse("#333333").unwrap(); // want panic + canvas.panel(0., 0., size.0, size.1); + + if toast.body.len() > 0 { + log::info!("{}: {}", title, toast.body); + canvas.label(PADDING.0, 54., og_width, size.1 - 54., toast.body); + + canvas.fg_color = color_parse("#101010").unwrap(); // want panic + canvas.bg_color = color_parse("#666666").unwrap(); // want panic + canvas.panel(0., 0., size.0, 30.); + canvas.label_centered(PADDING.0, 16., og_width, FONT_SIZE as f32 + 2., title); + } else { + log::info!("Toast: {}", title); + canvas.label(0., 0., size.0, size.1, title); + } + + let state = OverlayState { + name, + want_visible: true, + spawn_scale: size.0 * PIXELS_TO_METERS, + spawn_point: vec3a(0., -0.2, -0.5), + relative_to: RelativeTo::Head, + ..Default::default() + }; + let backend = Box::new(canvas.build()); + + Some((state, backend)) } diff --git a/src/overlays/watch.rs b/src/overlays/watch.rs index eefa9e2..c959a70 100644 --- a/src/overlays/watch.rs +++ b/src/overlays/watch.rs @@ -284,7 +284,7 @@ where canvas.font_size = font_size; let button = canvas.button(x, y, w, h, text.clone()); button.state = Some(ElemState::Mirror { name, show_hide }); - button.on_press = Some(btn_mirror_dn::); + button.on_press = Some(btn_mirror_dn); button.on_scroll = Some(overlay_button_scroll); } } @@ -346,14 +346,12 @@ enum ElemState { } #[cfg(feature = "wayland")] -fn btn_mirror_dn( +fn btn_mirror_dn( control: &mut Control<(), ElemState>, _: &mut (), app: &mut AppState, mode: PointerMode, -) where - O: Default, -{ +) { let ElemState::Mirror { name, show_hide } = control.state.as_ref().unwrap() // want panic else { @@ -428,10 +426,12 @@ fn btn_func_dn( Box::new(|app, o| { o.want_visible = false; o.spawn_scale = 0.0; - app.tasks.enqueue(TaskType::Toast(Toast::new( + Toast::new( "Watch hidden".into(), "Use show/hide button to restore.".into(), - ))) + ) + .with_timeout(3.) + .submit(app); }), )); } @@ -441,7 +441,6 @@ fn btn_func_dn( Box::new(|app, o| { if let RelativeTo::Hand(0) = o.relative_to { o.relative_to = RelativeTo::Hand(1); - o.spawn_rotation = app.session.watch_rot; o.spawn_rotation = app.session.watch_rot * Quat::from_rotation_x(PI) * Quat::from_rotation_z(PI); @@ -452,10 +451,9 @@ fn btn_func_dn( o.spawn_rotation = app.session.watch_rot; o.spawn_point = app.session.watch_pos.into(); } - app.tasks.enqueue(TaskType::Toast(Toast::new( - "Watch switched".into(), - "Check your other hand".into(), - ))) + Toast::new("Watch switched".into(), "Check your other hand".into()) + .with_timeout(3.) + .submit(app); }), )); } @@ -571,7 +569,7 @@ fn exec_label_update(control: &mut Control<(), ElemState>, _: &mut (), _: &mut A } else { if let Some(mut stdout) = proc.stdout.take() { let mut buf = String::new(); - if let Ok(_) = stdout.read_to_string(&mut buf) { + if stdout.read_to_string(&mut buf).is_ok() { control.set_text(&buf); } else { log::error!("Failed to read stdout for child process"); @@ -730,10 +728,11 @@ fn overlay_button_up(control: &mut Control<(), ElemState>, _: &mut (), app: &mut o.grabbable = o.recenter; o.show_hide = o.recenter; if !o.recenter { - app.tasks.enqueue(TaskType::Toast(Toast::new( + Toast::new( format!("{} is now locked in place!", o.name).into(), "Right-click again to toggle.".into(), - ))) + ) + .submit(app); } }), )); @@ -744,10 +743,11 @@ fn overlay_button_up(control: &mut Control<(), ElemState>, _: &mut (), app: &mut Box::new(|app, o| { o.interactable = !o.interactable; if !o.interactable { - app.tasks.enqueue(TaskType::Toast(Toast::new( + Toast::new( format!("{} is now non-interactable!", o.name).into(), "Middle-click again to toggle.".into(), - ))) + ) + .submit(app); } }), )); diff --git a/src/shaders/mod.rs b/src/shaders/mod.rs index 2838e1a..dd22b12 100644 --- a/src/shaders/mod.rs +++ b/src/shaders/mod.rs @@ -74,9 +74,7 @@ pub mod frag_sprite { void main() { - vec4 c = texture(in_texture, in_uv); - out_color.rgb = c.rgb; - out_color.a = min((c.r + c.g + c.b)*100.0, 1.0); + out_color = texture(in_texture, in_uv); } ", } diff --git a/src/state.rs b/src/state.rs index 0715819..7d7b154 100644 --- a/src/state.rs +++ b/src/state.rs @@ -15,7 +15,7 @@ use crate::{ }; pub const WATCH_DEFAULT_POS: Vec3 = Vec3::new(-0.03, -0.01, 0.125); -pub const WATCH_DEFAULT_ROT: Quat = Quat::from_xyzw(0.7071066, 0., 0.7071066, 0.0007963); +pub const WATCH_DEFAULT_ROT: Quat = Quat::from_xyzw(-0.7071066, 0.0007963618, 0.7071066, 0.0); pub struct AppState { pub fc: FontCache,