Compare commits

...
8 Commits
Author SHA1 Message Date
galister 85aa0fb1bc release 0.6 2024-09-28 01:54:53 +09:00
galister 7f72abc62a release modifiers on keyboard hide 2024-09-28 01:53:20 +09:00
galister 83ae798215 add virtualkey action for modular button 2024-09-27 02:23:13 +09:00
galister bdbdbea20b screen notification popups 2024-09-26 23:51:46 +09:00
galister a077f2dda4 fix dragging cursor off-screen 2024-09-26 23:51:44 +09:00
galister 8d35b45c2b rollback vulkano 2024-09-24 11:45:29 +02:00
galister 6af5623fa7 update readme 2024-09-19 06:18:06 +00:00
galister 62ef7a1210 update readme 2024-09-19 06:47:01 +09:00
14 changed files with 600 additions and 91 deletions

No files matched your search

Generated
+23 -20
View File
@@ -225,11 +225,11 @@ checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
name = "ash"
version = "0.38.0+1.3.281"
version = "0.37.3+1.3.251"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
dependencies = [
"libloading",
"libloading 0.7.4",
]
[[package]]
@@ -789,7 +789,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
"libloading 0.8.5",
]
[[package]]
@@ -1264,7 +1264,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading",
"libloading 0.8.5",
]
[[package]]
@@ -2031,6 +2031,16 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.5"
@@ -2671,7 +2681,7 @@ version = "0.18.0"
source = "git+https://github.com/galister/openxrs?rev=af4a55d#af4a55df60125491c80c61464c824219c6019b76"
dependencies = [
"libc",
"libloading",
"libloading 0.8.5",
"ndk-context",
"openxr-sys",
]
@@ -3472,12 +3482,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "slabbin"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8305086044614627ed85432d27b87cf9fc047204eaa036a11de6cf0120f273"
[[package]]
name = "slice-of-array"
version = "0.3.2"
@@ -3908,7 +3912,7 @@ dependencies = [
[[package]]
name = "vulkano"
version = "0.34.0"
source = "git+https://github.com/vulkano-rs/vulkano?rev=b9f3e89#b9f3e893005078b5680cd1672037465a2b5f3133"
source = "git+https://github.com/vulkano-rs/vulkano?rev=94f50f1#94f50f18bd25971ea123adb8b5782ad65a8f085c"
dependencies = [
"ahash",
"ash",
@@ -3918,17 +3922,16 @@ dependencies = [
"half",
"heck 0.4.1",
"indexmap 2.3.0",
"libloading",
"nom",
"libloading 0.8.5",
"objc",
"once_cell",
"parking_lot",
"proc-macro2",
"quote",
"raw-window-handle",
"regex",
"serde",
"serde_json",
"slabbin",
"smallvec",
"thread_local",
"vk-parse",
@@ -3938,7 +3941,7 @@ dependencies = [
[[package]]
name = "vulkano-macros"
version = "0.34.0"
source = "git+https://github.com/vulkano-rs/vulkano?rev=b9f3e89#b9f3e893005078b5680cd1672037465a2b5f3133"
source = "git+https://github.com/vulkano-rs/vulkano?rev=94f50f1#94f50f18bd25971ea123adb8b5782ad65a8f085c"
dependencies = [
"proc-macro-crate 2.0.2",
"proc-macro2",
@@ -3949,7 +3952,7 @@ dependencies = [
[[package]]
name = "vulkano-shaders"
version = "0.34.0"
source = "git+https://github.com/vulkano-rs/vulkano?rev=b9f3e89#b9f3e893005078b5680cd1672037465a2b5f3133"
source = "git+https://github.com/vulkano-rs/vulkano?rev=94f50f1#94f50f18bd25971ea123adb8b5782ad65a8f085c"
dependencies = [
"ahash",
"heck 0.4.1",
@@ -4574,7 +4577,7 @@ dependencies = [
[[package]]
name = "wlx-overlay-s"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"anyhow",
"ash",
@@ -4644,7 +4647,7 @@ dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"libloading",
"libloading 0.8.5",
"once_cell",
"rustix",
"x11rb-protocol",
+4 -4
View File
@@ -4,14 +4,14 @@ debug = true
[package]
name = "wlx-overlay-s"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.86"
ash = "^0.38.0"
ash = "^0.37.2"
chrono = "0.4.38"
chrono-tz = "0.9.0"
clap = { version = "4.5.6", features = ["derive"] }
@@ -53,8 +53,8 @@ smallvec = "1.13.2"
strum = { version = "0.26.2", features = ["derive"] }
sysinfo = { version = "0.31.2" }
thiserror = "1.0.61"
vulkano = { git = "https://github.com/vulkano-rs/vulkano", rev = "b9f3e89" }
vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano", rev = "b9f3e89" }
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.3.13", default-features = false }
libmonado-rs = { git = "https://github.com/technobaboo/libmonado-rs", rev = "a495f6d", optional = true }
winit = { version = "0.30.0", optional = true }
+27 -19
View File
@@ -6,21 +6,18 @@ WlxOverlay-S allows you to access your desktop screens while in VR.
In comparison to similar overlays, WlxOverlay-S aims to run alongside VR games and experiences while having as little performance impact as possible. The UI appearance and rendering techniques are kept as simple and efficient as possible, while still allowing a high degree of customizability.
This is the coming-together of two of my previous projects:
- [WlxOverlay](https://github.com/galister/WlxOverlay) (SteamVR overlay written in C#)
- [WlxOverlay-X](https://github.com/galister/wlx-overlay-x) (OpenXR overlay using StereoKit, written in Rust)
![Screenshot of WlxOverlay-S being used as an OpenXR home environment](https://github.com/galister/wlx-overlay-s/blob/guide/wlx-s.png?raw=true)
![Screenshot of WlxOverlay-S in Action](https://github.com/galister/wlx-overlay-s/blob/guide/wlx-s.png?raw=true)
# Join the Linux VR Community
## Join the Linux VR Community
We are available on either:
- Discord: https://discord.gg/gHwJ2vwSWV
- Discord: <https://discord.gg/gHwJ2vwSWV>
- Matrix Space: `#linux-vr-adventures:matrix.org`
Questions/issues specific to WlxOverlay-S will be handled in the `wlxoverlay` chat room.
# Setup
## Setup
1. Grab the latest AppImage from [Releases](https://github.com/galister/wlx-overlay-s/releases).
1. `chmod +x WlxOverlay-S-*.AppImage`
@@ -31,18 +28,21 @@ AUR package is [wlx-overlay-s-git](https://aur.archlinux.org/packages/wlx-overla
You may also want to [build from source](https://github.com/galister/wlx-overlay-s/wiki/Building-from-Source).
# First Start
## First Start
**If you get a screen share pop-up, check the terminal and select the screens in the order it tells you to.**
**When the screen share pop-up appears, check the terminal and select the screens in the order it requests.**
If you selected the screens in the wrong order:
- `rm ~/.config/wlxoverlay/conf.d/pw_tokens.yaml` then restart
SteamVR users: WlxOverlay-S will register itself for auto-start, so you will not need to start it every time.
- `rm ~/.config/wlxoverlay/conf.d/pw_tokens.yaml` then restart
**SteamVR users**: WlxOverlay-S will register itself for auto-start, so you will not need to start it every time.
**Envision users**: Set `wlx-overlay-s --openxr --show` as the _Autostart Command_ on your Envision profile! This will give you a home environment with a [customizable background](https://github.com/galister/wlx-overlay-s/wiki/OpenXR-Skybox)!
**Please continue reading the guide below.**
# Getting Started
## Getting Started
### Working Set
@@ -51,6 +51,7 @@ Your working set consists of your currently selected overlays; screens, mirrors,
The working set appears in front of you when shown, and can be re-centered by hiding and showing again.
Show and hide your working set using:
- Non-vive controller: double-tap B or Y on your left controller.
- Vive controller: double-tap the menu button on your left controller (OpenXR, for SteamVR, you might need to bind `showhide` yourself.)
@@ -58,6 +59,7 @@ Show and hide your working set using:
Much of the functionality in WlxOverlay-S depends on what color of laser you are using to interact with a UI element. \
Using the default settings, there are 3 modes:
- Regular Mode: Blue laser
- Right-click Mode: Orange laser
- Middle-click Mode: Purple laser
@@ -77,6 +79,7 @@ Check your left wrist for the watch. The watch is your primary tool for controll
Hovering a pointer over a screen will move the mouse. If there are more than one pointers hovering a screen, the pointer that was last used to click will take precedence.
The click depends on the laser color:
- Blue laser: Left click
- Orange laser: Right click
- Purple laser: Middle click
@@ -92,6 +95,7 @@ The keyboard is fully customizable via the [keyboard.yaml](https://raw.githubuse
Download it into your `~/.config/wlxoverlay/` folder and edit it to your liking.
Typing
- Use the BLUE laser when typing regularly.
- While using ORANGE laser, all keystrokes will have SHIFT applied.
- Purple laser has no effect as of now.
@@ -104,31 +108,35 @@ Typing
![Touch Controller Bindings](https://github.com/galister/wlx-overlay-s/blob/guide/wlx-oculus.png)
To customize bindings on OpenXR, refer to the [OpenXR Bindings wiki page](https://github.com/galister/wlx-overlay-s/wiki/OpenXR-Bindings).
If your bindings are not supported, please reach out. \
We would like to work with you and include additional bindings.
# Troubleshooting
## Troubleshooting
Check [here](https://github.com/galister/wlx-overlay-s/wiki/Troubleshooting) for tips.
# Known Issues
## Known Issues
### Mouse is not where it should be
## Mouse is not where it should be
Hyprland users: Hyprland v0.41.0 changed their absolute input implementation to one that does not respect existing absolute input standards. Make your voice heard: [Hyprland#6023](https://github.com/hyprwm/Hyprland/issues/6023)・[Hyprland#6889](https://github.com/hyprwm/Hyprland/issues/6889)
Niri users: use on Niri 0.1.7 or later.
Other desktops: You may have selected the screens in the wrong order, see [First Start](#first-start).
## Space-drag crashes SteamVR
### Space-drag crashes SteamVR
This has been idenfitied as an issue with SteamVR versions 2.5.5 and above (latest tested 2.7.2). One way to avoid the crash is by switching to the `temp-v1.27.5` branch of SteamVR (via beta selection) and selecting [Steam-Play-None](https://github.com/Scrumplex/Steam-Play-None) under the compatibility tab.
## Modifiers get stuck in weird ways
### Modifiers get stuck in weird ways
This is a rare issue that can make your desktop not react to click or keys due to a modifier being stuck somewhere. Restarting the overlay fixes this.
## X11 limitations
### X11 limitations
- X11 capture can generally seem slow. This is because zero-copy GPU capture is not supported on the general X11 desktop. Consider trying Wayland or Picom.
- DPI scaling is not supported and may cause the mouse to not follow the laser properly.
- Upright screens are not supported and can cause the mouse to act weirdly.
+11 -9
View File
@@ -313,15 +313,17 @@ where
pointer = &mut app.input_state.pointers[idx];
pointer.interaction.hovered_id = None;
}
if let Some(clicked_id) = pointer.interaction.clicked_id.take() {
if let Some(clicked) = overlays.mut_by_id(clicked_id) {
let hit = PointerHit {
pointer: pointer.idx,
overlay: clicked_id,
mode: pointer.interaction.mode,
..Default::default()
};
clicked.backend.on_pointer(app, &hit, false);
if !pointer.now.click && pointer.before.click {
if let Some(clicked_id) = pointer.interaction.clicked_id.take() {
if let Some(clicked) = overlays.mut_by_id(clicked_id) {
let hit = PointerHit {
pointer: pointer.idx,
overlay: clicked_id,
mode: pointer.interaction.mode,
..Default::default()
};
clicked.backend.on_pointer(app, &hit, false);
}
}
}
return (0.0, None); // no hit
+3
View File
@@ -2,6 +2,9 @@ pub mod common;
pub mod input;
pub mod notifications;
#[allow(clippy::all)]
mod notifications_dbus;
#[cfg(feature = "openvr")]
pub mod openvr;
+61 -1
View File
@@ -1,4 +1,9 @@
use dbus::{blocking::Connection, channel::MatchingReceiver, message::MatchRule};
use dbus::{
arg::{PropMap, Variant},
blocking::Connection,
channel::MatchingReceiver,
message::MatchRule,
};
use serde::Deserialize;
use std::{
sync::{
@@ -9,6 +14,7 @@ use std::{
};
use crate::{
backend::notifications_dbus::OrgFreedesktopNotifications,
overlays::toast::{Toast, ToastTopic},
state::AppState,
};
@@ -196,6 +202,60 @@ impl Drop for NotificationManager {
}
}
pub struct DbusNotificationSender {
connection: Connection,
}
impl DbusNotificationSender {
pub fn new() -> anyhow::Result<Self> {
Ok(Self {
connection: Connection::new_session()?,
})
}
pub fn notify_send(
&self,
summary: &str,
body: &str,
urgency: u8,
timeout: i32,
replaces_id: u32,
transient: bool,
) -> anyhow::Result<u32> {
let proxy = self.connection.with_proxy(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
Duration::from_millis(1000),
);
let mut hints = PropMap::new();
hints.insert("urgency".to_string(), Variant(Box::new(urgency)));
hints.insert("transient".to_string(), Variant(Box::new(transient)));
Ok(proxy.notify(
"WlxOverlay-S",
replaces_id,
"",
summary,
body,
vec![],
hints,
timeout,
)?)
}
pub fn notify_close(&self, id: u32) -> anyhow::Result<()> {
let proxy = self.connection.with_proxy(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
Duration::from_millis(1000),
);
proxy.close_notification(id)?;
Ok(())
}
}
fn parse_dbus(msg: &dbus::Message) -> anyhow::Result<Toast> {
let mut args = msg.iter_init();
let app_name: String = args.read()?;
+267
View File
@@ -0,0 +1,267 @@
// 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;
#[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_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>;
}
#[derive(Debug)]
pub struct OrgFreedesktopDBusPropertiesPropertiesChanged {
pub interface_name: String,
pub changed_properties: arg::PropMap,
pub invalidated_properties: Vec<String>,
}
impl arg::AppendAll for OrgFreedesktopDBusPropertiesPropertiesChanged {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.interface_name, i);
arg::RefArg::append(&self.changed_properties, i);
arg::RefArg::append(&self.invalidated_properties, i);
}
}
impl arg::ReadAll for OrgFreedesktopDBusPropertiesPropertiesChanged {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgFreedesktopDBusPropertiesPropertiesChanged {
interface_name: i.read()?,
changed_properties: i.read()?,
invalidated_properties: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgFreedesktopDBusPropertiesPropertiesChanged {
const NAME: &'static str = "PropertiesChanged";
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, ))
}
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, ))
}
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), ))
}
}
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> {
fn introspect(&self) -> Result<String, dbus::Error> {
self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ())
.and_then(|r: (String, )| Ok(r.0, ))
}
}
pub trait OrgFreedesktopDBusPeer {
fn ping(&self) -> Result<(), dbus::Error>;
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> {
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, ))
}
}
pub trait OrgFreedesktopNotifications {
fn set_noti_window_visibility(&self, value: bool) -> Result<(), dbus::Error>;
fn toggle_dnd(&self) -> Result<bool, dbus::Error>;
fn set_dnd(&self, state: bool) -> Result<(), dbus::Error>;
fn get_dnd(&self) -> Result<bool, dbus::Error>;
fn manually_close_notification(&self, id: u32, timeout: bool) -> Result<(), dbus::Error>;
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 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>;
fn set_dnd_(&self, value: bool) -> Result<(), dbus::Error>;
}
#[derive(Debug)]
pub struct OrgFreedesktopNotificationsOnDndToggle {
pub dnd: bool,
}
impl arg::AppendAll for OrgFreedesktopNotificationsOnDndToggle {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.dnd, i);
}
}
impl arg::ReadAll for OrgFreedesktopNotificationsOnDndToggle {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgFreedesktopNotificationsOnDndToggle {
dnd: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgFreedesktopNotificationsOnDndToggle {
const NAME: &'static str = "OnDndToggle";
const INTERFACE: &'static str = "org.freedesktop.Notifications";
}
#[derive(Debug)]
pub struct OrgFreedesktopNotificationsNotificationClosed {
pub id: u32,
pub reason: u32,
}
impl arg::AppendAll for OrgFreedesktopNotificationsNotificationClosed {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.id, i);
arg::RefArg::append(&self.reason, i);
}
}
impl arg::ReadAll for OrgFreedesktopNotificationsNotificationClosed {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgFreedesktopNotificationsNotificationClosed {
id: i.read()?,
reason: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgFreedesktopNotificationsNotificationClosed {
const NAME: &'static str = "NotificationClosed";
const INTERFACE: &'static str = "org.freedesktop.Notifications";
}
#[derive(Debug)]
pub struct OrgFreedesktopNotificationsActionInvoked {
pub id: u32,
pub action_key: String,
}
impl arg::AppendAll for OrgFreedesktopNotificationsActionInvoked {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.id, i);
arg::RefArg::append(&self.action_key, i);
}
}
impl arg::ReadAll for OrgFreedesktopNotificationsActionInvoked {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgFreedesktopNotificationsActionInvoked {
id: i.read()?,
action_key: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgFreedesktopNotificationsActionInvoked {
const NAME: &'static str = "ActionInvoked";
const INTERFACE: &'static str = "org.freedesktop.Notifications";
}
#[derive(Debug)]
pub struct OrgFreedesktopNotificationsNotificationReplied {
pub id: u32,
pub text: String,
}
impl arg::AppendAll for OrgFreedesktopNotificationsNotificationReplied {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.id, i);
arg::RefArg::append(&self.text, i);
}
}
impl arg::ReadAll for OrgFreedesktopNotificationsNotificationReplied {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgFreedesktopNotificationsNotificationReplied {
id: i.read()?,
text: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgFreedesktopNotificationsNotificationReplied {
const NAME: &'static str = "NotificationReplied";
const INTERFACE: &'static str = "org.freedesktop.Notifications";
}
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, ))
}
fn toggle_dnd(&self) -> Result<bool, dbus::Error> {
self.method_call("org.freedesktop.Notifications", "ToggleDnd", ())
.and_then(|r: (bool, )| Ok(r.0, ))
}
fn set_dnd(&self, state: bool) -> Result<(), dbus::Error> {
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, ))
}
fn manually_close_notification(&self, id: u32, timeout: bool) -> Result<(), dbus::Error> {
self.method_call("org.freedesktop.Notifications", "ManuallyCloseNotification", (id, timeout, ))
}
fn close_all_notifications(&self) -> Result<(), dbus::Error> {
self.method_call("org.freedesktop.Notifications", "CloseAllNotifications", ())
}
fn hide_latest_notification(&self, close: bool) -> Result<(), dbus::Error> {
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, ))
}
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, ))
}
fn get_server_information(&self) -> Result<(String, String, String, String), dbus::Error> {
self.method_call("org.freedesktop.Notifications", "GetServerInformation", ())
}
fn dnd(&self) -> Result<bool, dbus::Error> {
<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)
}
}
+22 -17
View File
@@ -44,8 +44,8 @@ use vulkano::{
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
},
device::{
physical::PhysicalDevice, Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures,
Queue, QueueCreateInfo, QueueFlags,
physical::PhysicalDevice, Device, DeviceCreateInfo, DeviceExtensions, Features, Queue,
QueueCreateInfo, QueueFlags,
},
format::Format,
image::{
@@ -195,7 +195,7 @@ impl WlxGraphics {
let target_version = vulkano::Version::V1_3;
let library = get_vulkan_library();
let vk_app_info_raw = vk::ApplicationInfo::default()
let vk_app_info_raw = vk::ApplicationInfo::builder()
.application_version(0)
.engine_version(0)
.api_version(vk_target_version);
@@ -205,7 +205,7 @@ impl WlxGraphics {
.create_vulkan_instance(
system,
get_instance_proc_addr,
&vk::InstanceCreateInfo::default()
&vk::InstanceCreateInfo::builder()
.application_info(&vk_app_info_raw)
.enabled_extension_names(&instance_extensions_raw)
as *const _ as *const _,
@@ -286,23 +286,25 @@ impl WlxGraphics {
})
.collect::<Vec<_>>();
let features = DeviceFeatures {
let features = Features {
dynamic_rendering: true,
..Default::default()
};
let queue_priorities = [1.0];
let queue_create_infos = [vk::DeviceQueueCreateInfo::default()
let queue_create_infos = [vk::DeviceQueueCreateInfo::builder()
.queue_family_index(queue_family_index)
.queue_priorities(&queue_priorities)];
.queue_priorities(&queue_priorities)
.build()];
let mut device_create_info = vk::DeviceCreateInfo::default()
let mut device_create_info = vk::DeviceCreateInfo::builder()
.queue_create_infos(&queue_create_infos)
.enabled_extension_names(&device_extensions_raw);
.enabled_extension_names(&device_extensions_raw)
.build();
let mut dynamic_rendering =
PhysicalDeviceDynamicRenderingFeatures::default().dynamic_rendering(true);
PhysicalDeviceDynamicRenderingFeatures::builder().dynamic_rendering(true);
dynamic_rendering.p_next = device_create_info.p_next as _;
device_create_info.p_next = (&mut dynamic_rendering) as *const _ as *const c_void;
@@ -487,9 +489,9 @@ impl WlxGraphics {
physical_device,
DeviceCreateInfo {
enabled_extensions: my_extensions,
enabled_features: DeviceFeatures {
enabled_features: Features {
dynamic_rendering: true,
..DeviceFeatures::empty()
..Features::empty()
},
queue_create_infos: vec![QueueCreateInfo {
queue_family_index,
@@ -625,9 +627,9 @@ impl WlxGraphics {
physical_device,
DeviceCreateInfo {
enabled_extensions: my_extensions,
enabled_features: DeviceFeatures {
enabled_features: Features {
dynamic_rendering: true,
..DeviceFeatures::empty()
..Features::empty()
},
queue_create_infos: vec![QueueCreateInfo {
queue_family_index,
@@ -1034,7 +1036,10 @@ impl WlxGraphics {
(fns.v1_0.queue_submit)(
self.queue.handle(),
1,
[SubmitInfo::default().command_buffers(&[command_buffer.handle()])].as_ptr(),
[SubmitInfo::builder()
.command_buffers(&[command_buffer.handle()])
.build()]
.as_ptr(),
fence.handle(),
)
}
@@ -1186,7 +1191,7 @@ impl WlxPipeline<WlxPipelineDynamic> {
let vep = vert.entry_point("main").unwrap(); // want panic
let fep = frag.entry_point("main").unwrap(); // want panic
let vertex_input_state = Vert2Uv::per_vertex().definition(&vep)?;
let vertex_input_state = Vert2Uv::per_vertex().definition(&vep.info().input_interface)?;
let stages = smallvec![
vulkano::pipeline::PipelineShaderStageCreateInfo::new(vep),
@@ -1343,7 +1348,7 @@ impl WlxPipeline<WlxPipelineLegacy> {
let vep = vert.entry_point("main").unwrap(); // want panic
let fep = frag.entry_point("main").unwrap(); // want panic
let vertex_input_state = Vert2Uv::per_vertex().definition(&vep)?;
let vertex_input_state = Vert2Uv::per_vertex().definition(&vep.info().input_interface)?;
let stages = smallvec![
vulkano::pipeline::PipelineShaderStageCreateInfo::new(vep),
+18 -2
View File
@@ -52,7 +52,15 @@ impl<D, S> CanvasBuilder<D, S> {
}
// Creates a label with fg_color, font_size inherited from the canvas
pub fn label(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32, text: Arc<str>) -> &mut Control<D, S> {
pub fn label(
&mut self,
x: f32,
y: f32,
w: f32,
h: f32,
radius: f32,
text: Arc<str>,
) -> &mut Control<D, S> {
let idx = self.canvas.controls.len();
self.canvas.controls.push(Control {
rect: Rect { x, y, w, h },
@@ -116,7 +124,15 @@ impl<D, S> CanvasBuilder<D, S> {
}
// Creates a button with fg_color, bg_color, font_size inherited from the canvas
pub fn button(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32, text: Arc<str>) -> &mut Control<D, S> {
pub fn button(
&mut self,
x: f32,
y: f32,
w: f32,
h: f32,
radius: f32,
text: Arc<str>,
) -> &mut Control<D, S> {
let idx = self.canvas.controls.len();
self.canvas.interactive_set_idx(x, y, w, h, idx);
+4
View File
@@ -220,6 +220,10 @@ impl<D, S> Canvas<D, S> {
cmd_buffer.end_render_pass()?;
cmd_buffer.build_and_execute_now()
}
pub fn data_mut(&mut self) -> &mut D {
&mut self.canvas.data
}
}
impl<D, S> InteractionHandler for Canvas<D, S> {
+14
View File
@@ -17,6 +17,7 @@ use crate::{
task::{ColorChannel, SystemTask, TaskType},
},
config::{save_layout, save_settings, AStrSetExt},
hid::VirtualKey,
overlays::{
toast::{Toast, ToastTopic},
watch::WATCH_NAME,
@@ -26,6 +27,12 @@ use crate::{
use super::{ExecArgs, ModularControl, ModularData};
#[derive(Deserialize, Clone)]
pub enum PressRelease {
Release,
Press,
}
#[derive(Deserialize, Clone, Copy)]
pub enum ViewAngleKind {
/// The cosine of the angle at which the watch becomes fully transparent
@@ -116,6 +123,10 @@ pub enum ButtonAction {
command: ExecArgs,
toast: Option<Arc<str>>,
},
VirtualKey {
keycode: VirtualKey,
action: PressRelease,
},
Watch {
action: WatchAction,
},
@@ -316,6 +327,9 @@ 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),
ButtonAction::VirtualKey { keycode, action } => app
.hid_provider
.send_key(*keycode, matches!(*action, PressRelease::Press)),
ButtonAction::Toast {
message,
body,
+2 -1
View File
@@ -7,6 +7,7 @@ use input_linux::{
};
use libc::{input_event, timeval};
use once_cell::sync::Lazy;
use serde::Deserialize;
use std::mem::transmute;
use std::{fs::File, sync::atomic::AtomicBool};
use strum::{EnumIter, EnumString, IntoEnumIterator};
@@ -316,7 +317,7 @@ pub const META: KeyModifier = 0x80;
#[allow(non_camel_case_types)]
#[repr(u16)]
#[derive(Debug, PartialEq, Clone, Copy, IntegerId, EnumString, EnumIter)]
#[derive(Debug, Deserialize, PartialEq, Clone, Copy, IntegerId, EnumString, EnumIter)]
pub enum VirtualKey {
Escape = 9,
N1, // number row
+69 -4
View File
@@ -6,12 +6,12 @@ use std::{
use crate::{
backend::{
input::PointerMode,
overlay::{OverlayData, OverlayState},
input::{InteractionHandler, PointerMode},
overlay::{OverlayBackend, OverlayData, OverlayRenderer, OverlayState},
},
config::{self, ConfigType},
gui::{
canvas::{builder::CanvasBuilder, control::Control},
canvas::{builder::CanvasBuilder, control::Control, Canvas},
color_parse, KeyCapType,
},
hid::{
@@ -180,7 +180,7 @@ where
interaction_transform,
..Default::default()
},
backend: Box::new(canvas),
backend: Box::new(KeyboardBackend { canvas }),
..Default::default()
})
}
@@ -435,3 +435,68 @@ fn key_events_for_macro(macro_verbs: &Vec<String>) -> Vec<(VirtualKey, bool)> {
}
key_events
}
struct KeyboardBackend {
canvas: Canvas<KeyboardData, KeyButtonData>,
}
impl OverlayBackend for KeyboardBackend {
fn set_interaction(&mut self, interaction: Box<dyn crate::backend::input::InteractionHandler>) {
self.canvas.set_interaction(interaction)
}
fn set_renderer(&mut self, renderer: Box<dyn crate::backend::overlay::OverlayRenderer>) {
self.canvas.set_renderer(renderer)
}
}
impl InteractionHandler for KeyboardBackend {
fn on_pointer(
&mut self,
app: &mut AppState,
hit: &crate::backend::input::PointerHit,
pressed: bool,
) {
self.canvas.on_pointer(app, hit, pressed)
}
fn on_scroll(
&mut self,
app: &mut AppState,
hit: &crate::backend::input::PointerHit,
delta: f32,
) {
self.canvas.on_scroll(app, hit, delta)
}
fn on_left(&mut self, app: &mut AppState, pointer: usize) {
self.canvas.on_left(app, pointer)
}
fn on_hover(
&mut self,
app: &mut AppState,
hit: &crate::backend::input::PointerHit,
) -> Option<crate::backend::input::Haptics> {
self.canvas.on_hover(app, hit)
}
}
impl OverlayRenderer for KeyboardBackend {
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.canvas.init(app)
}
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.canvas.render(app)
}
fn extent(&mut self) -> Option<[u32; 3]> {
self.canvas.extent()
}
fn view(&mut self) -> Option<std::sync::Arc<vulkano::image::view::ImageView>> {
self.canvas.view()
}
fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.canvas.data_mut().modifiers = 0;
app.hid_provider.set_modifiers(0);
self.canvas.pause(app)
}
fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> {
self.canvas.resume(app)
}
}
+75 -14
View File
@@ -18,6 +18,7 @@ use wlx_capture::{
DrmFormat, FrameFormat, MouseMeta, WlxFrame, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ABGR8888,
DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR2101010, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888,
},
pipewire::PipewireSelectScreenResult,
WlxCapture,
};
@@ -25,8 +26,8 @@ use wlx_capture::{
use {
crate::config_io,
std::error::Error,
std::{ops::Deref, path::PathBuf},
wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture},
std::{ops::Deref, path::PathBuf, task},
wlx_capture::pipewire::PipewireCapture,
};
#[cfg(all(feature = "x11", feature = "pipewire"))]
@@ -337,21 +338,22 @@ impl ScreenRenderer {
)> {
let name = output.name.clone();
let embed_mouse = !session.config.double_cursor_fix;
log::info!(
"On screen share prompt, pick: {} {} {} (pos {}, {})",
&output.name,
&output.make,
&output.model,
&output.logical_pos.0,
&output.logical_pos.1,
);
let select_screen_result = futures::executor::block_on(pipewire_select_screen(
let select_screen_result = select_pw_screen(
&format!(
"Now select: {} {} {} @ {},{}",
&output.name,
&output.make,
&output.model,
&output.logical_pos.0,
&output.logical_pos.1
),
token,
embed_mouse,
true,
true,
false,
))?;
)?;
let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
@@ -866,8 +868,16 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
let pw_tokens_copy = pw_tokens.clone();
let token = pw_tokens.arc_get("x11").map(|s| s.as_str());
let embed_mouse = !app.session.config.double_cursor_fix;
let select_screen_result =
futures::executor::block_on(pipewire_select_screen(token, embed_mouse, true, true, true))?;
let select_screen_result = select_pw_screen(
"Select ALL screens on the screencast pop-up!",
token,
embed_mouse,
true,
true,
true,
)?;
if let Some(restore_token) = select_screen_result.restore_token {
if pw_tokens.arc_set("x11".into(), restore_token.clone()) {
log::info!("Adding Pipewire token {}", restore_token);
@@ -1066,3 +1076,54 @@ fn best_match<'a>(
log::debug!("best: {:?}", best.map(|b| &b.monitor));
best
}
#[cfg(feature = "pipewire")]
fn select_pw_screen(
instructions: &str,
token: Option<&str>,
embed_mouse: bool,
screens_only: bool,
persist: bool,
multiple: bool,
) -> Result<PipewireSelectScreenResult, wlx_capture::pipewire::AshpdError> {
use crate::backend::notifications::DbusNotificationSender;
use wlx_capture::pipewire::pipewire_select_screen;
let future = async move {
let print_at = Instant::now() + Duration::from_millis(250);
let mut notify = None;
let f = pipewire_select_screen(token, embed_mouse, screens_only, persist, multiple);
futures::pin_mut!(f);
loop {
match futures::poll!(&mut f) {
task::Poll::Ready(result) => return result,
task::Poll::Pending => {
if Instant::now() >= print_at {
log::info!("{}", instructions);
if let Ok(sender) = DbusNotificationSender::new() {
if let Ok(id) = sender.notify_send(instructions, "", 2, 0, 0, true) {
notify = Some((sender, id));
}
}
break;
}
futures::future::lazy(|_| {
std::thread::sleep(Duration::from_millis(10));
})
.await;
continue;
}
}
}
let result = f.await;
if let Some((sender, id)) = notify {
let _ = sender.notify_close(id);
}
result
};
futures::executor::block_on(future)
}