WayVR: Initial GUI integration
The format of the wayvr.yaml configuration file is subject to change at any time.
This commit is contained in:
28
.github/workflows/build-wayland-openxr-openvr-wayvr.yml
vendored
Normal file
28
.github/workflows/build-wayland-openxr-openvr-wayvr.yml
vendored
Normal file
@@ -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
|
||||
@@ -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"]
|
||||
|
||||
@@ -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<AtomicBool>, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AtomicBool>, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<str>,
|
||||
pub app_name: Arc<str>,
|
||||
}
|
||||
|
||||
pub type OverlayTask = dyn FnOnce(&mut AppState, &mut OverlayState) + Send;
|
||||
pub type CreateOverlayTask =
|
||||
dyn FnOnce(&mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> + Send;
|
||||
@@ -59,6 +68,8 @@ pub enum TaskType {
|
||||
CreateOverlay(OverlaySelector, Box<CreateOverlayTask>),
|
||||
DropOverlay(OverlaySelector),
|
||||
System(SystemTask),
|
||||
#[cfg(feature = "wayvr")]
|
||||
WayVR(WayVRTask),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Copy)]
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ pub struct Display {
|
||||
// Display info stuff
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub name: String,
|
||||
wm: Rc<RefCell<window::WindowManager>>,
|
||||
displayed_windows: Vec<DisplayWindow>,
|
||||
wayland_env: super::WaylandEnv,
|
||||
@@ -76,6 +77,7 @@ impl Display {
|
||||
wayland_env: super::WaylandEnv,
|
||||
width: u32,
|
||||
height: u32,
|
||||
name: &str,
|
||||
) -> anyhow::Result<Self> {
|
||||
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,
|
||||
|
||||
@@ -178,10 +178,29 @@ impl WayVR {
|
||||
.map(|display| display.dmabuf_data.clone())
|
||||
}
|
||||
|
||||
pub fn get_display_by_name(&self, name: &str) -> Option<display::DisplayHandle> {
|
||||
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<display::DisplayHandle> {
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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<T>(config_type: ConfigType) -> T
|
||||
|
||||
60
src/config_wayvr.rs
Normal file
60
src/config_wayvr.rs
Normal file
@@ -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<String>,
|
||||
pub env: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[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<WayVRAppEntry>,
|
||||
}
|
||||
|
||||
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<String, WayVRCatalog>,
|
||||
pub displays: HashMap<String, WayVRDisplay>,
|
||||
}
|
||||
|
||||
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::<WayVRConfig>(ConfigType::WayVR);
|
||||
if config.version != 1 {
|
||||
panic!("WayVR config version {} is not supported", config.version);
|
||||
}
|
||||
config
|
||||
}
|
||||
@@ -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<str>,
|
||||
app_name: Arc<str>,
|
||||
},
|
||||
Window {
|
||||
target: Arc<str>,
|
||||
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)),
|
||||
|
||||
@@ -110,6 +110,15 @@ pub enum ModularElement {
|
||||
#[serde(flatten)]
|
||||
template: Box<OverlayListTemplate>,
|
||||
},
|
||||
// Ignored if "wayvr" feature is not enabled
|
||||
WayVRLauncher {
|
||||
rect: [f32; 4],
|
||||
corner_radius: Option<f32>,
|
||||
font_size: isize,
|
||||
fg_color: Arc<str>,
|
||||
bg_color: Arc<str>,
|
||||
catalog_name: Arc<str>,
|
||||
},
|
||||
}
|
||||
|
||||
#[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())
|
||||
|
||||
@@ -9,6 +9,9 @@ mod overlays;
|
||||
mod shaders;
|
||||
mod state;
|
||||
|
||||
#[cfg(feature = "wayvr")]
|
||||
mod config_wayvr;
|
||||
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
|
||||
@@ -10,43 +10,22 @@ use crate::{
|
||||
wayvr,
|
||||
},
|
||||
graphics::WlxGraphics,
|
||||
state::{self, KeyboardFocus},
|
||||
state::{self, AppState, KeyboardFocus},
|
||||
};
|
||||
|
||||
pub struct WayVRContext {
|
||||
wayvr: Rc<RefCell<wayvr::WayVR>>,
|
||||
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<RefCell<wayvr::WayVR>>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
processes: &[WayVRProcess],
|
||||
display: wayvr::display::DisplayHandle,
|
||||
) -> anyhow::Result<Self> {
|
||||
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<input::Haptics> {
|
||||
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<Arc<vulkano::image::view::ImageView>>,
|
||||
context: Rc<RefCell<WayVRContext>>,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl WayVRRenderer {
|
||||
pub fn new(
|
||||
app: &mut state::AppState,
|
||||
wvr: Rc<RefCell<wayvr::WayVR>>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
processes: &[WayVRProcess],
|
||||
display: wayvr::display::DisplayHandle,
|
||||
) -> anyhow::Result<Self> {
|
||||
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<SubresourceLayout> = 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<SubresourceLayout> = 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<O>(
|
||||
app: &mut state::AppState,
|
||||
width: u32,
|
||||
height: u32,
|
||||
processes: &[WayVRProcess],
|
||||
display: &wayvr::display::Display,
|
||||
display_handle: wayvr::display::DisplayHandle,
|
||||
display_scale: f32,
|
||||
) -> anyhow::Result<OverlayData<O>>
|
||||
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<O>(
|
||||
catalog_name: &Arc<str>,
|
||||
app_name: &Arc<str>,
|
||||
app: &mut AppState,
|
||||
) -> anyhow::Result<Option<OverlayData<O>>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
use crate::overlays::wayvr::create_wayvr;
|
||||
|
||||
let mut created_overlay: Option<OverlayData<O>> = 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::<O>(
|
||||
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<O>(
|
||||
catalog_name: &Arc<str>,
|
||||
app_name: &Arc<str>,
|
||||
app: &mut AppState,
|
||||
) -> Option<OverlayData<O>>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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%"]
|
||||
|
||||
47
src/res/wayvr.yaml
Normal file
47
src/res/wayvr.yaml
Normal file
@@ -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"
|
||||
|
||||
11
src/state.rs
11
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<ToastTopic, DisplayMethod>,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user