mirror of
https://github.com/wayvr-org/wayvr.git
synced 2026-10-28 15:02:39 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55867e803f | ||
|
|
60bc8b986e | ||
|
|
5f93bc9cac | ||
|
|
659f1492fb | ||
|
|
a55d0d74bb | ||
|
|
6d39380ebc | ||
|
|
a1cc41f541 | ||
|
|
bca6376b50 | ||
|
|
049f91eed3 | ||
|
|
05b3baa5d9 | ||
|
|
ed804820e9 | ||
|
|
91af7c99b1 | ||
|
|
0b7cd61d05 | ||
|
|
59b489680a | ||
|
|
b0883e81bf | ||
|
|
36074307b7 | ||
|
|
fa3af63e89 | ||
|
|
5cf6eeedb0 | ||
|
|
89680bf8cc | ||
|
|
009862e2a2 | ||
|
|
075e55a8e8 | ||
|
|
e66a8d27c9 | ||
|
|
507ff7353e | ||
|
|
c5a82c2879 | ||
|
|
965174c781 | ||
|
|
bf3f3dafbe | ||
|
|
4701d2b734 |
No files matched your search
@@ -44,6 +44,13 @@ jobs:
|
||||
./linuxdeploy-x86_64.AppImage -dwlx-overlay-s.desktop -iwlx-overlay-s.png --appdir=${APPDIR} --output appimage --exclude-library '*libpipewire*'
|
||||
mv WlxOverlay-S-$VERSION-x86_64.AppImage WlxOverlay-S-x86_64.AppImage
|
||||
|
||||
- name: Make tarball
|
||||
run: |
|
||||
pip install portage pycargoebuild
|
||||
wget https://github.com/gentoo/gentoo/raw/refs/heads/master/metadata/license-mapping.conf
|
||||
mkdir dist
|
||||
pycargoebuild --distdir dist --license-mapping license-mapping.conf --crate-tarball --crate-tarball-path wlx-overlay-s-crates.tar.xz
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -64,3 +71,13 @@ jobs:
|
||||
asset_path: ./WlxOverlay-S-x86_64.AppImage
|
||||
asset_name: WlxOverlay-S-${{ github.ref_name }}-x86_64.AppImage
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload crates tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_KEY }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./wlx-overlay-s-crates.tar.xz
|
||||
asset_name: WlxOverlay-S-${{ github.ref_name }}-crates.tar.xz
|
||||
asset_content_type: application/x-gtar
|
||||
Generated
+604
-438
File diff suppressed because it is too large.
Load diff
+7
-6
@@ -4,7 +4,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "wlx-overlay-s"
|
||||
version = "0.6.0"
|
||||
version = "25.4.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -19,7 +19,6 @@ config = "0.14.0"
|
||||
cstr = "0.2.12"
|
||||
ctrlc = { version = "3.4.4", features = ["termination"] }
|
||||
dbus = { version = "0.9.7" }
|
||||
env_logger = "0.11.5"
|
||||
fontconfig-rs = "0.1.1"
|
||||
freetype-rs = "0.36.0" # latest version supported on ubuntu 22.04
|
||||
futures = "0.3.30"
|
||||
@@ -56,7 +55,7 @@ thiserror = "2.0.3"
|
||||
vulkano = { git = "https://github.com/vulkano-rs/vulkano", rev = "94f50f1" }
|
||||
vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano", rev = "94f50f1" }
|
||||
wlx-capture = { git = "https://github.com/galister/wlx-capture", tag = "v0.4.2", default-features = false }
|
||||
libmonado-rs = { git = "https://github.com/technobaboo/libmonado-rs", rev = "a495f6d", optional = true }
|
||||
libmonado = { git = "https://github.com/technobaboo/libmonado-rs", rev = "256895b18c8f9368174fad8a6232ff07764eeacb", optional = true }
|
||||
winit = { version = "0.30.0", optional = true }
|
||||
xdg = "2.5.2"
|
||||
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
|
||||
@@ -69,12 +68,14 @@ image_dds = { version = "0.6.0", default-features = false, features = [
|
||||
"ddsfile",
|
||||
] }
|
||||
mint = "0.5.9"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
tracing = "0.1.41"
|
||||
|
||||
################################
|
||||
#WayVR-only deps
|
||||
################################
|
||||
khronos-egl = { version = "6.0.0", features = ["static"], optional = true }
|
||||
smithay = { git = "https://github.com/Smithay/smithay.git", default-features = false, features = [
|
||||
smithay = { version = "0.5.1", default-features = false, features = [
|
||||
"renderer_gl",
|
||||
"backend_egl",
|
||||
"backend_drm",
|
||||
@@ -86,7 +87,7 @@ wayland-client = { version = "0.31.6", optional = true }
|
||||
wayland-egl = { version = "0.32.4", optional = true }
|
||||
interprocess = { version = "2.2.2", optional = true }
|
||||
bytes = { version = "1.9.0", optional = true }
|
||||
wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "3c411d09ba1bba2609288e29739c0f1ec736b012", default-features = false, optional = true }
|
||||
wayvr_ipc = { git = "https://github.com/olekolek1000/wayvr-ipc.git", rev = "a72587d23f3bb8624d9aeb1f13c0a21e65350f51", default-features = false, optional = true }
|
||||
################################
|
||||
|
||||
[build-dependencies]
|
||||
@@ -95,7 +96,7 @@ regex = { version = "*" }
|
||||
[features]
|
||||
default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"]
|
||||
openvr = ["dep:ovr_overlay", "dep:json"]
|
||||
openxr = ["dep:openxr", "dep:libmonado-rs"]
|
||||
openxr = ["dep:openxr", "dep:libmonado"]
|
||||
osc = ["dep:rosc"]
|
||||
x11 = ["dep:xcb", "wlx-capture/xshm", "xkbcommon/x11"]
|
||||
wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
|
||||
|
||||
@@ -28,9 +28,21 @@ elements:
|
||||
target: settings
|
||||
action: Destroy # only triggers if exists since before current frame
|
||||
|
||||
# Dashboard toggle button
|
||||
- type: Button
|
||||
rect: [32, 162, 48, 36]
|
||||
corner_radius: 4
|
||||
font_size: 15
|
||||
bg_color: "#2288FF"
|
||||
fg_color: "#24273a"
|
||||
text: "Dash"
|
||||
click_up:
|
||||
- type: WayVR
|
||||
action: ToggleDashboard
|
||||
|
||||
# Keyboard button
|
||||
- type: Button
|
||||
rect: [32, 162, 60, 36]
|
||||
rect: [84, 162, 48, 36]
|
||||
corner_radius: 4
|
||||
font_size: 15
|
||||
fg_color: "#24273a"
|
||||
@@ -65,7 +77,7 @@ elements:
|
||||
|
||||
# bottom row, of keyboard + overlays
|
||||
- type: OverlayList
|
||||
rect: [94, 160, 306, 40]
|
||||
rect: [134, 160, 266, 40]
|
||||
corner_radius: 4
|
||||
font_size: 15
|
||||
fg_color: "#cad3f5"
|
||||
|
||||
+14
-12
@@ -217,7 +217,8 @@ impl Pointer {
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct PointerState {
|
||||
pub scroll: f32,
|
||||
pub scroll_x: f32,
|
||||
pub scroll_y: f32,
|
||||
pub click: bool,
|
||||
pub grab: bool,
|
||||
pub alt_click: bool,
|
||||
@@ -252,7 +253,7 @@ pub trait InteractionHandler {
|
||||
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option<Haptics>;
|
||||
fn on_left(&mut self, app: &mut AppState, pointer: usize);
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32);
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32);
|
||||
}
|
||||
|
||||
pub struct DummyInteractionHandler;
|
||||
@@ -263,7 +264,7 @@ impl InteractionHandler for DummyInteractionHandler {
|
||||
None
|
||||
}
|
||||
fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {}
|
||||
fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {}
|
||||
fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta_y: f32, _delta_x: f32) {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
@@ -419,8 +420,9 @@ where
|
||||
|
||||
pointer = &mut app.input_state.pointers[idx];
|
||||
|
||||
if pointer.now.scroll.abs() > 0.1 {
|
||||
let scroll = pointer.now.scroll;
|
||||
if pointer.now.scroll_x.abs() > 0.1 || pointer.now.scroll_y.abs() > 0.1 {
|
||||
let scroll_x = pointer.now.scroll_x;
|
||||
let scroll_y = pointer.now.scroll_y;
|
||||
if app.input_state.pointers[1 - idx]
|
||||
.interaction
|
||||
.grabbed
|
||||
@@ -432,7 +434,7 @@ where
|
||||
|
||||
if can_curve {
|
||||
let cur = hovered.state.curvature.unwrap_or(0.0);
|
||||
let new = (cur - scroll * 0.01).min(0.5);
|
||||
let new = (cur - scroll_y * 0.01).min(0.5);
|
||||
if new <= f32::EPSILON {
|
||||
hovered.state.curvature = None;
|
||||
} else {
|
||||
@@ -442,7 +444,7 @@ where
|
||||
hovered.state.curvature = None;
|
||||
}
|
||||
} else {
|
||||
hovered.backend.on_scroll(app, &hit, scroll);
|
||||
hovered.backend.on_scroll(app, &hit, scroll_y, scroll_x);
|
||||
}
|
||||
pointer = &mut app.input_state.pointers[idx];
|
||||
}
|
||||
@@ -560,10 +562,10 @@ impl Pointer {
|
||||
if self.now.click {
|
||||
self.interaction.mode = PointerMode::Special;
|
||||
let cur_scale = overlay.state.transform.x_axis.length();
|
||||
if cur_scale < 0.1 && self.now.scroll > 0.0 {
|
||||
if cur_scale < 0.1 && self.now.scroll_y > 0.0 {
|
||||
return;
|
||||
}
|
||||
if cur_scale > 20. && self.now.scroll < 0.0 {
|
||||
if cur_scale > 20. && self.now.scroll_y < 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -571,9 +573,9 @@ impl Pointer {
|
||||
.state
|
||||
.transform
|
||||
.matrix3
|
||||
.mul_scalar(1.0 - 0.025 * self.now.scroll);
|
||||
} else if config.allow_sliding && self.now.scroll.is_finite() {
|
||||
grab_data.offset.z -= self.now.scroll * 0.05;
|
||||
.mul_scalar(1.0 - 0.025 * self.now.scroll_y);
|
||||
} else if config.allow_sliding && self.now.scroll_y.is_finite() {
|
||||
grab_data.offset.z -= self.now.scroll_y * 0.05;
|
||||
}
|
||||
overlay.state.transform.translation = self.pose.transform_point3a(grab_data.offset);
|
||||
overlay.state.realign(hmd);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -g -m None -d org.freedesktop.Notifications -p /org/freedesktop/Notifications`, see https://github.com/diwic/dbus-rs
|
||||
use dbus as dbus;
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgFreedesktopDBusProperties {
|
||||
fn get<R0: for<'b> arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> Result<R0, dbus::Error>;
|
||||
fn get<R0: for<'b> arg::Get<'b> + 'static>(
|
||||
&self,
|
||||
interface_name: &str,
|
||||
property_name: &str,
|
||||
) -> Result<R0, dbus::Error>;
|
||||
fn get_all(&self, interface_name: &str) -> Result<arg::PropMap, dbus::Error>;
|
||||
fn set<I2: arg::Arg + arg::Append>(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), dbus::Error>;
|
||||
fn set<I2: arg::Arg + arg::Append>(
|
||||
&self,
|
||||
interface_name: &str,
|
||||
property_name: &str,
|
||||
value: I2,
|
||||
) -> Result<(), dbus::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -40,20 +49,42 @@ impl dbus::message::SignalArgs for OrgFreedesktopDBusPropertiesPropertiesChanged
|
||||
const INTERFACE: &'static str = "org.freedesktop.DBus.Properties";
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target=T>> OrgFreedesktopDBusProperties for blocking::Proxy<'a, C> {
|
||||
|
||||
fn get<R0: for<'b> arg::Get<'b> + 'static>(&self, interface_name: &str, property_name: &str) -> Result<R0, dbus::Error> {
|
||||
self.method_call("org.freedesktop.DBus.Properties", "Get", (interface_name, property_name, ))
|
||||
.and_then(|r: (arg::Variant<R0>, )| Ok((r.0).0, ))
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgFreedesktopDBusProperties
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn get<R0: for<'b> arg::Get<'b> + 'static>(
|
||||
&self,
|
||||
interface_name: &str,
|
||||
property_name: &str,
|
||||
) -> Result<R0, dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get",
|
||||
(interface_name, property_name),
|
||||
)
|
||||
.and_then(|r: (arg::Variant<R0>,)| Ok((r.0).0))
|
||||
}
|
||||
|
||||
fn get_all(&self, interface_name: &str) -> Result<arg::PropMap, dbus::Error> {
|
||||
self.method_call("org.freedesktop.DBus.Properties", "GetAll", (interface_name, ))
|
||||
.and_then(|r: (arg::PropMap, )| Ok(r.0, ))
|
||||
self.method_call(
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"GetAll",
|
||||
(interface_name,),
|
||||
)
|
||||
.and_then(|r: (arg::PropMap,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn set<I2: arg::Arg + arg::Append>(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.DBus.Properties", "Set", (interface_name, property_name, arg::Variant(value), ))
|
||||
fn set<I2: arg::Arg + arg::Append>(
|
||||
&self,
|
||||
interface_name: &str,
|
||||
property_name: &str,
|
||||
value: I2,
|
||||
) -> Result<(), dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Set",
|
||||
(interface_name, property_name, arg::Variant(value)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,11 +92,12 @@ pub trait OrgFreedesktopDBusIntrospectable {
|
||||
fn introspect(&self) -> Result<String, dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target=T>> OrgFreedesktopDBusIntrospectable for blocking::Proxy<'a, C> {
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>>
|
||||
OrgFreedesktopDBusIntrospectable for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn introspect(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ())
|
||||
.and_then(|r: (String, )| Ok(r.0, ))
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,15 +106,16 @@ pub trait OrgFreedesktopDBusPeer {
|
||||
fn get_machine_id(&self) -> Result<String, dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target=T>> OrgFreedesktopDBusPeer for blocking::Proxy<'a, C> {
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgFreedesktopDBusPeer
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn ping(&self) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.DBus.Peer", "Ping", ())
|
||||
}
|
||||
|
||||
fn get_machine_id(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ())
|
||||
.and_then(|r: (String, )| Ok(r.0, ))
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +128,17 @@ pub trait OrgFreedesktopNotifications {
|
||||
fn close_all_notifications(&self) -> Result<(), dbus::Error>;
|
||||
fn hide_latest_notification(&self, close: bool) -> Result<(), dbus::Error>;
|
||||
fn get_capabilities(&self) -> Result<Vec<String>, dbus::Error>;
|
||||
fn notify(&self, app_name: &str, replaces_id: u32, app_icon: &str, summary: &str, body: &str, actions: Vec<&str>, hints: arg::PropMap, expire_timeout: i32) -> Result<u32, dbus::Error>;
|
||||
fn notify(
|
||||
&self,
|
||||
app_name: &str,
|
||||
replaces_id: u32,
|
||||
app_icon: &str,
|
||||
summary: &str,
|
||||
body: &str,
|
||||
actions: Vec<&str>,
|
||||
hints: arg::PropMap,
|
||||
expire_timeout: i32,
|
||||
) -> Result<u32, dbus::Error>;
|
||||
fn close_notification(&self, id: u32) -> Result<(), dbus::Error>;
|
||||
fn get_server_information(&self) -> Result<(String, String, String, String), dbus::Error>;
|
||||
fn dnd(&self) -> Result<bool, dbus::Error>;
|
||||
@@ -115,9 +158,7 @@ impl arg::AppendAll for OrgFreedesktopNotificationsOnDndToggle {
|
||||
|
||||
impl arg::ReadAll for OrgFreedesktopNotificationsOnDndToggle {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgFreedesktopNotificationsOnDndToggle {
|
||||
dnd: i.read()?,
|
||||
})
|
||||
Ok(OrgFreedesktopNotificationsOnDndToggle { dnd: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,28 +248,37 @@ impl dbus::message::SignalArgs for OrgFreedesktopNotificationsNotificationReplie
|
||||
const INTERFACE: &'static str = "org.freedesktop.Notifications";
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target=T>> OrgFreedesktopNotifications for blocking::Proxy<'a, C> {
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgFreedesktopNotifications
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn set_noti_window_visibility(&self, value: bool) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "SetNotiWindowVisibility", (value, ))
|
||||
self.method_call(
|
||||
"org.freedesktop.Notifications",
|
||||
"SetNotiWindowVisibility",
|
||||
(value,),
|
||||
)
|
||||
}
|
||||
|
||||
fn toggle_dnd(&self) -> Result<bool, dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "ToggleDnd", ())
|
||||
.and_then(|r: (bool, )| Ok(r.0, ))
|
||||
.and_then(|r: (bool,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn set_dnd(&self, state: bool) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "SetDnd", (state, ))
|
||||
self.method_call("org.freedesktop.Notifications", "SetDnd", (state,))
|
||||
}
|
||||
|
||||
fn get_dnd(&self) -> Result<bool, dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "GetDnd", ())
|
||||
.and_then(|r: (bool, )| Ok(r.0, ))
|
||||
.and_then(|r: (bool,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn manually_close_notification(&self, id: u32, timeout: bool) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "ManuallyCloseNotification", (id, timeout, ))
|
||||
self.method_call(
|
||||
"org.freedesktop.Notifications",
|
||||
"ManuallyCloseNotification",
|
||||
(id, timeout),
|
||||
)
|
||||
}
|
||||
|
||||
fn close_all_notifications(&self) -> Result<(), dbus::Error> {
|
||||
@@ -236,21 +286,48 @@ impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target=T>> OrgFreedes
|
||||
}
|
||||
|
||||
fn hide_latest_notification(&self, close: bool) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "HideLatestNotification", (close, ))
|
||||
self.method_call(
|
||||
"org.freedesktop.Notifications",
|
||||
"HideLatestNotification",
|
||||
(close,),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_capabilities(&self) -> Result<Vec<String>, dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "GetCapabilities", ())
|
||||
.and_then(|r: (Vec<String>, )| Ok(r.0, ))
|
||||
.and_then(|r: (Vec<String>,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn notify(&self, app_name: &str, replaces_id: u32, app_icon: &str, summary: &str, body: &str, actions: Vec<&str>, hints: arg::PropMap, expire_timeout: i32) -> Result<u32, dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "Notify", (app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout, ))
|
||||
.and_then(|r: (u32, )| Ok(r.0, ))
|
||||
fn notify(
|
||||
&self,
|
||||
app_name: &str,
|
||||
replaces_id: u32,
|
||||
app_icon: &str,
|
||||
summary: &str,
|
||||
body: &str,
|
||||
actions: Vec<&str>,
|
||||
hints: arg::PropMap,
|
||||
expire_timeout: i32,
|
||||
) -> Result<u32, dbus::Error> {
|
||||
self.method_call(
|
||||
"org.freedesktop.Notifications",
|
||||
"Notify",
|
||||
(
|
||||
app_name,
|
||||
replaces_id,
|
||||
app_icon,
|
||||
summary,
|
||||
body,
|
||||
actions,
|
||||
hints,
|
||||
expire_timeout,
|
||||
),
|
||||
)
|
||||
.and_then(|r: (u32,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn close_notification(&self, id: u32) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.freedesktop.Notifications", "CloseNotification", (id, ))
|
||||
self.method_call("org.freedesktop.Notifications", "CloseNotification", (id,))
|
||||
}
|
||||
|
||||
fn get_server_information(&self) -> Result<(String, String, String, String), dbus::Error> {
|
||||
@@ -258,10 +335,19 @@ impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target=T>> OrgFreedes
|
||||
}
|
||||
|
||||
fn dnd(&self) -> Result<bool, dbus::Error> {
|
||||
<Self as blocking::stdintf::org_freedesktop_dbus::Properties>::get(self, "org.freedesktop.Notifications", "Dnd")
|
||||
<Self as blocking::stdintf::org_freedesktop_dbus::Properties>::get(
|
||||
self,
|
||||
"org.freedesktop.Notifications",
|
||||
"Dnd",
|
||||
)
|
||||
}
|
||||
|
||||
fn set_dnd_(&self, value: bool) -> Result<(), dbus::Error> {
|
||||
<Self as blocking::stdintf::org_freedesktop_dbus::Properties>::set(self, "org.freedesktop.Notifications", "Dnd", value)
|
||||
<Self as blocking::stdintf::org_freedesktop_dbus::Properties>::set(
|
||||
self,
|
||||
"org.freedesktop.Notifications",
|
||||
"Dnd",
|
||||
value,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -230,10 +230,12 @@ impl OpenVrInputSource {
|
||||
.map(|x| x.0.bState)
|
||||
.unwrap_or(false);
|
||||
|
||||
app_hand.now.scroll = input
|
||||
let scroll = input
|
||||
.get_analog_action_data(self.scroll_hnd, hand.input_hnd)
|
||||
.map(|x| x.0.y)
|
||||
.unwrap_or(0.0);
|
||||
.map(|x| (x.0.x, x.0.y))
|
||||
.unwrap_or((0.0, 0.0));
|
||||
app_hand.now.scroll_x = scroll.0;
|
||||
app_hand.now.scroll_y = scroll.1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ pub(super) fn ipd_from_views(views: &[xr::View]) -> f32 {
|
||||
let p0: Vec3 = Vec3M::from(views[0].pose.position).into();
|
||||
let p1: Vec3 = Vec3M::from(views[1].pose.position).into();
|
||||
|
||||
(p0.distance(p1) * 1000.0).round() * 0.1
|
||||
(p0.distance(p1) * 10000.0).round() * 0.1
|
||||
}
|
||||
|
||||
pub(super) fn transform_to_norm_quat(transform: &Affine3A) -> Quat {
|
||||
|
||||
+24
-15
@@ -5,8 +5,8 @@ use std::{
|
||||
};
|
||||
|
||||
use glam::{bool, Affine3A, Quat, Vec3};
|
||||
use libmonado_rs::{Device, Monado};
|
||||
use openxr::{self as xr, Quaternionf, Vector3f};
|
||||
use libmonado as mnd;
|
||||
use openxr::{self as xr, Quaternionf, Vector2f, Vector3f};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@@ -161,7 +161,7 @@ pub(super) struct OpenXrHandSource {
|
||||
action_modifier_right: CustomClickAction,
|
||||
action_modifier_middle: CustomClickAction,
|
||||
action_move_mouse: CustomClickAction,
|
||||
action_scroll: xr::Action<f32>,
|
||||
action_scroll: xr::Action<Vector2f>,
|
||||
action_haptics: xr::Action<xr::Haptic>,
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ impl OpenXrInputSource {
|
||||
}
|
||||
|
||||
fn update_device_battery_status(
|
||||
device: &mut Device,
|
||||
device: &mut mnd::Device,
|
||||
role: TrackedDeviceRole,
|
||||
app: &mut AppState,
|
||||
) {
|
||||
@@ -251,17 +251,23 @@ impl OpenXrInputSource {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_devices(&mut self, app: &mut AppState, monado: &mut Monado) {
|
||||
pub fn update_devices(&mut self, app: &mut AppState, monado: &mut mnd::Monado) {
|
||||
app.input_state.devices.clear();
|
||||
|
||||
let roles = [
|
||||
("head", TrackedDeviceRole::Hmd),
|
||||
("eyes", TrackedDeviceRole::None),
|
||||
("left", TrackedDeviceRole::LeftHand),
|
||||
("right", TrackedDeviceRole::RightHand),
|
||||
("gamepad", TrackedDeviceRole::None),
|
||||
("hand-tracking-left", TrackedDeviceRole::LeftHand),
|
||||
("hand-tracking-right", TrackedDeviceRole::RightHand),
|
||||
(mnd::DeviceRole::Head, TrackedDeviceRole::Hmd),
|
||||
(mnd::DeviceRole::Eyes, TrackedDeviceRole::None),
|
||||
(mnd::DeviceRole::Left, TrackedDeviceRole::LeftHand),
|
||||
(mnd::DeviceRole::Right, TrackedDeviceRole::RightHand),
|
||||
(mnd::DeviceRole::Gamepad, TrackedDeviceRole::None),
|
||||
(
|
||||
mnd::DeviceRole::HandTrackingLeft,
|
||||
TrackedDeviceRole::LeftHand,
|
||||
),
|
||||
(
|
||||
mnd::DeviceRole::HandTrackingRight,
|
||||
TrackedDeviceRole::RightHand,
|
||||
),
|
||||
];
|
||||
let mut seen = Vec::<u32>::with_capacity(32);
|
||||
for (mnd_role, wlx_role) in roles {
|
||||
@@ -276,7 +282,7 @@ impl OpenXrInputSource {
|
||||
if let Ok(devices) = monado.devices() {
|
||||
for mut device in devices {
|
||||
if !seen.contains(&device.index) {
|
||||
let role = if device.id >= 4 && device.id <= 8 {
|
||||
let role = if device.name_id >= 4 && device.name_id <= 8 {
|
||||
TrackedDeviceRole::Tracker
|
||||
} else {
|
||||
TrackedDeviceRole::None
|
||||
@@ -344,12 +350,15 @@ impl OpenXrHand {
|
||||
.action_grab
|
||||
.state(pointer.before.grab, xr, session)?;
|
||||
|
||||
pointer.now.scroll = self
|
||||
let scroll = self
|
||||
.source
|
||||
.action_scroll
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state;
|
||||
|
||||
pointer.now.scroll_x = scroll.x;
|
||||
pointer.now.scroll_y = scroll.y;
|
||||
|
||||
pointer.now.alt_click =
|
||||
self.source
|
||||
.action_alt_click
|
||||
@@ -411,7 +420,7 @@ impl OpenXrHandSource {
|
||||
&[],
|
||||
)?;
|
||||
|
||||
let action_scroll = action_set.create_action::<f32>(
|
||||
let action_scroll = action_set.create_action::<Vector2f>(
|
||||
&format!("{}_scroll", side),
|
||||
&format!("{} hand scroll", side),
|
||||
&[],
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
};
|
||||
|
||||
use glam::{Affine3A, Vec3};
|
||||
use libmonado_rs::Monado;
|
||||
use libmonado::Monado;
|
||||
use openxr as xr;
|
||||
use skybox::create_skybox;
|
||||
use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
|
||||
|
||||
@@ -81,6 +81,10 @@
|
||||
left: "/user/hand/left/input/thumbstick/y",
|
||||
right: "/user/hand/right/input/thumbstick/y"
|
||||
},
|
||||
scroll_horizontal: {
|
||||
left: "/user/hand/left/input/thumbstick/x",
|
||||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
show_hide: {
|
||||
double_click: true,
|
||||
left: "/user/hand/left/input/y/click",
|
||||
@@ -129,6 +133,10 @@
|
||||
scroll: {
|
||||
left: "/user/hand/left/input/thumbstick/y",
|
||||
right: "/user/hand/right/input/thumbstick/y"
|
||||
},
|
||||
scroll_horizontal: {
|
||||
left: "/user/hand/left/input/thumbstick/x",
|
||||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
toggle_dashboard: {
|
||||
double_click: false,
|
||||
@@ -180,6 +188,10 @@
|
||||
left: "/user/hand/left/input/trackpad/y",
|
||||
right: "/user/hand/right/input/trackpad/y"
|
||||
},
|
||||
scroll_horizontal: {
|
||||
left: "/user/hand/left/input/trackpad/x",
|
||||
right: "/user/hand/right/input/trackpad/x"
|
||||
},
|
||||
show_hide: {
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
},
|
||||
@@ -215,6 +227,10 @@
|
||||
left: "/user/hand/left/input/thumbstick/y",
|
||||
right: "/user/hand/right/input/thumbstick/y"
|
||||
},
|
||||
scroll_horizontal: {
|
||||
left: "/user/hand/left/input/thumbstick/x",
|
||||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
show_hide: {
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
},
|
||||
@@ -246,6 +262,10 @@
|
||||
left: "/user/hand/left/input/thumbstick/y",
|
||||
right: "/user/hand/right/input/thumbstick/y"
|
||||
},
|
||||
scroll_horizontal: {
|
||||
left: "/user/hand/left/input/thumbstick/x",
|
||||
right: "/user/hand/right/input/thumbstick/x"
|
||||
},
|
||||
show_hide: {
|
||||
left: "/user/hand/left/input/menu/click",
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use glam::{Affine3A, Quat, Vec3A};
|
||||
use libmonado_rs::{Monado, Pose, ReferenceSpaceType};
|
||||
use libmonado::{Monado, Pose, ReferenceSpaceType};
|
||||
|
||||
use crate::{
|
||||
backend::{common::OverlayContainer, input::InputState},
|
||||
|
||||
+4
-4
@@ -146,24 +146,24 @@ impl OscSender {
|
||||
|
||||
// send device battery parameters
|
||||
self.send_message(
|
||||
format!("/avatar/parameters/{parameter}Battery").into(),
|
||||
format!("/avatar/parameters/{parameter}Battery"),
|
||||
vec![OscType::Float(level)],
|
||||
)?;
|
||||
self.send_message(
|
||||
format!("/avatar/parameters/{parameter}Charging").into(),
|
||||
format!("/avatar/parameters/{parameter}Charging"),
|
||||
vec![OscType::Bool(device.charging)],
|
||||
)?;
|
||||
}
|
||||
|
||||
// send average controller and tracker battery parameters
|
||||
self.send_message(
|
||||
format!("/avatar/parameters/averageControllerBattery").into(),
|
||||
String::from("/avatar/parameters/averageControllerBattery"),
|
||||
vec![OscType::Float(
|
||||
controller_total_bat / controller_count as f32,
|
||||
)],
|
||||
)?;
|
||||
self.send_message(
|
||||
format!("/avatar/parameters/averageTrackerBattery").into(),
|
||||
String::from("/avatar/parameters/averageTrackerBattery"),
|
||||
vec![OscType::Float(tracker_total_bat / tracker_count as f32)],
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -365,8 +365,8 @@ impl InteractionHandler for SplitOverlayBackend {
|
||||
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
||||
self.interaction.on_hover(app, hit)
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
|
||||
self.interaction.on_scroll(app, hit, delta);
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) {
|
||||
self.interaction.on_scroll(app, hit, delta_y, delta_x);
|
||||
}
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||
self.interaction.on_pointer(app, hit, pressed);
|
||||
|
||||
@@ -157,6 +157,13 @@ impl XdgShellHandler for Application {
|
||||
surface.send_configure();
|
||||
}
|
||||
|
||||
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
||||
if let Some(client) = surface.wl_surface().client() {
|
||||
self.wayvr_tasks
|
||||
.send(WayVRTask::DropToplevel(client.id(), surface.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
|
||||
// Handle popup creation here
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
|
||||
use super::{
|
||||
client::WayVRCompositor, comp::send_frames_surface_tree, egl_data, event_queue::SyncEventQueue,
|
||||
process, smithay_wrapper, time, window, WayVRSignal,
|
||||
process, smithay_wrapper, time, window, BlitMethod, WayVRSignal,
|
||||
};
|
||||
|
||||
fn generate_auth_key() -> String {
|
||||
@@ -71,7 +71,8 @@ pub struct Display {
|
||||
gles_texture: GlesTexture, // TODO: drop texture
|
||||
egl_image: khronos_egl::Image,
|
||||
egl_data: Rc<egl_data::EGLData>,
|
||||
pub dmabuf_data: egl_data::DMAbufData,
|
||||
|
||||
pub render_data: egl_data::RenderData,
|
||||
|
||||
pub tasks: SyncEventQueue<DisplayTask>,
|
||||
}
|
||||
@@ -87,6 +88,7 @@ impl Drop for Display {
|
||||
|
||||
pub struct DisplayInitParams<'a> {
|
||||
pub wm: Rc<RefCell<window::WindowManager>>,
|
||||
pub config: &'a super::Config,
|
||||
pub renderer: &'a mut GlesRenderer,
|
||||
pub egl_data: Rc<egl_data::EGLData>,
|
||||
pub wayland_env: super::WaylandEnv,
|
||||
@@ -128,7 +130,13 @@ impl Display {
|
||||
})?;
|
||||
|
||||
let egl_image = params.egl_data.create_egl_image(tex_id)?;
|
||||
let dmabuf_data = params.egl_data.create_dmabuf_data(&egl_image)?;
|
||||
|
||||
let render_data = match params.config.blit_method {
|
||||
BlitMethod::Dmabuf => {
|
||||
egl_data::RenderData::Dmabuf(params.egl_data.create_dmabuf_data(&egl_image)?)
|
||||
}
|
||||
BlitMethod::Software => egl_data::RenderData::Software(None),
|
||||
};
|
||||
|
||||
let opaque = false;
|
||||
let size = (params.width as i32, params.height as i32).into();
|
||||
@@ -145,7 +153,7 @@ impl Display {
|
||||
wayland_env: params.wayland_env,
|
||||
wm: params.wm,
|
||||
displayed_windows: Vec::new(),
|
||||
dmabuf_data,
|
||||
render_data,
|
||||
egl_image,
|
||||
gles_texture,
|
||||
last_pressed_time_ms: 0,
|
||||
@@ -183,6 +191,11 @@ impl Display {
|
||||
self.reposition_windows();
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self, window_handle: window::WindowHandle) {
|
||||
self.displayed_windows
|
||||
.retain(|disp| disp.window_handle != window_handle);
|
||||
}
|
||||
|
||||
pub fn reposition_windows(&mut self) {
|
||||
let window_count = self.displayed_windows.len();
|
||||
|
||||
@@ -287,12 +300,11 @@ impl Display {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick_render(&self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
|
||||
pub fn tick_render(&mut self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
|
||||
renderer.bind(self.gles_texture.clone())?;
|
||||
|
||||
let size = Size::from((self.width as i32, self.height as i32));
|
||||
let damage: Rectangle<i32, smithay::utils::Physical> =
|
||||
Rectangle::from_loc_and_size((0, 0), size);
|
||||
let damage: Rectangle<i32, smithay::utils::Physical> = Rectangle::from_size(size);
|
||||
|
||||
let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = self
|
||||
.displayed_windows
|
||||
@@ -336,6 +348,35 @@ impl Display {
|
||||
send_frames_surface_tree(window.toplevel.wl_surface(), time_ms as u32);
|
||||
}
|
||||
|
||||
if let egl_data::RenderData::Software(_) = &self.render_data {
|
||||
// Read OpenGL texture into memory. Slow!
|
||||
let pixel_data = renderer.with_context(|gl| unsafe {
|
||||
gl.BindTexture(ffi::TEXTURE_2D, self.gles_texture.tex_id());
|
||||
|
||||
let len = self.width as usize * self.height as usize * 4;
|
||||
let mut data: Box<[u8]> = Box::new_uninit_slice(len).assume_init();
|
||||
gl.ReadPixels(
|
||||
0,
|
||||
0,
|
||||
self.width as i32,
|
||||
self.height as i32,
|
||||
ffi::RGBA,
|
||||
ffi::UNSIGNED_BYTE,
|
||||
data.as_mut_ptr().cast(),
|
||||
);
|
||||
|
||||
let data: Arc<[u8]> = Arc::from(data);
|
||||
data
|
||||
})?;
|
||||
|
||||
self.render_data =
|
||||
egl_data::RenderData::Software(Some(egl_data::RenderSoftwarePixelsData {
|
||||
data: pixel_data,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -443,13 +484,11 @@ impl Display {
|
||||
if let Some(window) = wm.windows.get(&window_handle) {
|
||||
let surf = window.toplevel.wl_surface().clone();
|
||||
|
||||
if manager.seat_keyboard.current_focus().is_none() {
|
||||
manager.seat_keyboard.set_focus(
|
||||
&mut manager.state,
|
||||
Some(surf),
|
||||
manager.serial_counter.next_serial(),
|
||||
);
|
||||
}
|
||||
manager.seat_keyboard.set_focus(
|
||||
&mut manager.state,
|
||||
Some(surf),
|
||||
manager.serial_counter.next_serial(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,7 +519,7 @@ impl Display {
|
||||
manager.seat_pointer.frame(&mut manager.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(&self, manager: &mut WayVRCompositor, delta: f32) {
|
||||
pub fn send_mouse_scroll(&self, manager: &mut WayVRCompositor, delta_y: f32, delta_x: f32) {
|
||||
manager.seat_pointer.axis(
|
||||
&mut manager.state,
|
||||
input::pointer::AxisFrame {
|
||||
@@ -490,8 +529,8 @@ impl Display {
|
||||
smithay::backend::input::AxisRelativeDirection::Identical,
|
||||
),
|
||||
time: 0,
|
||||
axis: (0.0, -delta as f64),
|
||||
v120: Some((0, (delta * -120.0) as i32)),
|
||||
axis: (delta_x as f64, -delta_y as f64),
|
||||
v120: Some((0, (delta_y * -120.0) as i32)),
|
||||
stop: (false, false),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::egl_ex;
|
||||
use anyhow::anyhow;
|
||||
|
||||
@@ -23,13 +25,26 @@ pub struct DMAbufModifierInfo {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DMAbufData {
|
||||
pub struct RenderDMAbufData {
|
||||
pub fd: i32,
|
||||
pub stride: i32,
|
||||
pub offset: i32,
|
||||
pub mod_info: DMAbufModifierInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderSoftwarePixelsData {
|
||||
pub data: Arc<[u8]>,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderData {
|
||||
Dmabuf(RenderDMAbufData),
|
||||
Software(Option<RenderSoftwarePixelsData>), // will be set if the next image data is available
|
||||
}
|
||||
|
||||
impl EGLData {
|
||||
pub fn load_func(&self, func_name: &str) -> anyhow::Result<extern "system" fn()> {
|
||||
let raw_fn = self.egl.get_proc_address(func_name).ok_or(anyhow::anyhow!(
|
||||
@@ -207,7 +222,10 @@ impl EGLData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dmabuf_data(&self, egl_image: &khronos_egl::Image) -> anyhow::Result<DMAbufData> {
|
||||
pub fn create_dmabuf_data(
|
||||
&self,
|
||||
egl_image: &khronos_egl::Image,
|
||||
) -> anyhow::Result<RenderDMAbufData> {
|
||||
use egl_ex::PFNEGLEXPORTDMABUFIMAGEMESAPROC as FUNC;
|
||||
unsafe {
|
||||
let egl_export_dmabuf_image_mesa =
|
||||
@@ -235,7 +253,7 @@ impl EGLData {
|
||||
|
||||
let mod_info = self.query_dmabuf_mod_info()?;
|
||||
|
||||
Ok(DMAbufData {
|
||||
Ok(RenderDMAbufData {
|
||||
fd: fds[0],
|
||||
stride: strides[0],
|
||||
offset: offsets[0],
|
||||
|
||||
+105
-37
@@ -40,6 +40,8 @@ use std::{
|
||||
use time::get_millis;
|
||||
use wayvr_ipc::{packet_client, packet_server};
|
||||
|
||||
use crate::{hid::MODS_TO_KEYS, state::AppState};
|
||||
|
||||
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
|
||||
const STR_INVALID_HANDLE_PROCESS: &str = "Invalid process handle";
|
||||
|
||||
@@ -71,6 +73,7 @@ pub struct ExternalProcessRequest {
|
||||
#[derive(Clone)]
|
||||
pub enum WayVRTask {
|
||||
NewToplevel(ClientId, ToplevelSurface),
|
||||
DropToplevel(ClientId, ToplevelSurface),
|
||||
NewExternalProcess(ExternalProcessRequest),
|
||||
DropOverlay(super::overlay::OverlayID),
|
||||
ProcessTerminationRequest(process::ProcessHandle),
|
||||
@@ -87,11 +90,27 @@ pub enum WayVRSignal {
|
||||
BroadcastStateChanged(packet_server::WvrStateChanged),
|
||||
}
|
||||
|
||||
pub enum BlitMethod {
|
||||
Dmabuf,
|
||||
Software,
|
||||
}
|
||||
|
||||
impl BlitMethod {
|
||||
pub fn from_string(str: &str) -> Option<BlitMethod> {
|
||||
match str {
|
||||
"dmabuf" => Some(BlitMethod::Dmabuf),
|
||||
"software" => Some(BlitMethod::Software),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub click_freeze_time_ms: u32,
|
||||
pub keyboard_repeat_delay_ms: u32,
|
||||
pub keyboard_repeat_rate: u32,
|
||||
pub auto_hide_delay: Option<u32>, // if None, auto-hide is disabled
|
||||
pub blit_method: BlitMethod,
|
||||
}
|
||||
|
||||
pub struct WayVRState {
|
||||
@@ -101,12 +120,13 @@ pub struct WayVRState {
|
||||
wm: Rc<RefCell<window::WindowManager>>,
|
||||
egl_data: Rc<egl_data::EGLData>,
|
||||
pub processes: process::ProcessVec,
|
||||
config: Config,
|
||||
pub config: Config,
|
||||
dashboard_display: Option<display::DisplayHandle>,
|
||||
pub tasks: SyncEventQueue<WayVRTask>,
|
||||
pub signals: SyncEventQueue<WayVRSignal>,
|
||||
ticks: u64,
|
||||
pub pending_haptic: Option<super::input::Haptics>,
|
||||
cur_modifiers: u8,
|
||||
}
|
||||
|
||||
pub struct WayVR {
|
||||
@@ -185,12 +205,10 @@ impl WayVR {
|
||||
}
|
||||
Ok(None) => {
|
||||
log::warn!("dmabuf: Failed to query render node");
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("dmabuf: Failed to get egl device for display: {}", err);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
};
|
||||
@@ -248,12 +266,13 @@ impl WayVR {
|
||||
tasks,
|
||||
pending_haptic: None,
|
||||
signals: SyncEventQueue::new(),
|
||||
cur_modifiers: 0,
|
||||
};
|
||||
|
||||
Ok(Self { state, ipc_server })
|
||||
}
|
||||
|
||||
pub fn tick_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<()> {
|
||||
pub fn tick_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<bool> {
|
||||
// millis since the start of wayvr
|
||||
let display = self
|
||||
.state
|
||||
@@ -263,12 +282,12 @@ impl WayVR {
|
||||
|
||||
if !display.wants_redraw {
|
||||
// Nothing changed, do not render
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !display.visible {
|
||||
// Display is invisible, do not render
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let time_ms = get_millis() - self.state.time_start;
|
||||
@@ -276,15 +295,16 @@ impl WayVR {
|
||||
display.tick_render(&mut self.state.manager.state.gles_renderer, time_ms)?;
|
||||
display.wants_redraw = false;
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn tick_events(&mut self) -> anyhow::Result<Vec<TickTask>> {
|
||||
pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result<Vec<TickTask>> {
|
||||
let mut tasks: Vec<TickTask> = Vec::new();
|
||||
|
||||
self.ipc_server.tick(&mut server_ipc::TickParams {
|
||||
state: &mut self.state,
|
||||
tasks: &mut tasks,
|
||||
app,
|
||||
})?;
|
||||
|
||||
// Check for redraw events
|
||||
@@ -343,36 +363,63 @@ impl WayVR {
|
||||
WayVRTask::NewToplevel(client_id, toplevel) => {
|
||||
// Attach newly created toplevel surfaces to displays
|
||||
for client in &self.state.manager.clients {
|
||||
if client.client.id() == client_id {
|
||||
if let Some(process_handle) =
|
||||
process::find_by_pid(&self.state.processes, client.pid)
|
||||
{
|
||||
let window_handle = self
|
||||
.state
|
||||
.wm
|
||||
.borrow_mut()
|
||||
.create_window(client.display_handle, &toplevel);
|
||||
if client.client.id() != client_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(display) =
|
||||
self.state.displays.get_mut(&client.display_handle)
|
||||
{
|
||||
display.add_window(window_handle, process_handle, &toplevel);
|
||||
self.state.signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
packet_server::WvrStateChanged::WindowCreated,
|
||||
));
|
||||
} else {
|
||||
// This shouldn't happen, scream if it does
|
||||
log::error!("Could not attach window handle into display");
|
||||
}
|
||||
} else {
|
||||
log::error!(
|
||||
let Some(process_handle) =
|
||||
process::find_by_pid(&self.state.processes, client.pid)
|
||||
else {
|
||||
log::error!(
|
||||
"WayVR window creation failed: Unexpected process ID {}. It wasn't registered before.",
|
||||
client.pid
|
||||
);
|
||||
}
|
||||
continue;
|
||||
};
|
||||
|
||||
break;
|
||||
let window_handle = self
|
||||
.state
|
||||
.wm
|
||||
.borrow_mut()
|
||||
.create_window(client.display_handle, &toplevel);
|
||||
|
||||
let Some(display) = self.state.displays.get_mut(&client.display_handle)
|
||||
else {
|
||||
// This shouldn't happen, scream if it does
|
||||
log::error!("Could not attach window handle into display");
|
||||
continue;
|
||||
};
|
||||
|
||||
display.add_window(window_handle, process_handle, &toplevel);
|
||||
self.state.signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
packet_server::WvrStateChanged::WindowCreated,
|
||||
));
|
||||
}
|
||||
}
|
||||
WayVRTask::DropToplevel(client_id, toplevel) => {
|
||||
for client in &self.state.manager.clients {
|
||||
if client.client.id() != client_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut wm = self.state.wm.borrow_mut();
|
||||
let Some(window_handle) = wm.find_window_handle(&toplevel) else {
|
||||
log::warn!("DropToplevel: Couldn't find matching window handle");
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(display) = self.state.displays.get_mut(&client.display_handle)
|
||||
else {
|
||||
log::warn!("DropToplevel: Couldn't find matching display");
|
||||
continue;
|
||||
};
|
||||
|
||||
display.remove_window(window_handle);
|
||||
wm.remove_window(window_handle);
|
||||
|
||||
drop(wm);
|
||||
|
||||
display.reposition_windows();
|
||||
}
|
||||
}
|
||||
WayVRTask::ProcessTerminationRequest(process_handle) => {
|
||||
@@ -462,9 +509,14 @@ impl WayVRState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_mouse_scroll(&mut self, display: display::DisplayHandle, delta: f32) {
|
||||
pub fn send_mouse_scroll(
|
||||
&mut self,
|
||||
display: display::DisplayHandle,
|
||||
delta_y: f32,
|
||||
delta_x: f32,
|
||||
) {
|
||||
if let Some(display) = self.displays.get(&display) {
|
||||
display.send_mouse_scroll(&mut self.manager, delta);
|
||||
display.send_mouse_scroll(&mut self.manager, delta_y, delta_x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,6 +524,19 @@ impl WayVRState {
|
||||
self.manager.send_key(virtual_key, down);
|
||||
}
|
||||
|
||||
pub fn set_modifiers(&mut self, modifiers: u8) {
|
||||
let changed = self.cur_modifiers ^ modifiers;
|
||||
for i in 0..8 {
|
||||
let m = 1 << i;
|
||||
if changed & m != 0 {
|
||||
if let Some(vk) = MODS_TO_KEYS.get(m).into_iter().flatten().next() {
|
||||
self.send_key(*vk as u32, modifiers & m != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cur_modifiers = modifiers;
|
||||
}
|
||||
|
||||
pub fn set_display_visible(&mut self, display: display::DisplayHandle, visible: bool) {
|
||||
if let Some(display) = self.displays.get_mut(&display) {
|
||||
display.set_visible(visible);
|
||||
@@ -488,10 +553,13 @@ impl WayVRState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dmabuf_data(&self, display: display::DisplayHandle) -> Option<egl_data::DMAbufData> {
|
||||
pub fn get_render_data(
|
||||
&self,
|
||||
display: display::DisplayHandle,
|
||||
) -> Option<&egl_data::RenderData> {
|
||||
self.displays
|
||||
.get(&display)
|
||||
.map(|display| display.dmabuf_data.clone())
|
||||
.map(|display| &display.render_data)
|
||||
}
|
||||
|
||||
pub fn create_display(
|
||||
@@ -506,12 +574,12 @@ impl WayVRState {
|
||||
egl_data: self.egl_data.clone(),
|
||||
renderer: &mut self.manager.state.gles_renderer,
|
||||
wayland_env: self.manager.wayland_env.clone(),
|
||||
config: &self.config,
|
||||
width,
|
||||
height,
|
||||
name,
|
||||
primary,
|
||||
})?;
|
||||
|
||||
let handle = self.displays.add(display);
|
||||
|
||||
self.signals.send(WayVRSignal::BroadcastStateChanged(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, io::Read};
|
||||
|
||||
use wayvr_ipc::packet_server;
|
||||
|
||||
@@ -88,6 +88,23 @@ impl Drop for WayVRProcess {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_process_env_value(pid: i32, key: &str) -> anyhow::Result<Option<String>> {
|
||||
let path = format!("/proc/{}/environ", pid);
|
||||
let mut env_data = String::new();
|
||||
std::fs::File::open(path)?.read_to_string(&mut env_data)?;
|
||||
let lines: Vec<&str> = env_data.split('\0').filter(|s| !s.is_empty()).collect();
|
||||
|
||||
for line in lines {
|
||||
if let Some(cell) = line.split_once('=') {
|
||||
if cell.0 == key {
|
||||
return Ok(Some(String::from(cell.1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
impl WayVRProcess {
|
||||
fn is_running(&mut self) -> bool {
|
||||
match self.child.try_wait() {
|
||||
@@ -153,22 +170,42 @@ impl ExternalProcess {
|
||||
gen_id!(ProcessVec, Process, ProcessCell, ProcessHandle);
|
||||
|
||||
pub fn find_by_pid(processes: &ProcessVec, pid: u32) -> Option<ProcessHandle> {
|
||||
log::debug!("Finding process with PID {}", pid);
|
||||
|
||||
for (idx, cell) in processes.vec.iter().enumerate() {
|
||||
if let Some(cell) = cell {
|
||||
match &cell.obj {
|
||||
Process::Managed(wayvr_process) => {
|
||||
if wayvr_process.child.id() == pid {
|
||||
return Some(ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
let Some(cell) = cell else {
|
||||
continue;
|
||||
};
|
||||
match &cell.obj {
|
||||
Process::Managed(wayvr_process) => {
|
||||
if wayvr_process.child.id() == pid {
|
||||
return Some(ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
Process::External(external_process) => {
|
||||
if external_process.pid == pid {
|
||||
return Some(ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
Process::External(external_process) => {
|
||||
if external_process.pid == pid {
|
||||
return Some(ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Finding by PID failed, trying WAYVR_DISPLAY_AUTH...");
|
||||
|
||||
if let Ok(Some(value)) = get_process_env_value(pid as i32, "WAYVR_DISPLAY_AUTH") {
|
||||
for (idx, cell) in processes.vec.iter().enumerate() {
|
||||
let Some(cell) = cell else {
|
||||
continue;
|
||||
};
|
||||
if let Process::Managed(wayvr_process) = &cell.obj {
|
||||
if wayvr_process.auth_key == value {
|
||||
return Some(ProcessVec::get_handle(cell, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Process find with PID {} failed", pid);
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::{display, process, window, TickTask, WayVRSignal};
|
||||
use bytes::BufMut;
|
||||
use glam::Vec3A;
|
||||
use interprocess::local_socket::{self, traits::Listener, ToNsName};
|
||||
use smallvec::SmallVec;
|
||||
use std::io::{Read, Write};
|
||||
use wayvr_ipc::{
|
||||
ipc::{self},
|
||||
packet_client::{self, PacketClient},
|
||||
packet_server::{self, PacketServer},
|
||||
packet_server::{self, PacketServer, WlxInputStatePointer},
|
||||
};
|
||||
|
||||
pub struct AuthInfo {
|
||||
@@ -70,6 +73,7 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option<Payload> {
|
||||
pub struct TickParams<'a> {
|
||||
pub state: &'a mut super::WayVRState,
|
||||
pub tasks: &'a mut Vec<TickTask>,
|
||||
pub app: &'a AppState,
|
||||
}
|
||||
|
||||
pub fn gen_args_vec(input: &str) -> Vec<&str> {
|
||||
@@ -177,6 +181,34 @@ impl Connection {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_wlx_input_state(
|
||||
&mut self,
|
||||
params: &TickParams,
|
||||
serial: ipc::Serial,
|
||||
) -> anyhow::Result<()> {
|
||||
let input_state = ¶ms.app.input_state;
|
||||
|
||||
let to_arr = |vec: &Vec3A| -> [f32; 3] { [vec.x, vec.y, vec.z] };
|
||||
|
||||
send_packet(
|
||||
&mut self.conn,
|
||||
&ipc::data_encode(&PacketServer::WlxInputStateResponse(
|
||||
serial,
|
||||
packet_server::WlxInputState {
|
||||
hmd_pos: to_arr(&input_state.hmd.translation),
|
||||
left: WlxInputStatePointer {
|
||||
pos: to_arr(&input_state.pointers[0].raw_pose.translation),
|
||||
},
|
||||
right: WlxInputStatePointer {
|
||||
pos: to_arr(&input_state.pointers[0].raw_pose.translation),
|
||||
},
|
||||
},
|
||||
)),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_wvr_display_create(
|
||||
&mut self,
|
||||
params: &mut TickParams,
|
||||
@@ -471,6 +503,9 @@ impl Connection {
|
||||
|
||||
match packet {
|
||||
PacketClient::Handshake(_) => unreachable!(), // handled previously
|
||||
PacketClient::WlxInputState(serial) => {
|
||||
self.handle_wlx_input_state(params, serial)?;
|
||||
}
|
||||
PacketClient::WvrDisplayList(serial) => {
|
||||
self.handle_wvr_display_list(params, serial)?;
|
||||
}
|
||||
@@ -633,10 +668,11 @@ impl WayVRServer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn broadcast(&mut self, packet: packet_server::PacketServer) -> anyhow::Result<()> {
|
||||
pub fn broadcast(&mut self, packet: packet_server::PacketServer) {
|
||||
for connection in &mut self.connections {
|
||||
send_packet(&mut connection.conn, &ipc::data_encode(&packet))?;
|
||||
if let Err(e) = send_packet(&mut connection.conn, &ipc::data_encode(&packet)) {
|
||||
log::error!("failed to broadcast packet: {:?}", e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,53 +2,53 @@ use super::egl_data;
|
||||
use smithay::backend::{egl as smithay_egl, renderer::gles::ffi};
|
||||
|
||||
pub fn get_egl_display(data: &egl_data::EGLData) -> anyhow::Result<smithay_egl::EGLDisplay> {
|
||||
Ok(unsafe { smithay_egl::EGLDisplay::from_raw(data.display.as_ptr(), data.config.as_ptr())? })
|
||||
Ok(unsafe { smithay_egl::EGLDisplay::from_raw(data.display.as_ptr(), data.config.as_ptr())? })
|
||||
}
|
||||
|
||||
pub fn get_egl_context(
|
||||
data: &egl_data::EGLData,
|
||||
display: &smithay_egl::EGLDisplay,
|
||||
data: &egl_data::EGLData,
|
||||
display: &smithay_egl::EGLDisplay,
|
||||
) -> anyhow::Result<smithay_egl::EGLContext> {
|
||||
let display_ptr = display.get_display_handle().handle;
|
||||
debug_assert!(display_ptr == data.display.as_ptr());
|
||||
let config_ptr = data.config.as_ptr();
|
||||
let context_ptr = data.context.as_ptr();
|
||||
Ok(unsafe { smithay_egl::EGLContext::from_raw(display_ptr, config_ptr, context_ptr)? })
|
||||
let display_ptr = display.get_display_handle().handle;
|
||||
debug_assert!(display_ptr == data.display.as_ptr());
|
||||
let config_ptr = data.config.as_ptr();
|
||||
let context_ptr = data.context.as_ptr();
|
||||
Ok(unsafe { smithay_egl::EGLContext::from_raw(display_ptr, config_ptr, context_ptr)? })
|
||||
}
|
||||
|
||||
pub fn create_framebuffer_texture(
|
||||
gl: &ffi::Gles2,
|
||||
width: u32,
|
||||
height: u32,
|
||||
tex_format: u32,
|
||||
internal_format: u32,
|
||||
gl: &ffi::Gles2,
|
||||
width: u32,
|
||||
height: u32,
|
||||
tex_format: u32,
|
||||
internal_format: u32,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let mut tex = 0;
|
||||
gl.GenTextures(1, &mut tex);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, tex);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_MIN_FILTER,
|
||||
ffi::NEAREST as i32,
|
||||
);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_MAG_FILTER,
|
||||
ffi::NEAREST as i32,
|
||||
);
|
||||
gl.TexImage2D(
|
||||
ffi::TEXTURE_2D,
|
||||
0,
|
||||
internal_format as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
tex_format,
|
||||
ffi::UNSIGNED_BYTE,
|
||||
std::ptr::null(),
|
||||
);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, 0);
|
||||
tex
|
||||
}
|
||||
unsafe {
|
||||
let mut tex = 0;
|
||||
gl.GenTextures(1, &mut tex);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, tex);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_MIN_FILTER,
|
||||
ffi::NEAREST as i32,
|
||||
);
|
||||
gl.TexParameteri(
|
||||
ffi::TEXTURE_2D,
|
||||
ffi::TEXTURE_MAG_FILTER,
|
||||
ffi::NEAREST as i32,
|
||||
);
|
||||
gl.TexImage2D(
|
||||
ffi::TEXTURE_2D,
|
||||
0,
|
||||
internal_format as i32,
|
||||
width as i32,
|
||||
height as i32,
|
||||
0,
|
||||
tex_format,
|
||||
ffi::UNSIGNED_BYTE,
|
||||
std::ptr::null(),
|
||||
);
|
||||
gl.BindTexture(ffi::TEXTURE_2D, 0);
|
||||
tex
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
// Returns milliseconds since unix epoch
|
||||
pub fn get_millis() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u64
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u64
|
||||
}
|
||||
@@ -77,6 +77,10 @@ impl WindowManager {
|
||||
) -> WindowHandle {
|
||||
self.windows.add(Window::new(display_handle, toplevel))
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self, window_handle: WindowHandle) {
|
||||
self.windows.remove(&window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
gen_id!(WindowVec, Window, WindowCell, WindowHandle);
|
||||
|
||||
@@ -207,6 +207,9 @@ pub struct GeneralConfig {
|
||||
#[serde(default = "def_toast_topics")]
|
||||
pub notification_topics: IdMap<ToastTopic, DisplayMethod>,
|
||||
|
||||
#[serde(default = "def_empty")]
|
||||
pub notification_sound: Arc<str>,
|
||||
|
||||
#[serde(default = "def_true")]
|
||||
pub keyboard_sound_enabled: bool,
|
||||
|
||||
|
||||
+15
-15
@@ -112,8 +112,8 @@ fn def_keyboard_repeat_rate() -> u32 {
|
||||
50
|
||||
}
|
||||
|
||||
fn def_version() -> u32 {
|
||||
1
|
||||
fn def_blit_method() -> String {
|
||||
String::from("dmabuf")
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
@@ -125,8 +125,6 @@ pub struct WayVRDashboard {
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct WayVRConfig {
|
||||
#[serde(default = "def_version")]
|
||||
pub version: u32,
|
||||
#[serde(default = "def_false")]
|
||||
pub run_compositor_at_start: bool,
|
||||
|
||||
@@ -150,6 +148,9 @@ pub struct WayVRConfig {
|
||||
|
||||
#[serde(default = "def_keyboard_repeat_rate")]
|
||||
pub keyboard_repeat_rate: u32,
|
||||
|
||||
#[serde(default = "def_blit_method")]
|
||||
pub blit_method: String,
|
||||
}
|
||||
|
||||
impl WayVRConfig {
|
||||
@@ -173,17 +174,19 @@ impl WayVRConfig {
|
||||
pub fn get_wayvr_config(
|
||||
config_general: &crate::config::GeneralConfig,
|
||||
config_wayvr: &crate::config_wayvr::WayVRConfig,
|
||||
) -> wayvr::Config {
|
||||
wayvr::Config {
|
||||
) -> anyhow::Result<wayvr::Config> {
|
||||
Ok(wayvr::Config {
|
||||
click_freeze_time_ms: config_general.click_freeze_time_ms,
|
||||
keyboard_repeat_delay_ms: config_wayvr.keyboard_repeat_delay,
|
||||
keyboard_repeat_rate: config_wayvr.keyboard_repeat_rate,
|
||||
blit_method: wayvr::BlitMethod::from_string(&config_wayvr.blit_method)
|
||||
.ok_or(anyhow::anyhow!("Unknown blit method"))?,
|
||||
auto_hide_delay: if config_wayvr.auto_hide {
|
||||
Some(config_wayvr.auto_hide_delay)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn post_load(
|
||||
@@ -200,7 +203,9 @@ impl WayVRConfig {
|
||||
if primary_count > 1 {
|
||||
anyhow::bail!("Number of primary displays is more than 1")
|
||||
} else if primary_count == 0 {
|
||||
log::warn!("No primary display specified");
|
||||
log::warn!(
|
||||
"No primary display specified. External Wayland applications will not be attached."
|
||||
);
|
||||
}
|
||||
|
||||
for (catalog_name, catalog) in &self.catalogs {
|
||||
@@ -219,7 +224,7 @@ impl WayVRConfig {
|
||||
if self.run_compositor_at_start {
|
||||
// Start Wayland server instantly
|
||||
Ok(Some(Rc::new(RefCell::new(WayVRData::new(
|
||||
Self::get_wayvr_config(config, self),
|
||||
Self::get_wayvr_config(config, self)?,
|
||||
)?))))
|
||||
} else {
|
||||
// Lazy-init WayVR later if the user requested
|
||||
@@ -236,10 +241,5 @@ pub fn load_wayvr() -> WayVRConfig {
|
||||
config_io::ConfigRoot::WayVR.get_conf_d_path()
|
||||
);
|
||||
|
||||
let config = load_config_with_conf_d::<WayVRConfig>("wayvr.yaml", config_io::ConfigRoot::WayVR);
|
||||
|
||||
if config.version != def_version() {
|
||||
panic!("WayVR config version {} is not supported", config.version);
|
||||
}
|
||||
config
|
||||
load_config_with_conf_d::<WayVRConfig>("wayvr.yaml", config_io::ConfigRoot::WayVR)
|
||||
}
|
||||
+5
-4
@@ -802,10 +802,9 @@ impl WlxGraphics {
|
||||
frame: DmabufFrame,
|
||||
tiling: ImageTiling,
|
||||
layouts: Vec<SubresourceLayout>,
|
||||
modifiers: Vec<u64>,
|
||||
modifiers: &[u64],
|
||||
) -> anyhow::Result<Arc<Image>> {
|
||||
let extent = [frame.format.width, frame.format.height, 1];
|
||||
|
||||
let format = fourcc_to_vk(frame.format.fourcc)?;
|
||||
|
||||
let image = unsafe {
|
||||
@@ -817,7 +816,7 @@ impl WlxGraphics {
|
||||
usage: ImageUsage::SAMPLED,
|
||||
external_memory_handle_types: ExternalMemoryHandleTypes::DMA_BUF,
|
||||
tiling,
|
||||
drm_format_modifiers: modifiers,
|
||||
drm_format_modifiers: modifiers.to_owned(),
|
||||
drm_format_modifier_plane_layouts: layouts,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -894,7 +893,7 @@ impl WlxGraphics {
|
||||
tiling = ImageTiling::DrmFormatModifier;
|
||||
};
|
||||
|
||||
self.dmabuf_texture_ex(frame, tiling, layouts, modifiers)
|
||||
self.dmabuf_texture_ex(frame, tiling, layouts, &modifiers)
|
||||
}
|
||||
|
||||
pub fn render_texture(
|
||||
@@ -942,6 +941,7 @@ impl WlxGraphics {
|
||||
)?))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_pipeline_with_layouts(
|
||||
self: &Arc<Self>,
|
||||
render_target: Arc<ImageView>,
|
||||
@@ -1304,6 +1304,7 @@ impl WlxPipeline<WlxPipelineLegacy> {
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new_with_layout(
|
||||
render_target: Arc<ImageView>,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
|
||||
@@ -153,6 +153,7 @@ impl<D, S> CanvasBuilder<D, S> {
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn key_button(
|
||||
&mut self,
|
||||
x: f32,
|
||||
|
||||
@@ -19,6 +19,7 @@ pub type ControlRendererHl<D, S> = fn(
|
||||
Vec4,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) struct Control<D, S> {
|
||||
pub state: Option<S>,
|
||||
pub rect: Rect,
|
||||
@@ -34,7 +35,7 @@ pub(crate) struct Control<D, S> {
|
||||
pub on_update: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_press: Option<fn(&mut Self, &mut D, &mut AppState, PointerMode)>,
|
||||
pub on_release: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_scroll: Option<fn(&mut Self, &mut D, &mut AppState, f32)>,
|
||||
pub on_scroll: Option<fn(&mut Self, &mut D, &mut AppState, f32, f32)>,
|
||||
pub test_highlight: Option<fn(&Self, &mut D, &mut AppState) -> Option<Vec4>>,
|
||||
|
||||
pub(super) on_render_bg: Option<ControlRenderer<D, S>>,
|
||||
|
||||
@@ -267,13 +267,13 @@ impl<D, S> InteractionHandler for Canvas<D, S> {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta_y: f32, delta_x: f32) {
|
||||
let idx = self.hover_controls[hit.pointer];
|
||||
|
||||
if let Some(idx) = idx {
|
||||
let c = &mut self.controls[idx];
|
||||
if let Some(ref mut f) = c.on_scroll {
|
||||
f(c, &mut self.canvas.data, app, delta);
|
||||
f(c, &mut self.canvas.data, app, delta_y, delta_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,13 +326,19 @@ fn modular_button_up(button: &mut ModularControl, _: &mut (), app: &mut AppState
|
||||
}
|
||||
}
|
||||
|
||||
fn modular_button_scroll(button: &mut ModularControl, _: &mut (), app: &mut AppState, delta: f32) {
|
||||
fn modular_button_scroll(
|
||||
button: &mut ModularControl,
|
||||
_: &mut (),
|
||||
app: &mut AppState,
|
||||
delta_y: f32,
|
||||
_delta_x: f32,
|
||||
) {
|
||||
// want panic
|
||||
let ModularData::Button(data) = button.state.as_mut().unwrap() else {
|
||||
panic!("modular_button_scroll: button state is not Button");
|
||||
};
|
||||
|
||||
let actions = if delta < 0.0 {
|
||||
let actions = if delta_y < 0.0 {
|
||||
data.scroll_down.as_ref()
|
||||
} else {
|
||||
data.scroll_up.as_ref()
|
||||
|
||||
+97
-91
@@ -1,4 +1,4 @@
|
||||
use glam::Vec2;
|
||||
use glam::{IVec2, Vec2};
|
||||
use idmap::{idmap, IdMap};
|
||||
use idmap_derive::IntegerId;
|
||||
use input_linux::{
|
||||
@@ -46,7 +46,7 @@ pub fn initialize() -> Box<dyn HidProvider> {
|
||||
pub trait HidProvider {
|
||||
fn mouse_move(&mut self, pos: Vec2);
|
||||
fn send_button(&mut self, button: u16, down: bool);
|
||||
fn wheel(&mut self, delta: i32);
|
||||
fn wheel(&mut self, delta_y: i32, delta_x: i32);
|
||||
fn set_modifiers(&mut self, mods: u8);
|
||||
fn send_key(&self, key: VirtualKey, down: bool);
|
||||
fn set_desktop_extent(&mut self, extent: Vec2);
|
||||
@@ -64,11 +64,12 @@ struct MouseAction {
|
||||
last_requested_pos: Option<Vec2>,
|
||||
pos: Option<Vec2>,
|
||||
button: Option<MouseButtonAction>,
|
||||
scroll: Option<i32>,
|
||||
scroll: Option<IVec2>,
|
||||
}
|
||||
|
||||
pub struct UInputProvider {
|
||||
handle: UInputHandle<File>,
|
||||
keyboard_handle: UInputHandle<File>,
|
||||
mouse_handle: UInputHandle<File>,
|
||||
desktop_extent: Vec2,
|
||||
desktop_origin: Vec2,
|
||||
cur_modifiers: u8,
|
||||
@@ -90,97 +91,94 @@ const EV_ABS: u16 = 0x3;
|
||||
|
||||
impl UInputProvider {
|
||||
fn try_new() -> Option<Self> {
|
||||
if let Ok(file) = File::create("/dev/uinput") {
|
||||
let handle = UInputHandle::new(file);
|
||||
let keyboard_file = File::create("/dev/uinput").ok()?;
|
||||
let keyboard_handle = UInputHandle::new(keyboard_file);
|
||||
|
||||
let id = InputId {
|
||||
bustype: 0x03,
|
||||
vendor: 0x4711,
|
||||
product: 0x0829,
|
||||
version: 5,
|
||||
};
|
||||
let mouse_file = File::create("/dev/uinput").ok()?;
|
||||
let mouse_handle = UInputHandle::new(mouse_file);
|
||||
|
||||
let name = b"WlxOverlay-S Keyboard-Mouse Hybrid Thing\0";
|
||||
let kbd_id = InputId {
|
||||
bustype: 0x03,
|
||||
vendor: 0x4711,
|
||||
product: 0x0829,
|
||||
version: 5,
|
||||
};
|
||||
let mouse_id = InputId {
|
||||
bustype: 0x03,
|
||||
vendor: 0x4711,
|
||||
product: 0x0830,
|
||||
version: 5,
|
||||
};
|
||||
let kbd_name = b"WlxOverlay-S Keyboard\0";
|
||||
let mouse_name = b"WlxOverlay-S Mouse\0";
|
||||
|
||||
let abs_info = vec![
|
||||
AbsoluteInfoSetup {
|
||||
axis: input_linux::AbsoluteAxis::X,
|
||||
info: AbsoluteInfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: MOUSE_EXTENT as _,
|
||||
fuzz: 0,
|
||||
flat: 0,
|
||||
resolution: 10,
|
||||
},
|
||||
let abs_info = vec![
|
||||
AbsoluteInfoSetup {
|
||||
axis: input_linux::AbsoluteAxis::X,
|
||||
info: AbsoluteInfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: MOUSE_EXTENT as _,
|
||||
fuzz: 0,
|
||||
flat: 0,
|
||||
resolution: 10,
|
||||
},
|
||||
AbsoluteInfoSetup {
|
||||
axis: input_linux::AbsoluteAxis::Y,
|
||||
info: AbsoluteInfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: MOUSE_EXTENT as _,
|
||||
fuzz: 0,
|
||||
flat: 0,
|
||||
resolution: 10,
|
||||
},
|
||||
},
|
||||
AbsoluteInfoSetup {
|
||||
axis: input_linux::AbsoluteAxis::Y,
|
||||
info: AbsoluteInfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: MOUSE_EXTENT as _,
|
||||
fuzz: 0,
|
||||
flat: 0,
|
||||
resolution: 10,
|
||||
},
|
||||
];
|
||||
},
|
||||
];
|
||||
|
||||
if handle.set_evbit(EventKind::Key).is_err() {
|
||||
return None;
|
||||
}
|
||||
if handle.set_evbit(EventKind::Absolute).is_err() {
|
||||
return None;
|
||||
}
|
||||
if handle.set_evbit(EventKind::Relative).is_err() {
|
||||
return None;
|
||||
}
|
||||
|
||||
for btn in MOUSE_LEFT..=MOUSE_MIDDLE {
|
||||
let key: Key = unsafe { transmute(btn) };
|
||||
if handle.set_keybit(key).is_err() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
for key in VirtualKey::iter() {
|
||||
let key: Key = unsafe { transmute((key as u16) - 8) };
|
||||
if handle.set_keybit(key).is_err() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if handle.set_absbit(AbsoluteAxis::X).is_err() {
|
||||
return None;
|
||||
}
|
||||
if handle.set_absbit(AbsoluteAxis::Y).is_err() {
|
||||
return None;
|
||||
}
|
||||
if handle.set_relbit(RelativeAxis::Wheel).is_err() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if handle.create(&id, name, 0, &abs_info).is_ok() {
|
||||
return Some(UInputProvider {
|
||||
handle,
|
||||
desktop_extent: Vec2::ZERO,
|
||||
desktop_origin: Vec2::ZERO,
|
||||
current_action: Default::default(),
|
||||
cur_modifiers: 0,
|
||||
});
|
||||
}
|
||||
keyboard_handle.set_evbit(EventKind::Key).ok()?;
|
||||
for key in VirtualKey::iter() {
|
||||
let mapped_key: Key = unsafe { std::mem::transmute((key as u16) - 8) };
|
||||
keyboard_handle.set_keybit(mapped_key).ok()?;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
keyboard_handle.create(&kbd_id, kbd_name, 0, &[]).ok()?;
|
||||
|
||||
mouse_handle.set_evbit(EventKind::Absolute).ok()?;
|
||||
mouse_handle.set_evbit(EventKind::Relative).ok()?;
|
||||
mouse_handle.set_absbit(AbsoluteAxis::X).ok()?;
|
||||
mouse_handle.set_absbit(AbsoluteAxis::Y).ok()?;
|
||||
mouse_handle.set_relbit(RelativeAxis::WheelHiRes).ok()?;
|
||||
mouse_handle
|
||||
.set_relbit(RelativeAxis::HorizontalWheelHiRes)
|
||||
.ok()?;
|
||||
mouse_handle.set_evbit(EventKind::Key).ok()?;
|
||||
|
||||
for btn in MOUSE_LEFT..=MOUSE_MIDDLE {
|
||||
let mouse_btn: Key = unsafe { transmute(btn) };
|
||||
mouse_handle.set_keybit(mouse_btn).ok()?;
|
||||
}
|
||||
mouse_handle
|
||||
.create(&mouse_id, mouse_name, 0, &abs_info)
|
||||
.ok()?;
|
||||
|
||||
Some(UInputProvider {
|
||||
keyboard_handle,
|
||||
mouse_handle,
|
||||
desktop_extent: Vec2::ZERO,
|
||||
desktop_origin: Vec2::ZERO,
|
||||
current_action: Default::default(),
|
||||
cur_modifiers: 0,
|
||||
})
|
||||
}
|
||||
fn send_button_internal(&self, button: u16, down: bool) {
|
||||
let time = get_time();
|
||||
let events = [
|
||||
new_event(time, EV_KEY, button, down as _),
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.handle.write(&events) {
|
||||
if let Err(res) = self.mouse_handle.write(&events) {
|
||||
log::error!("send_button: {}", res.to_string());
|
||||
}
|
||||
}
|
||||
@@ -196,17 +194,23 @@ impl UInputProvider {
|
||||
new_event(time, EV_ABS, AbsoluteAxis::Y as _, pos.y as i32),
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.handle.write(&events) {
|
||||
if let Err(res) = self.mouse_handle.write(&events) {
|
||||
log::error!("{}", res.to_string());
|
||||
}
|
||||
}
|
||||
fn wheel_internal(&self, delta: i32) {
|
||||
fn wheel_internal(&self, delta_y: i32, delta_x: i32) {
|
||||
let time = get_time();
|
||||
let events = [
|
||||
new_event(time, EV_REL, RelativeAxis::Wheel as _, delta),
|
||||
new_event(time, EV_REL, RelativeAxis::WheelHiRes as _, delta_y),
|
||||
new_event(
|
||||
time,
|
||||
EV_REL,
|
||||
RelativeAxis::HorizontalWheelHiRes as _,
|
||||
delta_x,
|
||||
),
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.handle.write(&events) {
|
||||
if let Err(res) = self.mouse_handle.write(&events) {
|
||||
log::error!("wheel: {}", res.to_string());
|
||||
}
|
||||
}
|
||||
@@ -226,13 +230,15 @@ impl HidProvider for UInputProvider {
|
||||
self.cur_modifiers = modifiers;
|
||||
}
|
||||
fn send_key(&self, key: VirtualKey, down: bool) {
|
||||
log::debug!("send_key: {:?} {}", key, down);
|
||||
#[cfg(debug_assertions)]
|
||||
log::trace!("send_key: {:?} {}", key, down);
|
||||
|
||||
let time = get_time();
|
||||
let events = [
|
||||
new_event(time, EV_KEY, (key as u16) - 8, down as _),
|
||||
new_event(time, EV_SYN, 0, 0),
|
||||
];
|
||||
if let Err(res) = self.handle.write(&events) {
|
||||
if let Err(res) = self.keyboard_handle.write(&events) {
|
||||
log::error!("send_key: {}", res.to_string());
|
||||
}
|
||||
}
|
||||
@@ -254,9 +260,9 @@ impl HidProvider for UInputProvider {
|
||||
self.current_action.pos = self.current_action.last_requested_pos;
|
||||
}
|
||||
}
|
||||
fn wheel(&mut self, delta: i32) {
|
||||
fn wheel(&mut self, delta_y: i32, delta_x: i32) {
|
||||
if self.current_action.scroll.is_none() {
|
||||
self.current_action.scroll = Some(delta);
|
||||
self.current_action.scroll = Some(IVec2::new(delta_x, delta_y));
|
||||
// Pass mouse motion events only if not scrolling
|
||||
// (allows scrolling on all Chromium-based applications)
|
||||
self.current_action.pos = None;
|
||||
@@ -270,7 +276,7 @@ impl HidProvider for UInputProvider {
|
||||
self.send_button_internal(button.button, button.down);
|
||||
}
|
||||
if let Some(scroll) = self.current_action.scroll.take() {
|
||||
self.wheel_internal(scroll);
|
||||
self.wheel_internal(scroll.y, scroll.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,7 +284,7 @@ impl HidProvider for UInputProvider {
|
||||
impl HidProvider for DummyProvider {
|
||||
fn mouse_move(&mut self, _pos: Vec2) {}
|
||||
fn send_button(&mut self, _button: u16, _down: bool) {}
|
||||
fn wheel(&mut self, _delta: i32) {}
|
||||
fn wheel(&mut self, _delta_y: i32, _delta_x: i32) {}
|
||||
fn set_modifiers(&mut self, _modifiers: u8) {}
|
||||
fn send_key(&self, _key: VirtualKey, _down: bool) {}
|
||||
fn set_desktop_extent(&mut self, _extent: Vec2) {}
|
||||
|
||||
+45
-33
@@ -22,6 +22,8 @@ use std::{
|
||||
|
||||
use clap::Parser;
|
||||
use sysinfo::Pid;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
|
||||
/// The lightweight desktop overlay for OpenVR and OpenXR
|
||||
#[derive(Default, Parser, Debug)]
|
||||
@@ -171,51 +173,61 @@ fn args_get_openxr(_args: &Args) -> bool {
|
||||
}
|
||||
|
||||
fn logging_init(args: &mut Args) -> anyhow::Result<()> {
|
||||
let log_file = args
|
||||
let log_file_path = args
|
||||
.log_to
|
||||
.take()
|
||||
.or_else(|| std::env::var("WLX_LOGFILE").ok())
|
||||
.or_else(|| Some("/tmp/wlx.log".to_string()));
|
||||
.unwrap_or(String::from("/tmp/wlx.log"));
|
||||
|
||||
if let Some(log_to) = log_file.filter(|s| !s.is_empty()) {
|
||||
if let Err(e) = file_logging_init(&log_to) {
|
||||
log::error!("Failed to initialize file logging: {}", e);
|
||||
env_logger::init();
|
||||
let file_writer = match std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&log_file_path)
|
||||
{
|
||||
Ok(file) => {
|
||||
println!("Logging to {}", &log_file_path);
|
||||
Some(file)
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to open log file (path: {:?}): {}", e, log_file_path);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let registry = tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.pretty()
|
||||
.with_writer(std::io::stderr),
|
||||
)
|
||||
.with(
|
||||
/* read RUST_LOG env var */
|
||||
EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy()
|
||||
.add_directive("zbus=warn".parse().unwrap())
|
||||
.add_directive("wlx_capture::wayland=info".parse().unwrap())
|
||||
.add_directive("smithay=debug".parse().unwrap()), /* GLES render spam */
|
||||
);
|
||||
|
||||
if let Some(writer) = file_writer {
|
||||
registry
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.with_file(true)
|
||||
.with_line_number(true)
|
||||
.with_writer(writer)
|
||||
.with_ansi(false),
|
||||
)
|
||||
.init();
|
||||
} else {
|
||||
env_logger::init();
|
||||
registry.init();
|
||||
}
|
||||
|
||||
log_panics::init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_logging_init(log_to: &str) -> anyhow::Result<()> {
|
||||
use std::io::Write;
|
||||
let target = Box::new(std::fs::File::create(log_to)?);
|
||||
|
||||
env_logger::Builder::new()
|
||||
.target(env_logger::Target::Pipe(target))
|
||||
.filter(None, log::LevelFilter::Info)
|
||||
.filter_module("zbus", log::LevelFilter::Warn)
|
||||
.parse_default_env()
|
||||
.format(|buf, record| {
|
||||
eprintln!("[{}] {}", record.level(), record.args());
|
||||
writeln!(
|
||||
buf,
|
||||
"[{} {} {}] {}",
|
||||
chrono::Local::now().format("%H:%M:%S%.3f"),
|
||||
record.level(),
|
||||
record.module_path().unwrap_or_default(),
|
||||
record.args()
|
||||
)
|
||||
})
|
||||
.init();
|
||||
|
||||
println!("Logging to: {}", log_to);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_single_instance(replace: bool) -> bool {
|
||||
let mut path = std::env::var("XDG_RUNTIME_DIR")
|
||||
.map(PathBuf::from)
|
||||
|
||||
@@ -51,7 +51,13 @@ fn set_modifiers(app: &mut AppState, mods: u8) {
|
||||
KeyboardFocus::PhysicalScreen => {
|
||||
app.hid_provider.set_modifiers(mods);
|
||||
}
|
||||
KeyboardFocus::WayVR => {}
|
||||
KeyboardFocus::WayVR =>
|
||||
{
|
||||
#[cfg(feature = "wayvr")]
|
||||
if let Some(wayvr) = &app.wayvr {
|
||||
wayvr.borrow_mut().data.state.set_modifiers(mods);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +101,7 @@ where
|
||||
canvas.fg_color = color_parse("#cad3f5").unwrap(); //safe
|
||||
canvas.bg_color = color_parse("#1e2030").unwrap(); //safe
|
||||
|
||||
let has_altgr = keymap.as_ref().map_or(false, |k| k.has_altgr());
|
||||
let has_altgr = keymap.as_ref().is_some_and(|k| k.has_altgr());
|
||||
|
||||
if !LAYOUT.auto_labels.unwrap_or(true) {
|
||||
keymap = None;
|
||||
@@ -125,7 +131,7 @@ where
|
||||
let label0 = keymap.label_for_key(vk, 0);
|
||||
let label1 = keymap.label_for_key(vk, SHIFT);
|
||||
|
||||
if label0.chars().next().map_or(false, |f| f.is_alphabetic()) {
|
||||
if label0.chars().next().is_some_and(|f| f.is_alphabetic()) {
|
||||
label.push(label1);
|
||||
if has_altgr {
|
||||
cap_type = KeyCapType::RegularAltGr;
|
||||
@@ -516,9 +522,10 @@ impl InteractionHandler for KeyboardBackend {
|
||||
&mut self,
|
||||
app: &mut AppState,
|
||||
hit: &crate::backend::input::PointerHit,
|
||||
delta: f32,
|
||||
delta_y: f32,
|
||||
delta_x: f32,
|
||||
) {
|
||||
self.canvas.on_scroll(app, hit, delta)
|
||||
self.canvas.on_scroll(app, hit, delta_y, delta_x)
|
||||
}
|
||||
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
|
||||
self.canvas.on_left(app, pointer)
|
||||
|
||||
+3
-16
@@ -3,7 +3,6 @@ use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
ops::Add,
|
||||
ptr,
|
||||
sync::{atomic::AtomicU64, Arc},
|
||||
time::{Duration, Instant},
|
||||
@@ -88,7 +87,6 @@ fn set_next_move(millis_from_now: u64) {
|
||||
}
|
||||
|
||||
pub struct ScreenInteractionHandler {
|
||||
next_scroll: Instant,
|
||||
mouse_transform: Affine2,
|
||||
}
|
||||
impl ScreenInteractionHandler {
|
||||
@@ -113,7 +111,6 @@ impl ScreenInteractionHandler {
|
||||
};
|
||||
|
||||
ScreenInteractionHandler {
|
||||
next_scroll: Instant::now(),
|
||||
mouse_transform: transform,
|
||||
}
|
||||
}
|
||||
@@ -152,19 +149,9 @@ impl InteractionHandler for ScreenInteractionHandler {
|
||||
let pos = self.mouse_transform.transform_point2(hit.uv);
|
||||
app.hid_provider.mouse_move(pos);
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
|
||||
if self.next_scroll > Instant::now() {
|
||||
return;
|
||||
}
|
||||
let max_millis = if matches!(hit.mode, PointerMode::Left) {
|
||||
200.0
|
||||
} else {
|
||||
100.0
|
||||
};
|
||||
|
||||
let millis = (1. - delta.abs()) * max_millis;
|
||||
self.next_scroll = Instant::now().add(Duration::from_millis(millis as _));
|
||||
app.hid_provider.wheel(if delta < 0. { -1 } else { 1 })
|
||||
fn on_scroll(&mut self, app: &mut AppState, _hit: &PointerHit, delta_y: f32, delta_x: f32) {
|
||||
app.hid_provider
|
||||
.wheel((delta_y * 64.) as i32, (delta_x * 64.) as i32)
|
||||
}
|
||||
fn on_left(&mut self, _app: &mut AppState, _hand: usize) {}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ use crate::{
|
||||
const FONT_SIZE: isize = 16;
|
||||
const PADDING: (f32, f32) = (25., 7.);
|
||||
const PIXELS_TO_METERS: f32 = 1. / 2000.;
|
||||
const TOAST_AUDIO_WAV: &[u8] = include_bytes!("../res/557297.wav");
|
||||
static TOAST_NAME: Lazy<Arc<str>> = Lazy::new(|| "toast".into());
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
@@ -78,6 +77,9 @@ impl Toast {
|
||||
let destroy_at = instant.add(std::time::Duration::from_secs_f32(self.timeout));
|
||||
|
||||
let has_sound = self.sound && app.session.config.notifications_sound_enabled;
|
||||
if has_sound {
|
||||
app.audio.play(app.toast_sound);
|
||||
}
|
||||
|
||||
// drop any toast that was created before us.
|
||||
// (DropOverlay only drops overlays that were
|
||||
@@ -109,9 +111,6 @@ impl Toast {
|
||||
instant,
|
||||
);
|
||||
|
||||
if has_sound {
|
||||
app.audio.play(TOAST_AUDIO_WAV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+129
-64
@@ -1,7 +1,10 @@
|
||||
use glam::{vec3a, Affine2, Vec3, Vec3A};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
||||
use vulkano::image::SubresourceLayout;
|
||||
use wayvr_ipc::packet_server;
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
image::{view::ImageView, SubresourceLayout},
|
||||
};
|
||||
use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged};
|
||||
use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
|
||||
|
||||
use crate::{
|
||||
@@ -153,19 +156,25 @@ impl InteractionHandler for WayVRInteractionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_scroll(&mut self, _app: &mut state::AppState, _hit: &input::PointerHit, delta: f32) {
|
||||
fn on_scroll(
|
||||
&mut self,
|
||||
_app: &mut state::AppState,
|
||||
_hit: &input::PointerHit,
|
||||
delta_y: f32,
|
||||
delta_x: f32,
|
||||
) {
|
||||
let ctx = self.context.borrow();
|
||||
ctx.wayvr
|
||||
.borrow_mut()
|
||||
.data
|
||||
.state
|
||||
.send_mouse_scroll(ctx.display, delta);
|
||||
.send_mouse_scroll(ctx.display, delta_y, delta_x);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WayVRRenderer {
|
||||
dmabuf_image: Option<Arc<vulkano::image::Image>>,
|
||||
view: Option<Arc<vulkano::image::view::ImageView>>,
|
||||
vk_image: Option<Arc<vulkano::image::Image>>,
|
||||
vk_image_view: Option<Arc<vulkano::image::view::ImageView>>,
|
||||
context: Rc<RefCell<WayVRContext>>,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
}
|
||||
@@ -178,8 +187,8 @@ impl WayVRRenderer {
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
context: Rc::new(RefCell::new(WayVRContext::new(wvr, display)?)),
|
||||
dmabuf_image: None,
|
||||
view: None,
|
||||
vk_image: None,
|
||||
vk_image_view: None,
|
||||
graphics: app.graphics.clone(),
|
||||
})
|
||||
}
|
||||
@@ -332,12 +341,23 @@ where
|
||||
anyhow::bail!("Overlay ID not set for dashboard display");
|
||||
};
|
||||
|
||||
let cur_visibility = !display.visible;
|
||||
|
||||
wayvr
|
||||
.data
|
||||
.ipc_server
|
||||
.broadcast(PacketServer::WvrStateChanged(if cur_visibility {
|
||||
WvrStateChanged::DashboardShown
|
||||
} else {
|
||||
WvrStateChanged::DashboardHidden
|
||||
}));
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(
|
||||
OverlaySelector::Id(overlay_id),
|
||||
Box::new(move |app, o| {
|
||||
// Toggle visibility
|
||||
o.want_visible = !o.want_visible;
|
||||
if o.want_visible {
|
||||
o.want_visible = cur_visibility;
|
||||
if cur_visibility {
|
||||
o.reset(app, true);
|
||||
}
|
||||
}),
|
||||
@@ -447,12 +467,12 @@ where
|
||||
wayvr
|
||||
.data
|
||||
.ipc_server
|
||||
.broadcast(packet_server::PacketServer::WvrStateChanged(packet))?;
|
||||
.broadcast(packet_server::PacketServer::WvrStateChanged(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let res = wayvr.data.tick_events()?;
|
||||
let res = wayvr.data.tick_events(app)?;
|
||||
drop(wayvr);
|
||||
|
||||
for result in res {
|
||||
@@ -534,54 +554,82 @@ where
|
||||
}
|
||||
|
||||
impl WayVRRenderer {
|
||||
fn ensure_dmabuf(&mut self, data: wayvr::egl_data::DMAbufData) -> anyhow::Result<()> {
|
||||
if self.dmabuf_image.is_none() {
|
||||
// First init
|
||||
let mut planes = [FramePlane::default(); 4];
|
||||
planes[0].fd = Some(data.fd);
|
||||
planes[0].offset = data.offset as u32;
|
||||
planes[0].stride = data.stride;
|
||||
fn ensure_software_data(
|
||||
&mut self,
|
||||
data: &wayvr::egl_data::RenderSoftwarePixelsData,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut upload = self
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
|
||||
let ctx = self.context.borrow_mut();
|
||||
let wayvr = ctx.wayvr.borrow_mut();
|
||||
if let Some(disp) = wayvr.data.state.displays.get(&ctx.display) {
|
||||
let frame = DmabufFrame {
|
||||
format: FrameFormat {
|
||||
width: disp.width as u32,
|
||||
height: disp.height as u32,
|
||||
fourcc: FourCC {
|
||||
value: data.mod_info.fourcc,
|
||||
},
|
||||
modifier: data.mod_info.modifiers[0], /* possibly not proper? */
|
||||
..Default::default()
|
||||
},
|
||||
num_planes: 1,
|
||||
planes,
|
||||
};
|
||||
let tex = upload.texture2d_raw(
|
||||
data.width as u32,
|
||||
data.height as u32,
|
||||
vulkano::format::Format::R8G8B8A8_UNORM,
|
||||
&data.data,
|
||||
)?;
|
||||
|
||||
drop(wayvr);
|
||||
upload.build_and_execute_now()?;
|
||||
|
||||
let layouts: Vec<SubresourceLayout> = vec![SubresourceLayout {
|
||||
offset: data.offset as _,
|
||||
size: 0,
|
||||
row_pitch: data.stride as _,
|
||||
array_pitch: None,
|
||||
depth_pitch: None,
|
||||
}];
|
||||
self.vk_image = Some(tex.clone());
|
||||
self.vk_image_view = Some(ImageView::new_default(tex).unwrap());
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
fn ensure_dmabuf_data(
|
||||
&mut self,
|
||||
data: &wayvr::egl_data::RenderDMAbufData,
|
||||
) -> anyhow::Result<()> {
|
||||
if self.vk_image.is_some() {
|
||||
return Ok(()); // already initialized and automatically updated due to direct zero-copy textue access
|
||||
}
|
||||
|
||||
// First init
|
||||
let mut planes = [FramePlane::default(); 4];
|
||||
planes[0].fd = Some(data.fd);
|
||||
planes[0].offset = data.offset as u32;
|
||||
planes[0].stride = data.stride;
|
||||
|
||||
let ctx = self.context.borrow_mut();
|
||||
let wayvr = ctx.wayvr.borrow_mut();
|
||||
let Some(disp) = wayvr.data.state.displays.get(&ctx.display) else {
|
||||
anyhow::bail!("Failed to fetch WayVR display")
|
||||
};
|
||||
|
||||
let frame = DmabufFrame {
|
||||
format: FrameFormat {
|
||||
width: disp.width as u32,
|
||||
height: disp.height as u32,
|
||||
fourcc: FourCC {
|
||||
value: data.mod_info.fourcc,
|
||||
},
|
||||
modifier: data.mod_info.modifiers[0], /* possibly not proper? */
|
||||
..Default::default()
|
||||
},
|
||||
num_planes: 1,
|
||||
planes,
|
||||
};
|
||||
|
||||
drop(wayvr);
|
||||
|
||||
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.vk_image = Some(tex.clone());
|
||||
self.vk_image_view = Some(vulkano::image::view::ImageView::new_default(tex).unwrap());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -609,34 +657,48 @@ impl OverlayRenderer for WayVRRenderer {
|
||||
let ctx = self.context.borrow();
|
||||
let mut wayvr = ctx.wayvr.borrow_mut();
|
||||
|
||||
match wayvr.data.tick_display(ctx.display) {
|
||||
Ok(_) => {}
|
||||
let redrawn = match wayvr.data.tick_display(ctx.display) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::error!("tick_display failed: {}", e);
|
||||
return Ok(()); // do not proceed further
|
||||
}
|
||||
};
|
||||
|
||||
if !redrawn {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dmabuf_data = wayvr
|
||||
let data = wayvr
|
||||
.data
|
||||
.state
|
||||
.get_dmabuf_data(ctx.display)
|
||||
.ok_or(anyhow::anyhow!("Failed to fetch dmabuf data"))?
|
||||
.get_render_data(ctx.display)
|
||||
.ok_or(anyhow::anyhow!("Failed to fetch render data"))?
|
||||
.clone();
|
||||
|
||||
drop(wayvr);
|
||||
drop(ctx);
|
||||
self.ensure_dmabuf(dmabuf_data.clone())?;
|
||||
|
||||
match data {
|
||||
wayvr::egl_data::RenderData::Dmabuf(data) => {
|
||||
self.ensure_dmabuf_data(&data)?;
|
||||
}
|
||||
wayvr::egl_data::RenderData::Software(data) => {
|
||||
if let Some(new_frame) = &data {
|
||||
self.ensure_software_data(new_frame)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Option<Arc<vulkano::image::view::ImageView>> {
|
||||
self.view.clone()
|
||||
self.vk_image_view.clone()
|
||||
}
|
||||
|
||||
fn frame_transform(&mut self) -> Option<FrameTransform> {
|
||||
self.view.as_ref().map(|view| FrameTransform {
|
||||
self.vk_image_view.as_ref().map(|view| FrameTransform {
|
||||
extent: view.image().extent(),
|
||||
..Default::default()
|
||||
})
|
||||
@@ -725,8 +787,11 @@ where
|
||||
if let Some(app_entry) = catalog.get_app(app_name) {
|
||||
let mut wayvr = wayvr.borrow_mut();
|
||||
|
||||
let disp_handle =
|
||||
get_or_create_display_by_name(app, &mut wayvr, &app_entry.target_display)?;
|
||||
let disp_handle = get_or_create_display_by_name(
|
||||
app,
|
||||
&mut wayvr,
|
||||
&app_entry.target_display.to_lowercase(),
|
||||
)?;
|
||||
|
||||
let args_vec = match &app_entry.args {
|
||||
Some(args) => gen_args_vec(args),
|
||||
|
||||
+13
-6
@@ -5,6 +5,13 @@
|
||||
|
||||
version: 1
|
||||
|
||||
# If your gpu has some issues with zero-copy textures, you can set this option to "software".
|
||||
#
|
||||
# Possible options:
|
||||
# "dmabuf": Use zero-copy texture access (from EGL to Vulkan) - no performance impact
|
||||
# "software": Read pixel data to memory via glReadPixels() every time a content has been updated. Minor performance impact on large resolutions
|
||||
blit_method: "dmabuf"
|
||||
|
||||
# Set to true if you want to make Wyland server instantly available.
|
||||
# By default, WayVR starts only when it's needed.
|
||||
# (this option is primarily used for remote starting external processes and development purposes)
|
||||
@@ -34,18 +41,18 @@ dashboard:
|
||||
env: ["GDK_BACKEND=wayland"]
|
||||
|
||||
displays:
|
||||
Watch:
|
||||
watch:
|
||||
width: 400
|
||||
height: 600
|
||||
scale: 0.4
|
||||
attach_to: "HandRight" # HandLeft, HandRight
|
||||
pos: [0.0, 0.0, 0.125]
|
||||
rotation: {axis: [1.0, 0.0, 0.0], angle: -45.0}
|
||||
Disp1:
|
||||
disp1:
|
||||
width: 640
|
||||
height: 480
|
||||
primary: true # Required if you want to attach external processes (not spawned by WayVR itself) without WAYVR_DISPLAY_NAME set
|
||||
Disp2:
|
||||
disp2:
|
||||
width: 1280
|
||||
height: 720
|
||||
scale: 2.0
|
||||
@@ -54,18 +61,18 @@ catalogs:
|
||||
default_catalog:
|
||||
apps:
|
||||
- name: "Calc"
|
||||
target_display: "Disp1"
|
||||
target_display: "disp1"
|
||||
exec: "kcalc"
|
||||
env: ["FOO=bar"]
|
||||
shown_at_start: false
|
||||
|
||||
- name: "htop"
|
||||
target_display: "Watch"
|
||||
target_display: "watch"
|
||||
exec: "konsole"
|
||||
args: "-e htop"
|
||||
|
||||
- name: "Browser"
|
||||
target_display: "Disp2"
|
||||
target_display: "disp2"
|
||||
exec: "cage"
|
||||
args: "chromium -- --incognito"
|
||||
|
||||
+5
-5
@@ -46,11 +46,11 @@ pub mod frag_color {
|
||||
",
|
||||
}
|
||||
}
|
||||
//layout (location = 1) in float corner_radius;
|
||||
//out_color = in_color;
|
||||
// Some equation that determines whether to keep the pixel
|
||||
// Use Lerp not if
|
||||
//out_color.a = 0;
|
||||
//layout (location = 1) in float corner_radius;
|
||||
//out_color = in_color;
|
||||
// Some equation that determines whether to keep the pixel
|
||||
// Use Lerp not if
|
||||
//out_color.a = 0;
|
||||
|
||||
pub mod frag_glyph {
|
||||
vulkano_shaders::shader! {
|
||||
|
||||
+42
-1
@@ -51,6 +51,7 @@ pub struct AppState {
|
||||
pub anchor: Affine3A,
|
||||
pub sprites: AStrMap<Arc<ImageView>>,
|
||||
pub keyboard_focus: KeyboardFocus,
|
||||
pub toast_sound: &'static [u8],
|
||||
|
||||
#[cfg(feature = "osc")]
|
||||
pub osc_sender: Option<OscSender>,
|
||||
@@ -111,6 +112,11 @@ impl AppState {
|
||||
#[cfg(feature = "osc")]
|
||||
let osc_sender = crate::backend::osc::OscSender::new(session.config.osc_out_port).ok();
|
||||
|
||||
let toast_sound_wav = AppState::try_load_bytes(
|
||||
&session.config.notification_sound,
|
||||
include_bytes!("res/557297.wav")
|
||||
);
|
||||
|
||||
Ok(AppState {
|
||||
fc: FontCache::new(session.config.primary_font.clone())?,
|
||||
session,
|
||||
@@ -123,6 +129,7 @@ impl AppState {
|
||||
anchor: Affine3A::IDENTITY,
|
||||
sprites: AStrMap::new(),
|
||||
keyboard_focus: KeyboardFocus::PhysicalScreen,
|
||||
toast_sound: toast_sound_wav,
|
||||
|
||||
#[cfg(feature = "osc")]
|
||||
osc_sender,
|
||||
@@ -139,12 +146,46 @@ impl AppState {
|
||||
Ok(wvr.clone())
|
||||
} else {
|
||||
let wayvr = Rc::new(RefCell::new(WayVRData::new(
|
||||
WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config),
|
||||
WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config)?,
|
||||
)?));
|
||||
self.wayvr = Some(wayvr.clone());
|
||||
Ok(wayvr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8]
|
||||
{
|
||||
if path.is_empty() {
|
||||
return fallback_data;
|
||||
}
|
||||
|
||||
let real_path = config_io::get_config_root().join(&*path);
|
||||
|
||||
if std::fs::File::open(real_path.clone()).is_err() {
|
||||
log::warn!(
|
||||
"Could not open file at: {}",
|
||||
path
|
||||
);
|
||||
return fallback_data;
|
||||
};
|
||||
|
||||
return match std::fs::read(real_path.clone()){
|
||||
// Box is used here to work around `f`'s limited lifetime
|
||||
Ok(f) => {
|
||||
Box::leak(Box::new(f)).as_slice()
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Failed to read file at: {}",
|
||||
path
|
||||
);
|
||||
log::warn!("{:?}", e);
|
||||
fallback_data
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct AppSession {
|
||||
|
||||
Reference in new issue
Block a user