diff --git a/.github/workflows/build-wayland-openxr-openvr-wayvr.yml b/.github/workflows/build-wayland-openxr-openvr-wayvr.yml new file mode 100644 index 0000000..3a9d5c4 --- /dev/null +++ b/.github/workflows/build-wayland-openxr-openvr-wayvr.yml @@ -0,0 +1,28 @@ +name: Check Wayland+OpenXR+OpenVR+WayVR + +on: + pull_request: + #branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.3 + - name: Prepare Environment + run: | + sudo add-apt-repository -syn universe + sudo add-apt-repository -syn ppa:pipewire-debian/pipewire-upstream || sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 25088A0359807596 + sudo apt-get update -y + sudo apt-get install -y fuse cmake pkg-config fontconfig libasound2-dev libxkbcommon-dev libxkbcommon-x11-0 libxkbcommon-x11-dev libopenxr-dev libfontconfig-dev libdbus-1-dev libpipewire-0.3-0 libpipewire-0.3-dev libspa-0.2-dev libx11-6 libxext6 libxrandr2 libx11-dev libxext-dev libxrandr-dev libopenvr-dev libopenvr-api1 + - name: Build + run: cargo build --verbose --no-default-features --features=wayland,openxr,openvr,wayvr + - name: Run tests + run: cargo test --verbose --no-default-features --features=wayland,openxr,openvr,wayvr diff --git a/Cargo.toml b/Cargo.toml index 26c80b8..2d7539a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ wayland-egl = { version = "0.32.4", optional = true } regex = { version = "*" } [features] -default = ["openxr", "openvr", "osc", "x11", "wayland"] +default = ["openxr", "openvr", "osc", "x11", "wayland", "wayvr"] openvr = ["dep:ovr_overlay", "dep:json"] openxr = ["dep:openxr", "dep:libmonado-rs"] osc = ["dep:rosc"] diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index f854480..6a17f4a 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -42,6 +42,9 @@ use crate::{ state::AppState, }; +#[cfg(feature = "wayvr")] +use crate::overlays::wayvr::action_wayvr; + pub mod helpers; pub mod input; pub mod lines; @@ -262,6 +265,14 @@ pub fn openvr_run(running: Arc, show_by_default: bool) -> Result<(), overlays.show_hide(&mut state); } }, + #[cfg(feature = "wayvr")] + TaskType::WayVR(task) => { + if let Some(overlay) = + action_wayvr(&task.catalog_name, &task.app_name, &mut state) + { + overlays.add(overlay); + } + } } } diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index b28a071..5f5a960 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -31,6 +31,9 @@ use crate::{ state::AppState, }; +#[cfg(feature = "wayvr")] +use crate::overlays::wayvr::action_wayvr; + mod helpers; mod input; mod lines; @@ -487,6 +490,14 @@ pub fn openxr_run(running: Arc, show_by_default: bool) -> Result<(), } _ => {} }, + #[cfg(feature = "wayvr")] + TaskType::WayVR(task) => { + if let Some(overlay) = + action_wayvr(&task.catalog_name, &task.app_name, &mut app_state) + { + overlays.add(overlay); + } + } } } diff --git a/src/backend/task.rs b/src/backend/task.rs index dc07b86..47c598f 100644 --- a/src/backend/task.rs +++ b/src/backend/task.rs @@ -5,6 +5,9 @@ use std::{ time::Instant, }; +#[cfg(feature = "wayvr")] +use std::sync::Arc; + use serde::Deserialize; use crate::state::AppState; @@ -49,6 +52,12 @@ pub enum SystemTask { ShowHide, } +#[cfg(feature = "wayvr")] +pub struct WayVRTask { + pub catalog_name: Arc, + pub app_name: Arc, +} + pub type OverlayTask = dyn FnOnce(&mut AppState, &mut OverlayState) + Send; pub type CreateOverlayTask = dyn FnOnce(&mut AppState) -> Option<(OverlayState, Box)> + Send; @@ -59,6 +68,8 @@ pub enum TaskType { CreateOverlay(OverlaySelector, Box), DropOverlay(OverlaySelector), System(SystemTask), + #[cfg(feature = "wayvr")] + WayVR(WayVRTask), } #[derive(Deserialize, Clone, Copy)] diff --git a/src/backend/wayvr/README.md b/src/backend/wayvr/README.md index da5f07b..fd72057 100644 --- a/src/backend/wayvr/README.md +++ b/src/backend/wayvr/README.md @@ -30,7 +30,7 @@ - Due to unknown circumstances, dma-buf textures may display various graphical glitches due to invalid dma-buf tiling modifier. Please report your GPU model when filing an issue. Alternatively, you can run wlx-overlay-s with `LIBGL_ALWAYS_SOFTWARE=1` to mitigate that (only the Smithay compositor will run in software renderer mode, wlx will still be accelerated). -- Potential data race in the rendering pipeline - A texture could be displayed during the clear-and-blit process in the compositor, causing minor artifacts (no fence sync support yet). +- ~~Potential data race in the rendering pipeline - A texture could be displayed during the clear-and-blit process in the compositor, causing minor artifacts (no fence sync support yet).~~ happens on all overlays on a simulated Monado driver - Even though some applications support Wayland, some still check for the `DISPLAY` environment variable and an available X11 server, throwing an error. This can be fixed by running `cage`. diff --git a/src/backend/wayvr/display.rs b/src/backend/wayvr/display.rs index b5282a3..f44964b 100644 --- a/src/backend/wayvr/display.rs +++ b/src/backend/wayvr/display.rs @@ -46,6 +46,7 @@ pub struct Display { // Display info stuff pub width: u32, pub height: u32, + pub name: String, wm: Rc>, displayed_windows: Vec, wayland_env: super::WaylandEnv, @@ -76,6 +77,7 @@ impl Display { wayland_env: super::WaylandEnv, width: u32, height: u32, + name: &str, ) -> anyhow::Result { let tex_format = ffi::RGBA; let internal_format = ffi::RGBA8; @@ -102,6 +104,7 @@ impl Display { wm, width, height, + name: String::from(name), displayed_windows: Vec::new(), egl_data, dmabuf_data, diff --git a/src/backend/wayvr/mod.rs b/src/backend/wayvr/mod.rs index 7262ce3..87b6521 100644 --- a/src/backend/wayvr/mod.rs +++ b/src/backend/wayvr/mod.rs @@ -178,10 +178,29 @@ impl WayVR { .map(|display| display.dmabuf_data.clone()) } + pub fn get_display_by_name(&self, name: &str) -> Option { + for (idx, cell) in self.displays.vec.iter().enumerate() { + if let Some(cell) = cell { + if cell.obj.name == name { + return Some(DisplayVec::get_handle(cell, idx)); + } + } + } + None + } + + pub fn get_display_by_handle( + &self, + display: display::DisplayHandle, + ) -> Option<&display::Display> { + self.displays.get(&display) + } + pub fn create_display( &mut self, width: u32, height: u32, + name: &str, ) -> anyhow::Result { let display = display::Display::new( self.wm.clone(), @@ -190,6 +209,7 @@ impl WayVR { self.manager.wayland_env.clone(), width, height, + name, )?; Ok(self.displays.add(display)) } diff --git a/src/config.rs b/src/config.rs index 5bec6fb..b744671 100644 --- a/src/config.rs +++ b/src/config.rs @@ -297,18 +297,20 @@ impl GeneralConfig { } } -const FALLBACKS: [&str; 4] = [ +const FALLBACKS: [&str; 5] = [ include_str!("res/keyboard.yaml"), include_str!("res/watch.yaml"), include_str!("res/settings.yaml"), include_str!("res/anchor.yaml"), + include_str!("res/wayvr.yaml"), ]; -const FILES: [&str; 4] = [ +const FILES: [&str; 5] = [ "keyboard.yaml", "watch.yaml", "settings.yaml", "anchor.yaml", + "wayvr.yaml", ]; #[derive(Clone, Copy)] @@ -318,6 +320,8 @@ pub enum ConfigType { Watch, Settings, Anchor, + #[allow(dead_code)] + WayVR, } pub fn load_known_yaml(config_type: ConfigType) -> T diff --git a/src/config_wayvr.rs b/src/config_wayvr.rs new file mode 100644 index 0000000..338b773 --- /dev/null +++ b/src/config_wayvr.rs @@ -0,0 +1,60 @@ +#[cfg(not(feature = "wayvr"))] +compile_error!("WayVR feature is not enabled"); + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::config::{load_known_yaml, ConfigType}; + +#[derive(Clone, Deserialize, Serialize)] +pub struct WayVRAppEntry { + pub name: String, + pub target_display: String, + pub exec: String, + pub args: Option, + pub env: Option>, +} + +#[derive(Clone, Deserialize, Serialize)] +pub struct WayVRDisplay { + pub width: u32, + pub height: u32, + pub scale: f32, +} + +#[derive(Clone, Deserialize, Serialize)] +pub struct WayVRCatalog { + pub apps: Vec, +} + +impl WayVRCatalog { + pub fn get_app(&self, name: &str) -> Option<&WayVRAppEntry> { + self.apps.iter().find(|&app| app.name.as_str() == name) + } +} + +#[derive(Deserialize, Serialize)] +pub struct WayVRConfig { + pub version: u32, + pub catalogs: HashMap, + pub displays: HashMap, +} + +impl WayVRConfig { + pub fn get_catalog(&self, name: &str) -> Option<&WayVRCatalog> { + self.catalogs.get(name) + } + + pub fn get_display(&self, name: &str) -> Option<&WayVRDisplay> { + self.displays.get(name) + } +} + +pub fn load_wayvr() -> WayVRConfig { + let config = load_known_yaml::(ConfigType::WayVR); + if config.version != 1 { + panic!("WayVR config version {} is not supported", config.version); + } + config +} diff --git a/src/gui/modular/button.rs b/src/gui/modular/button.rs index d78ef84..9f34615 100644 --- a/src/gui/modular/button.rs +++ b/src/gui/modular/button.rs @@ -25,6 +25,9 @@ use crate::{ state::AppState, }; +#[cfg(feature = "wayvr")] +use crate::backend::task::WayVRTask; + use super::{ExecArgs, ModularControl, ModularData}; #[derive(Deserialize, Clone)] @@ -134,6 +137,11 @@ pub enum ButtonAction { target: OverlaySelector, action: OverlayAction, }, + #[cfg(feature = "wayvr")] + WayVR { + catalog_name: Arc, + app_name: Arc, + }, Window { target: Arc, action: WindowAction, @@ -327,6 +335,16 @@ fn handle_action(action: &ButtonAction, press: &mut PressData, app: &mut AppStat ButtonAction::Watch { action } => run_watch(action, app), ButtonAction::Overlay { target, action } => run_overlay(target, action, app), ButtonAction::Window { target, action } => run_window(target, action, app), + #[cfg(feature = "wayvr")] + ButtonAction::WayVR { + catalog_name, + app_name, + } => { + app.tasks.enqueue(TaskType::WayVR(WayVRTask { + catalog_name: catalog_name.clone(), + app_name: app_name.clone(), + })); + } ButtonAction::VirtualKey { keycode, action } => app .hid_provider .send_key(*keycode, matches!(*action, PressRelease::Press)), diff --git a/src/gui/modular/mod.rs b/src/gui/modular/mod.rs index b2a8772..72ec058 100644 --- a/src/gui/modular/mod.rs +++ b/src/gui/modular/mod.rs @@ -110,6 +110,15 @@ pub enum ModularElement { #[serde(flatten)] template: Box, }, + // Ignored if "wayvr" feature is not enabled + WayVRLauncher { + rect: [f32; 4], + corner_radius: Option, + font_size: isize, + fg_color: Arc, + bg_color: Arc, + catalog_name: Arc, + }, } #[derive(Deserialize, Clone)] @@ -398,6 +407,58 @@ pub fn modular_canvas( }; } } + #[allow(unused_variables)] // needed in case if wayvr feature is not enabled + ModularElement::WayVRLauncher { + rect: [x, y, w, h], + corner_radius, + font_size, + fg_color, + bg_color, + catalog_name, + } => { + #[cfg(feature = "wayvr")] + { + if let Some(catalog) = state.session.wayvr_config.get_catalog(catalog_name) { + let mut button_x = *x; + let button_y = *y; + + for app in &catalog.apps { + let button_w: f32 = *w / catalog.apps.len() as f32; + let button_h: f32 = *h; + + canvas.bg_color = color_parse(bg_color).unwrap_or(*FALLBACK_COLOR); + canvas.fg_color = color_parse(fg_color).unwrap_or(*FALLBACK_COLOR); + canvas.font_size = *font_size; + + let button = canvas.button( + button_x + 2., + button_y + 2., + button_w - 4., + button_h - 4., + corner_radius.unwrap_or_default(), + Arc::from(app.name.as_str()), + ); + + let data = ButtonData { + click_down: Some(vec![ButtonAction::WayVR { + catalog_name: catalog_name.clone(), + app_name: Arc::from(app.name.as_str()), + }]), + ..Default::default() + }; + + modular_button_init(button, &data); + button_x += button_w; + } + } else { + log::error!("WayVR catalog \"{}\" not found", catalog_name); + } + } + #[cfg(not(feature = "wayvr"))] + { + log::error!("WayVR feature is not available, ignoring"); + } + } } } Ok(canvas.build()) diff --git a/src/main.rs b/src/main.rs index 8ca26f2..3e8d5ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,9 @@ mod overlays; mod shaders; mod state; +#[cfg(feature = "wayvr")] +mod config_wayvr; + use std::{ path::PathBuf, sync::{ diff --git a/src/overlays/wayvr.rs b/src/overlays/wayvr.rs index 0b616d9..b44dbb3 100644 --- a/src/overlays/wayvr.rs +++ b/src/overlays/wayvr.rs @@ -10,43 +10,22 @@ use crate::{ wayvr, }, graphics::WlxGraphics, - state::{self, KeyboardFocus}, + state::{self, AppState, KeyboardFocus}, }; pub struct WayVRContext { wayvr: Rc>, display: wayvr::display::DisplayHandle, - width: u32, - height: u32, -} - -#[derive(Default)] -pub struct WayVRProcess<'a> { - pub exec_path: &'a str, - pub args: &'a [&'a str], - pub env: &'a [(&'a str, &'a str)], } impl WayVRContext { pub fn new( wvr: Rc>, - width: u32, - height: u32, - processes: &[WayVRProcess], + display: wayvr::display::DisplayHandle, ) -> anyhow::Result { - let mut wayvr = wvr.borrow_mut(); - - let display = wayvr.create_display(width, height)?; - - for process in processes { - wayvr.spawn_process(display, process.exec_path, process.args, process.env)?; - } - Ok(Self { wayvr: wvr.clone(), display, - width, - height, }) } } @@ -73,14 +52,15 @@ impl InteractionHandler for WayVRInteractionHandler { ) -> Option { let ctx = self.context.borrow(); - let pos = self.mouse_transform.transform_point2(hit.uv); - let x = ((pos.x * ctx.width as f32) as i32).max(0); - let y = ((pos.y * ctx.height as f32) as i32).max(0); + let mut wayvr = ctx.wayvr.borrow_mut(); + if let Some(disp) = wayvr.get_display_by_handle(ctx.display) { + let pos = self.mouse_transform.transform_point2(hit.uv); + let x = ((pos.x * disp.width as f32) as i32).max(0); + let y = ((pos.y * disp.height as f32) as i32).max(0); - let ctx = self.context.borrow(); - ctx.wayvr - .borrow_mut() - .send_mouse_move(ctx.display, x as u32, y as u32); + let ctx = self.context.borrow(); + wayvr.send_mouse_move(ctx.display, x as u32, y as u32); + } None } @@ -120,24 +100,16 @@ pub struct WayVRRenderer { view: Option>, context: Rc>, graphics: Arc, - width: u32, - height: u32, } impl WayVRRenderer { pub fn new( app: &mut state::AppState, wvr: Rc>, - width: u32, - height: u32, - processes: &[WayVRProcess], + display: wayvr::display::DisplayHandle, ) -> anyhow::Result { Ok(Self { - context: Rc::new(RefCell::new(WayVRContext::new( - wvr, width, height, processes, - )?)), - width, - height, + context: Rc::new(RefCell::new(WayVRContext::new(wvr, display)?)), dmabuf_image: None, view: None, graphics: app.graphics.clone(), @@ -154,35 +126,43 @@ impl WayVRRenderer { planes[0].offset = data.offset as u32; planes[0].stride = data.stride; - let frame = DmabufFrame { - format: FrameFormat { - width: self.width, - height: self.height, - fourcc: FourCC { - value: data.mod_info.fourcc, + let ctx = self.context.borrow_mut(); + let wayvr = ctx.wayvr.borrow_mut(); + if let Some(disp) = wayvr.get_display_by_handle(ctx.display) { + let frame = DmabufFrame { + format: FrameFormat { + width: disp.width, + height: disp.height, + fourcc: FourCC { + value: data.mod_info.fourcc, + }, + modifier: data.mod_info.modifiers[0], /* possibly not proper? */ }, - modifier: data.mod_info.modifiers[0], /* possibly not proper? */ - }, - num_planes: 1, - planes, - }; + num_planes: 1, + planes, + }; - let layouts: Vec = vec![SubresourceLayout { - offset: data.offset as _, - size: 0, - row_pitch: data.stride as _, - array_pitch: None, - depth_pitch: None, - }]; + drop(wayvr); - let tex = self.graphics.dmabuf_texture_ex( - frame, - vulkano::image::ImageTiling::DrmFormatModifier, - layouts, - data.mod_info.modifiers, - )?; - self.dmabuf_image = Some(tex.clone()); - self.view = Some(vulkano::image::view::ImageView::new_default(tex).unwrap()); + let layouts: Vec = vec![SubresourceLayout { + offset: data.offset as _, + size: 0, + row_pitch: data.stride as _, + array_pitch: None, + depth_pitch: None, + }]; + + let tex = self.graphics.dmabuf_texture_ex( + frame, + vulkano::image::ImageTiling::DrmFormatModifier, + layouts, + data.mod_info.modifiers, + )?; + self.dmabuf_image = Some(tex.clone()); + self.view = Some(vulkano::image::view::ImageView::new_default(tex).unwrap()); + } else { + anyhow::bail!("Failed to fetch WayVR display") + } } Ok(()) @@ -232,31 +212,30 @@ impl OverlayRenderer for WayVRRenderer { #[allow(dead_code)] pub fn create_wayvr( app: &mut state::AppState, - width: u32, - height: u32, - processes: &[WayVRProcess], + display: &wayvr::display::Display, + display_handle: wayvr::display::DisplayHandle, + display_scale: f32, ) -> anyhow::Result> where O: Default, { - let transform = ui_transform(&[width, height]); + let transform = ui_transform(&[display.width, display.height]); let state = OverlayState { - name: format!("WayVR Screen ({}x{})", width, height).into(), + name: format!("WayVR Screen ({}x{})", display.width, display.height).into(), keyboard_focus: Some(KeyboardFocus::WayVR), want_visible: true, interactable: true, - recenter: true, grabbable: true, - spawn_scale: 1.0, - spawn_point: vec3a(0.0, -0.5, 0.0), + spawn_scale: display_scale, + spawn_point: vec3a(0.0, -0.1, -1.0), interaction_transform: transform, ..Default::default() }; let wayvr = app.get_wayvr()?; - let renderer = WayVRRenderer::new(app, wayvr, width, height, processes)?; + let renderer = WayVRRenderer::new(app, wayvr, display_handle)?; let context = renderer.context.clone(); let backend = Box::new(SplitOverlayBackend { @@ -270,3 +249,100 @@ where ..Default::default() }) } + +fn action_wayvr_internal( + catalog_name: &Arc, + app_name: &Arc, + app: &mut AppState, +) -> anyhow::Result>> +where + O: Default, +{ + use crate::overlays::wayvr::create_wayvr; + + let mut created_overlay: Option> = None; + + let wayvr = app.get_wayvr()?.clone(); + + let catalog = app + .session + .wayvr_config + .get_catalog(catalog_name) + .ok_or(anyhow::anyhow!( + "Failed to get catalog \"{}\"", + catalog_name + ))? + .clone(); + + if let Some(app_entry) = catalog.get_app(app_name) { + let mut wayvr = wayvr.borrow_mut(); + + let disp_handle = if let Some(disp) = wayvr.get_display_by_name(&app_entry.target_display) { + disp + } else { + let conf_display = app + .session + .wayvr_config + .get_display(&app_entry.target_display) + .ok_or(anyhow::anyhow!( + "Cannot find display named \"{}\"", + app_entry.target_display + ))?; + + let display_handle = wayvr.create_display( + conf_display.width, + conf_display.height, + &app_entry.target_display, + )?; + let display = wayvr.get_display_by_handle(display_handle).unwrap(); // Never fails + created_overlay = Some(create_wayvr::( + app, + display, + display_handle, + conf_display.scale, + )?); + display_handle + }; + + // Parse additional args + let args_vec: Vec<&str> = if let Some(args) = &app_entry.args { + args.as_str().split_whitespace().collect() + } else { + vec![] + }; + + // Parse additional env + let env_vec: Vec<(&str, &str)> = if let Some(env) = &app_entry.env { + // splits "FOO=BAR=TEST,123" into (&"foo", &"bar=test,123") + env.iter() + .filter_map(|e| e.as_str().split_once('=')) + .collect() + } else { + vec![] + }; + + wayvr.spawn_process(disp_handle, &app_entry.exec, &args_vec, &env_vec)? + } + + Ok(created_overlay) +} + +// Returns newly created overlay (if needed) +pub fn action_wayvr( + catalog_name: &Arc, + app_name: &Arc, + app: &mut AppState, +) -> Option> +where + O: Default, +{ + match action_wayvr_internal(catalog_name, app_name, app) { + Ok(res) => res, + Err(e) => { + // Happens if something went wrong with initialization + // or input exec path is invalid. Do nothing, just print an error + log::error!("action_wayvr failed: {}", e); + None + } + } +} diff --git a/src/res/watch.yaml b/src/res/watch.yaml index f74a5ac..9ce92e6 100644 --- a/src/res/watch.yaml +++ b/src/res/watch.yaml @@ -1,4 +1,4 @@ -# looking to make changes? +# looking to make changes? # drop me in ~/.config/wlxoverlay/watch.yaml # @@ -24,7 +24,7 @@ elements: - type: Window target: settings action: ShowUi # only triggers if not exists - - type: Window + - type: Window target: settings action: Destroy # only triggers if exists since before current frame @@ -55,12 +55,12 @@ elements: scroll_up: - type: Overlay target: "kbd" - action: + action: Opacity: { delta: 0.025 } scroll_down: - type: Overlay target: "kbd" - action: + action: Opacity: { delta: -0.025 } # bottom row, of keyboard + overlays @@ -75,9 +75,9 @@ elements: long_click_up: Reset right_up: ToggleImmovable middle_up: ToggleInteraction - scroll_up: + scroll_up: Opacity: { delta: 0.025 } - scroll_down: + scroll_down: Opacity: { delta: -0.025 } # local clock @@ -89,7 +89,7 @@ elements: source: Clock format: "%H:%M" # 23:59 #format: "%I:%M %p" # 11:59 PM - + # local date - type: Label rect: [20, 117, 200, 20] @@ -126,7 +126,7 @@ elements: fg_color: "#8bd5ca" source: Static text: "Tokyo" # change TZ1 label here - + # alt clock 2 - type: Label rect: [210, 150, 200, 50] @@ -167,7 +167,7 @@ elements: text: "Vol +" click_down: - type: Exec - command: [ "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%" ] + command: ["pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%"] - type: Button rect: [315, 116, 70, 32] corner_radius: 4 @@ -177,4 +177,4 @@ elements: text: "Vol -" click_down: - type: Exec - command: [ "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%" ] + command: ["pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%"] diff --git a/src/res/wayvr.yaml b/src/res/wayvr.yaml new file mode 100644 index 0000000..1ce69e0 --- /dev/null +++ b/src/res/wayvr.yaml @@ -0,0 +1,47 @@ +# This is an example WayVR panel configuration. It demonstrates all the capabilities of this module. +# looking to make changes? +# drop me in ~/.config/wlxoverlay/wayvr.yaml +# + +version: 1 + +displays: + disp_square: + width: 500 + height: 350 + scale: 1.0 + disp_term: + width: 640 + height: 480 + scale: 1.25 + disp_browser: + width: 1280 + height: 720 + scale: 2.0 + disp_plasma: + width: 1280 + height: 720 + scale: 2.0 + +catalogs: + default_catalog: + apps: + - name: "Calc" + target_display: "disp_square" + exec: "kcalc" + env: ["FOO=bar"] + + - name: "Terminal" + target_display: "disp_term" + exec: "konsole" + + - name: "Browser" + target_display: "disp_browser" + exec: "cage" + args: "chromium -- --incognito" + + - name: "Plasma" + target_display: "disp_plasma" + exec: "cage" + args: "dbus-run-session -- kwin_wayland --xwayland plasmashell" + diff --git a/src/state.rs b/src/state.rs index 0c0d43a..4b3fc98 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,6 +13,9 @@ use std::{cell::RefCell, rc::Rc}; #[cfg(feature = "wayvr")] use crate::backend::wayvr::WayVR; +#[cfg(feature = "wayvr")] +use crate::config_wayvr::{self, WayVRConfig}; + use crate::{ backend::{input::InputState, overlay::OverlayID, task::TaskContainer}, config::{AStrMap, GeneralConfig}, @@ -125,6 +128,9 @@ impl AppState { pub struct AppSession { pub config: GeneralConfig, + #[cfg(feature = "wayvr")] + pub wayvr_config: WayVRConfig, + pub toast_topics: IdMap, } @@ -143,9 +149,14 @@ impl AppSession { toast_topics.insert(*k, *v); }); + #[cfg(feature = "wayvr")] + let wayvr_config = config_wayvr::load_wayvr(); + AppSession { config, toast_topics, + #[cfg(feature = "wayvr")] + wayvr_config, } } }