Merge remote-tracking branch 'origin/wlvk' into next-dash-interface

[skip ci]
This commit is contained in:
Aleksander
2025-12-25 13:58:30 +01:00
42 changed files with 1213 additions and 3271 deletions

162
Cargo.lock generated
View File

@@ -376,16 +376,16 @@ dependencies = [
"futures-lite", "futures-lite",
"parking", "parking",
"polling", "polling",
"rustix 1.1.2", "rustix 1.1.3",
"slab", "slab",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "async-lock" name = "async-lock"
version = "3.4.1" version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [ dependencies = [
"event-listener", "event-listener",
"event-listener-strategy", "event-listener-strategy",
@@ -418,7 +418,7 @@ dependencies = [
"cfg-if", "cfg-if",
"event-listener", "event-listener",
"futures-lite", "futures-lite",
"rustix 1.1.2", "rustix 1.1.3",
] ]
[[package]] [[package]]
@@ -444,7 +444,7 @@ dependencies = [
"cfg-if", "cfg-if",
"futures-core", "futures-core",
"futures-io", "futures-io",
"rustix 1.1.2", "rustix 1.1.3",
"signal-hook-registry", "signal-hook-registry",
"slab", "slab",
"windows-sys 0.61.2", "windows-sys 0.61.2",
@@ -874,7 +874,7 @@ checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"polling", "polling",
"rustix 1.1.2", "rustix 1.1.3",
"slab", "slab",
"tracing", "tracing",
] ]
@@ -898,16 +898,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
dependencies = [ dependencies = [
"calloop 0.14.3", "calloop 0.14.3",
"rustix 1.1.2", "rustix 1.1.3",
"wayland-backend", "wayland-backend",
"wayland-client", "wayland-client",
] ]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.49" version = "1.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -1637,46 +1637,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
[[package]]
name = "drm"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
"drm-ffi",
"drm-fourcc",
"libc",
"rustix 0.38.44",
]
[[package]]
name = "drm-ffi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b"
dependencies = [
"drm-sys",
"rustix 0.38.44",
]
[[package]] [[package]]
name = "drm-fourcc" name = "drm-fourcc"
version = "2.2.0" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
[[package]]
name = "drm-sys"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c"
dependencies = [
"libc",
"linux-raw-sys 0.6.5",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.15.0" version = "1.15.0"
@@ -2216,7 +2182,7 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [ dependencies = [
"rustix 1.1.2", "rustix 1.1.3",
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
@@ -2321,17 +2287,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "gl_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
dependencies = [
"khronos_api",
"log",
"xml-rs",
]
[[package]] [[package]]
name = "glam" name = "glam"
version = "0.30.9" version = "0.30.9"
@@ -2974,9 +2929,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.15" version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
[[package]] [[package]]
name = "jiff" name = "jiff"
@@ -3071,22 +3026,6 @@ dependencies = [
"ucd-trie", "ucd-trie",
] ]
[[package]]
name = "khronos-egl"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "khronos_api"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]] [[package]]
name = "kurbo" name = "kurbo"
version = "0.11.3" version = "0.11.3"
@@ -3234,12 +3173,6 @@ version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.11.0" version = "0.11.0"
@@ -3530,9 +3463,9 @@ dependencies = [
[[package]] [[package]]
name = "ntapi" name = "ntapi"
version = "0.4.1" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]
@@ -4315,7 +4248,7 @@ dependencies = [
"concurrent-queue", "concurrent-queue",
"hermit-abi 0.5.2", "hermit-abi 0.5.2",
"pin-project-lite", "pin-project-lite",
"rustix 1.1.2", "rustix 1.1.3",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
@@ -4541,9 +4474,9 @@ dependencies = [
[[package]] [[package]]
name = "rangemap" name = "rangemap"
version = "1.7.0" version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbbbbea733ec66275512d0b9694f34102e7d5406fdbe2ad8d21b28dce92887c" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
[[package]] [[package]]
name = "rav1e" name = "rav1e"
@@ -4877,9 +4810,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.1.2" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"errno", "errno",
@@ -4905,9 +4838,9 @@ dependencies = [
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.20" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
[[package]] [[package]]
name = "same-file" name = "same-file"
@@ -5005,15 +4938,15 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.145" version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu",
"serde", "serde",
"serde_core", "serde_core",
"zmij",
] ]
[[package]] [[package]]
@@ -5221,24 +5154,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740cea6927892bc182d5bf70c8f79806c8bc9f68f2fb96e55a30be171b63af98" checksum = "740cea6927892bc182d5bf70c8f79806c8bc9f68f2fb96e55a30be171b63af98"
dependencies = [ dependencies = [
"appendlist", "appendlist",
"ash",
"atomic_float", "atomic_float",
"bitflags 2.10.0", "bitflags 2.10.0",
"calloop 0.14.3", "calloop 0.14.3",
"cgmath", "cgmath",
"cursor-icon", "cursor-icon",
"downcast-rs", "downcast-rs",
"drm",
"drm-ffi",
"drm-fourcc", "drm-fourcc",
"encoding_rs", "encoding_rs",
"errno", "errno",
"gl_generator",
"indexmap 2.12.1", "indexmap 2.12.1",
"libc", "libc",
"libloading",
"profiling", "profiling",
"rand", "rand",
"rustix 1.1.2", "rustix 1.1.3",
"scopeguard", "scopeguard",
"sha2", "sha2",
"smallvec", "smallvec",
@@ -5293,7 +5223,7 @@ dependencies = [
"log", "log",
"memmap2", "memmap2",
"pkg-config", "pkg-config",
"rustix 1.1.2", "rustix 1.1.3",
"thiserror 2.0.17", "thiserror 2.0.17",
"wayland-backend", "wayland-backend",
"wayland-client", "wayland-client",
@@ -5570,14 +5500,14 @@ checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.23.0" version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"getrandom 0.3.4", "getrandom 0.3.4",
"once_cell", "once_cell",
"rustix 1.1.2", "rustix 1.1.3",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
@@ -6275,7 +6205,7 @@ checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
dependencies = [ dependencies = [
"cc", "cc",
"downcast-rs", "downcast-rs",
"rustix 1.1.2", "rustix 1.1.3",
"scoped-tls", "scoped-tls",
"smallvec", "smallvec",
"wayland-sys", "wayland-sys",
@@ -6288,7 +6218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"rustix 1.1.2", "rustix 1.1.3",
"wayland-backend", "wayland-backend",
"wayland-scanner", "wayland-scanner",
] ]
@@ -6310,21 +6240,11 @@ version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29"
dependencies = [ dependencies = [
"rustix 1.1.2", "rustix 1.1.3",
"wayland-client", "wayland-client",
"xcursor", "xcursor",
] ]
[[package]]
name = "wayland-egl"
version = "0.32.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36232ee23ba3ea34a6835d68ca1af91d3ca3d6eddcf9c7147c4e0e66901b9fd"
dependencies = [
"wayland-backend",
"wayland-sys",
]
[[package]] [[package]]
name = "wayland-protocols" name = "wayland-protocols"
version = "0.32.9" version = "0.32.9"
@@ -6411,7 +6331,7 @@ checksum = "fcbd4f3aba6c9fba70445ad2a484c0ef0356c1a9459b1e8e435bedc1971a6222"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"downcast-rs", "downcast-rs",
"rustix 1.1.2", "rustix 1.1.3",
"wayland-backend", "wayland-backend",
"wayland-scanner", "wayland-scanner",
] ]
@@ -7094,7 +7014,6 @@ dependencies = [
"interprocess", "interprocess",
"json", "json",
"json5 1.3.0", "json5 1.3.0",
"khronos-egl",
"libc", "libc",
"libmonado", "libmonado",
"log", "log",
@@ -7123,7 +7042,6 @@ dependencies = [
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",
"wayland-client", "wayland-client",
"wayland-egl",
"wayvr-ipc", "wayvr-ipc",
"wgui", "wgui",
"winit", "winit",
@@ -7162,7 +7080,7 @@ dependencies = [
"libc", "libc",
"libloading", "libloading",
"once_cell", "once_cell",
"rustix 1.1.2", "rustix 1.1.3",
"x11rb-protocol", "x11rb-protocol",
] ]
@@ -7467,6 +7385,12 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "zmij"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e404bcd8afdaf006e529269d3e85a743f9480c3cef60034d77860d02964f3ba"
[[package]] [[package]]
name = "zune-core" name = "zune-core"
version = "0.5.0" version = "0.5.0"
@@ -7484,9 +7408,9 @@ dependencies = [
[[package]] [[package]]
name = "zune-jpeg" name = "zune-jpeg"
version = "0.5.7" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d915729b0e7d5fe35c2f294c5dc10b30207cc637920e5b59077bfa3da63f28" checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
dependencies = [ dependencies = [
"zune-core", "zune-core",
] ]

View File

@@ -315,93 +315,16 @@ impl WayVRClient {
} }
} }
pub async fn fn_wvr_display_list( pub async fn fn_wvr_window_list(
client: WayVRClientMutex, client: WayVRClientMutex,
serial: Serial, serial: Serial,
) -> anyhow::Result<Vec<packet_server::WvrDisplay>> {
Ok(
send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayList(serial),
WvrDisplayListResponse
)
.list,
)
}
pub async fn fn_wvr_display_get(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<Option<packet_server::WvrDisplay>> {
Ok(send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayGet(serial, handle),
WvrDisplayGetResponse
))
}
pub async fn fn_wvr_display_remove(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayRemove(serial, handle),
WvrDisplayRemoveResponse
)
.map_err(|e| anyhow::anyhow!("{}", e))
}
pub async fn fn_wvr_display_create(
client: WayVRClientMutex,
serial: Serial,
params: packet_client::WvrDisplayCreateParams,
) -> anyhow::Result<packet_server::WvrDisplayHandle> {
Ok(send_and_wait!(
client,
serial,
&PacketClient::WvrDisplayCreate(serial, params),
WvrDisplayCreateResponse
))
}
pub async fn fn_wvr_display_set_visible(
client: WayVRClientMutex,
handle: packet_server::WvrDisplayHandle,
visible: bool,
) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WvrDisplaySetVisible(handle, visible));
Ok(())
}
pub async fn fn_wvr_display_set_layout(
client: WayVRClientMutex,
handle: packet_server::WvrDisplayHandle,
layout: packet_server::WvrDisplayWindowLayout,
) -> anyhow::Result<()> {
send_only!(
client,
&PacketClient::WvrDisplaySetWindowLayout(handle, layout)
);
Ok(())
}
pub async fn fn_wvr_display_window_list(
client: WayVRClientMutex,
serial: Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<Option<Vec<packet_server::WvrWindow>>> { ) -> anyhow::Result<Option<Vec<packet_server::WvrWindow>>> {
Ok( Ok(
send_and_wait!( send_and_wait!(
client, client,
serial, serial,
&PacketClient::WvrDisplayWindowList(serial, handle), &PacketClient::WvrWindowList(serial),
WvrDisplayWindowListResponse WvrWindowListResponse
) )
.map(|res| res.list), .map(|res| res.list),
) )
@@ -478,11 +401,12 @@ impl WayVRClient {
)) ))
} }
pub async fn fn_wlx_haptics( pub async fn fn_wlx_device_haptics(
client: WayVRClientMutex, client: WayVRClientMutex,
device: usize,
params: packet_client::WlxHapticsParams, params: packet_client::WlxHapticsParams,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WlxHaptics(params)); send_only!(client, &PacketClient::WlxDeviceHaptics(device, params));
Ok(()) Ok(())
} }

View File

@@ -67,22 +67,13 @@ pub struct WlxModifyPanelParams {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PacketClient { pub enum PacketClient {
Handshake(Handshake), Handshake(Handshake),
WvrDisplayCreate(Serial, WvrDisplayCreateParams), WvrWindowList(Serial),
WvrDisplayGet(Serial, packet_server::WvrDisplayHandle),
WvrDisplayList(Serial),
WvrDisplayRemove(Serial, packet_server::WvrDisplayHandle),
WvrDisplaySetVisible(packet_server::WvrDisplayHandle, bool),
WvrDisplayWindowList(Serial, packet_server::WvrDisplayHandle),
WvrDisplaySetWindowLayout(
packet_server::WvrDisplayHandle,
packet_server::WvrDisplayWindowLayout,
),
WvrWindowSetVisible(packet_server::WvrWindowHandle, bool), WvrWindowSetVisible(packet_server::WvrWindowHandle, bool),
WvrProcessGet(Serial, packet_server::WvrProcessHandle), WvrProcessGet(Serial, packet_server::WvrProcessHandle),
WvrProcessLaunch(Serial, WvrProcessLaunchParams), WvrProcessLaunch(Serial, WvrProcessLaunchParams),
WvrProcessList(Serial), WvrProcessList(Serial),
WvrProcessTerminate(packet_server::WvrProcessHandle), WvrProcessTerminate(packet_server::WvrProcessHandle),
WlxHaptics(WlxHapticsParams),
WlxInputState(Serial), WlxInputState(Serial),
WlxModifyPanel(WlxModifyPanelParams), WlxModifyPanel(WlxModifyPanelParams),
WlxDeviceHaptics(usize, WlxHapticsParams),
} }

View File

@@ -48,14 +48,11 @@ pub struct WvrDisplay {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrWindow { pub struct WvrWindow {
pub pos_x: i32,
pub pos_y: i32,
pub size_x: u32, pub size_x: u32,
pub size_y: u32, pub size_y: u32,
pub visible: bool, pub visible: bool,
pub handle: WvrWindowHandle, pub handle: WvrWindowHandle,
pub process_handle: WvrProcessHandle, pub process_handle: WvrProcessHandle,
pub display_handle: WvrDisplayHandle,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -71,7 +68,6 @@ pub struct WvrWindowList {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WvrProcess { pub struct WvrProcess {
pub name: String, pub name: String,
pub display_handle: WvrDisplayHandle,
pub handle: WvrProcessHandle, pub handle: WvrProcessHandle,
pub userdata: HashMap<String, String>, pub userdata: HashMap<String, String>,
} }
@@ -103,8 +99,6 @@ pub enum WvrDisplayWindowLayout {
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum WvrStateChanged { pub enum WvrStateChanged {
DisplayCreated,
DisplayRemoved,
ProcessCreated, ProcessCreated,
ProcessRemoved, ProcessRemoved,
WindowCreated, WindowCreated,
@@ -132,11 +126,7 @@ pub enum PacketServer {
Disconnect(Disconnect), Disconnect(Disconnect),
HandshakeSuccess(HandshakeSuccess), HandshakeSuccess(HandshakeSuccess),
WlxInputStateResponse(Serial, WlxInputState), WlxInputStateResponse(Serial, WlxInputState),
WvrDisplayCreateResponse(Serial, WvrDisplayHandle), WvrWindowListResponse(Serial, Option<WvrWindowList>),
WvrDisplayGetResponse(Serial, Option<WvrDisplay>),
WvrDisplayListResponse(Serial, WvrDisplayList),
WvrDisplayRemoveResponse(Serial, Result<(), String>),
WvrDisplayWindowListResponse(Serial, Option<WvrWindowList>),
WvrProcessGetResponse(Serial, Option<WvrProcess>), WvrProcessGetResponse(Serial, Option<WvrProcess>),
WvrProcessLaunchResponse(Serial, Result<WvrProcessHandle, String>), WvrProcessLaunchResponse(Serial, Result<WvrProcessHandle, String>),
WvrProcessListResponse(Serial, WvrProcessList), WvrProcessListResponse(Serial, WvrProcessList),
@@ -149,11 +139,7 @@ impl PacketServer {
PacketServer::Disconnect(_) => None, PacketServer::Disconnect(_) => None,
PacketServer::HandshakeSuccess(_) => None, PacketServer::HandshakeSuccess(_) => None,
PacketServer::WlxInputStateResponse(serial, _) => Some(serial), PacketServer::WlxInputStateResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayCreateResponse(serial, _) => Some(serial), PacketServer::WvrWindowListResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayGetResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayListResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayRemoveResponse(serial, _) => Some(serial),
PacketServer::WvrDisplayWindowListResponse(serial, _) => Some(serial),
PacketServer::WvrProcessGetResponse(serial, _) => Some(serial), PacketServer::WvrProcessGetResponse(serial, _) => Some(serial),
PacketServer::WvrProcessLaunchResponse(serial, _) => Some(serial), PacketServer::WvrProcessLaunchResponse(serial, _) => Some(serial),
PacketServer::WvrProcessListResponse(serial, _) => Some(serial), PacketServer::WvrProcessListResponse(serial, _) => Some(serial),

View File

@@ -37,104 +37,18 @@ fn handle_result<T: Serialize>(pretty_print: bool, result: anyhow::Result<T>) {
} }
} }
pub async fn wvr_display_create( pub async fn wvr_window_list(state: &mut WayVRClientState) {
state: &mut WayVRClientState,
width: u16,
height: u16,
name: String,
scale: Option<f32>,
attach_to: packet_client::AttachTo,
) {
handle_result( handle_result(
state.pretty_print, state.pretty_print,
WayVRClient::fn_wvr_display_create( WayVRClient::fn_wvr_window_list(
state.wayvr_client.clone(), state.wayvr_client.clone(),
state.serial_generator.increment_get(), state.serial_generator.increment_get(),
packet_client::WvrDisplayCreateParams {
width,
height,
name,
scale,
attach_to,
},
)
.await
.context("failed to create display"),
);
}
pub async fn wvr_display_list(state: &mut WayVRClientState) {
handle_result(
state.pretty_print,
WayVRClient::fn_wvr_display_list(
state.wayvr_client.clone(),
state.serial_generator.increment_get(),
)
.await
.context("failed to fetch displays"),
);
}
pub async fn wvr_display_get(
state: &mut WayVRClientState,
handle: packet_server::WvrDisplayHandle,
) {
handle_result(
state.pretty_print,
WayVRClient::fn_wvr_display_get(
state.wayvr_client.clone(),
state.serial_generator.increment_get(),
handle,
)
.await
.context("failed to fetch display"),
);
}
pub async fn wvr_display_window_list(
state: &mut WayVRClientState,
handle: packet_server::WvrDisplayHandle,
) {
handle_result(
state.pretty_print,
WayVRClient::fn_wvr_display_window_list(
state.wayvr_client.clone(),
state.serial_generator.increment_get(),
handle,
) )
.await .await
.context("failed to list window displays"), .context("failed to list window displays"),
); );
} }
pub async fn wvr_display_remove(
state: &mut WayVRClientState,
handle: packet_server::WvrDisplayHandle,
) {
handle_result(
state.pretty_print,
WayVRClient::fn_wvr_display_remove(
state.wayvr_client.clone(),
state.serial_generator.increment_get(),
handle,
)
.await
.context("failed to remove display"),
);
}
pub async fn wvr_display_set_visible(
state: &mut WayVRClientState,
handle: packet_server::WvrDisplayHandle,
visible: bool,
) {
handle_empty_result(
WayVRClient::fn_wvr_display_set_visible(state.wayvr_client.clone(), handle, visible)
.await
.context("failed to set display visibility"),
)
}
pub async fn wvr_window_set_visible( pub async fn wvr_window_set_visible(
state: &mut WayVRClientState, state: &mut WayVRClientState,
handle: packet_server::WvrWindowHandle, handle: packet_server::WvrWindowHandle,
@@ -214,15 +128,17 @@ pub async fn wvr_process_launch(
) )
} }
pub async fn wlx_haptics( pub async fn wlx_device_haptics(
state: &mut WayVRClientState, state: &mut WayVRClientState,
device: usize,
intensity: f32, intensity: f32,
duration: f32, duration: f32,
frequency: f32, frequency: f32,
) { ) {
handle_empty_result( handle_empty_result(
WayVRClient::fn_wlx_haptics( WayVRClient::fn_wlx_device_haptics(
state.wayvr_client.clone(), state.wayvr_client.clone(),
device,
packet_client::WlxHapticsParams { packet_client::WlxHapticsParams {
intensity, intensity,
duration, duration,

View File

@@ -10,10 +10,9 @@ use env_logger::Env;
use wayvr_ipc::{client::WayVRClient, ipc, packet_client}; use wayvr_ipc::{client::WayVRClient, ipc, packet_client};
use crate::helper::{ use crate::helper::{
WayVRClientState, wlx_haptics, wlx_input_state, wlx_panel_modify, wvr_display_create, WayVRClientState, wlx_device_haptics, wlx_input_state, wlx_panel_modify, wvr_process_get,
wvr_display_get, wvr_display_list, wvr_display_remove, wvr_display_set_visible, wvr_process_launch, wvr_process_list, wvr_process_terminate, wvr_window_list,
wvr_display_window_list, wvr_process_get, wvr_process_launch, wvr_process_list, wvr_window_set_visible,
wvr_process_terminate, wvr_window_set_visible,
}; };
mod helper; mod helper;
@@ -76,12 +75,12 @@ async fn run_batch(state: &mut WayVRClientState, fail_fast: bool) -> anyhow::Res
} }
async fn parse_run_line(state: &mut WayVRClientState, line: &str) -> anyhow::Result<()> { async fn parse_run_line(state: &mut WayVRClientState, line: &str) -> anyhow::Result<()> {
let mut argv = shell_words::split(&line).with_context(|| format!("parse error"))?; let mut argv = shell_words::split(line).context("parse error")?;
// clap expects argv[0] to be the binary name // clap expects argv[0] to be the binary name
argv.insert(0, env!("CARGO_PKG_NAME").to_string()); argv.insert(0, env!("CARGO_PKG_NAME").to_string());
let args = Args::try_parse_from(argv).with_context(|| format!("invalid arguments"))?; let args = Args::try_parse_from(argv).context("invalid arguments")?;
run_once(state, args).await?; run_once(state, args).await?;
Ok(()) Ok(())
@@ -95,43 +94,8 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
Subcommands::InputState => { Subcommands::InputState => {
wlx_input_state(state).await; wlx_input_state(state).await;
} }
Subcommands::DisplayCreate { Subcommands::WindowList => {
width, wvr_window_list(state).await;
height,
name,
scale,
} => {
wvr_display_create(
state,
width,
height,
name,
scale,
packet_client::AttachTo::None,
)
.await;
}
Subcommands::DisplayList => {
wvr_display_list(state).await;
}
Subcommands::DisplayGet { handle } => {
let handle = serde_json::from_str(&handle).context("Invalid handle")?;
wvr_display_get(state, handle).await;
}
Subcommands::DisplayWindowList { handle } => {
let handle = serde_json::from_str(&handle).context("Invalid handle")?;
wvr_display_window_list(state, handle).await;
}
Subcommands::DisplayRemove { handle } => {
let handle = serde_json::from_str(&handle).context("Invalid handle")?;
wvr_display_remove(state, handle).await;
}
Subcommands::DisplaySetVisible {
handle,
visible_0_or_1,
} => {
let handle = serde_json::from_str(&handle).context("Invalid handle")?;
wvr_display_set_visible(state, handle, visible_0_or_1 != 0).await;
} }
Subcommands::WindowSetVisible { Subcommands::WindowSetVisible {
handle, handle,
@@ -162,11 +126,12 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
wvr_process_launch(state, exec, name, env, handle, args, HashMap::new()).await; wvr_process_launch(state, exec, name, env, handle, args, HashMap::new()).await;
} }
Subcommands::Haptics { Subcommands::Haptics {
device,
intensity, intensity,
duration, duration,
frequency, frequency,
} => { } => {
wlx_haptics(state, intensity, duration, frequency).await; wlx_device_haptics(state, device, intensity, duration, frequency).await;
} }
Subcommands::PanelModify { Subcommands::PanelModify {
overlay, overlay,
@@ -220,39 +185,9 @@ enum Subcommands {
}, },
/// Get the positions of HMD & controllers /// Get the positions of HMD & controllers
InputState, InputState,
/// Create a new WayVR display /// List WayVR windows
DisplayCreate { WindowList,
width: u16,
height: u16,
name: String,
#[arg(short, long)]
scale: Option<f32>,
//attach_to: packet_client::AttachTo,
},
/// List WayVR displays
DisplayList,
/// Retrieve information about a single WayVR display
DisplayGet {
/// A display handle JSON returned by DisplayList or DisplayCreate
handle: String,
},
/// List windows attached to a WayVR display
DisplayWindowList {
/// A display handle JSON returned by DisplayList or DisplayCreate
handle: String,
},
/// Delete a WayVR display /// Delete a WayVR display
DisplayRemove {
/// A display handle JSON returned by DisplayList or DisplayCreate
handle: String,
},
/// Change the visibility of a WayVR display
DisplaySetVisible {
/// A display handle JSON returned by DisplayList or DisplayCreate
handle: String,
visible_0_or_1: u8,
},
// DisplaySetLayout skipped // DisplaySetLayout skipped
/// Change the visibility of a window on a WayVR display /// Change the visibility of a window on a WayVR display
WindowSetVisible { WindowSetVisible {
@@ -283,6 +218,8 @@ enum Subcommands {
}, },
/// Trigger haptics on the user's controller /// Trigger haptics on the user's controller
Haptics { Haptics {
/// 0 for left, 1 for right controller
device: usize,
#[arg(short, long, default_value = "0.25")] #[arg(short, long, default_value = "0.25")]
intensity: f32, intensity: f32,
#[arg(short, long, default_value = "0.1")] #[arg(short, long, default_value = "0.1")]

View File

@@ -1,47 +1,6 @@
use std::{fmt::Display, os::fd::RawFd}; use std::os::fd::RawFd;
#[derive(Debug, Clone, Copy, Default)] use drm_fourcc::{DrmFormat, DrmModifier};
pub struct FourCC {
pub value: u32,
}
impl PartialEq for FourCC {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl From<u32> for FourCC {
fn from(value: u32) -> Self {
Self { value }
}
}
impl From<FourCC> for u32 {
fn from(fourcc: FourCC) -> Self {
fourcc.value
}
}
impl Display for FourCC {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for i in 0..4 {
if let Some(c) = char::from_u32((self.value >> (i * 8)) & 0xFF) {
write!(f, "{c}")?
} else {
write!(f, "?")?
}
}
Ok(())
}
}
pub const DRM_FORMAT_ARGB8888: u32 = 0x34325241; // AR24
pub const DRM_FORMAT_ABGR8888: u32 = 0x34324241; // AB24
pub const DRM_FORMAT_XRGB8888: u32 = 0x34325258; // XR24
pub const DRM_FORMAT_XBGR8888: u32 = 0x34324258; // XB24
pub const DRM_FORMAT_ABGR2101010: u32 = 0x30334241; // AB30
pub const DRM_FORMAT_XBGR2101010: u32 = 0x30334258; // XB30
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
#[rustfmt::skip] #[rustfmt::skip]
@@ -73,24 +32,27 @@ pub enum Transform {
Flipped270, Flipped270,
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy)]
pub struct FrameFormat { pub struct FrameFormat {
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,
pub fourcc: FourCC, pub drm_format: DrmFormat,
pub modifier: u64,
pub transform: Transform, pub transform: Transform,
} }
impl FrameFormat { impl FrameFormat {
#[must_use]
pub fn get_mod_hi(&self) -> u32 { pub fn get_mod_hi(&self) -> u32 {
(self.modifier >> 32) as _ let m = u64::from(self.drm_format.modifier);
(m >> 32) as _
} }
#[must_use]
pub fn get_mod_lo(&self) -> u32 { pub fn get_mod_lo(&self) -> u32 {
(self.modifier & 0xFFFFFFFF) as _ let m = u64::from(self.drm_format.modifier);
(m & 0xFFFFFFFF) as _
} }
pub fn set_mod(&mut self, mod_hi: u32, mod_low: u32) { pub fn set_mod(&mut self, mod_hi: u32, mod_low: u32) {
self.modifier = ((mod_hi as u64) << 32) + mod_low as u64; self.drm_format.modifier = DrmModifier::from(((mod_hi as u64) << 32) + mod_low as u64);
} }
} }
@@ -101,13 +63,6 @@ pub struct FramePlane {
pub stride: i32, pub stride: i32,
} }
#[derive(Default, Clone)]
pub struct DrmFormat {
pub fourcc: FourCC,
pub modifiers: Vec<u64>,
}
#[derive(Default)]
pub struct DmabufFrame { pub struct DmabufFrame {
pub format: FrameFormat, pub format: FrameFormat,
pub num_planes: usize, pub num_planes: usize,
@@ -126,7 +81,7 @@ impl DmabufFrame {
0x3056, // HEIGHT 0x3056, // HEIGHT
self.format.height as _, self.format.height as _,
0x3271, // LINUX_DRM_FOURCC_EXT, 0x3271, // LINUX_DRM_FOURCC_EXT,
self.format.fourcc.value as _, self.format.drm_format.code as _,
]; ];
for i in 0..self.num_planes { for i in 0..self.num_planes {
@@ -162,14 +117,12 @@ impl DmabufFrame {
} }
} }
#[derive(Default)]
pub struct MemFdFrame { pub struct MemFdFrame {
pub format: FrameFormat, pub format: FrameFormat,
pub plane: FramePlane, pub plane: FramePlane,
pub mouse: Option<MouseMeta>, pub mouse: Option<MouseMeta>,
} }
#[derive(Default)]
pub struct MemPtrFrame { pub struct MemPtrFrame {
pub format: FrameFormat, pub format: FrameFormat,
pub ptr: usize, pub ptr: usize,

View File

@@ -1,7 +1,8 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(clippy::expect_fun_call)] #![allow(clippy::expect_fun_call)]
use frame::{DrmFormat, WlxFrame}; pub use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
use frame::WlxFrame;
pub mod frame; pub mod frame;

View File

@@ -1,4 +1,5 @@
use std::any::Any; use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::AtomicU32; use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -12,6 +13,9 @@ use ashpd::desktop::{
pub use ashpd::Error as AshpdError; pub use ashpd::Error as AshpdError;
use drm_fourcc::DrmFormat;
use drm_fourcc::DrmFourcc;
use drm_fourcc::DrmModifier;
use pipewire as pw; use pipewire as pw;
use pw::spa; use pw::spa;
@@ -33,14 +37,6 @@ use spa::utils::ChoiceEnum;
use spa::utils::ChoiceFlags; use spa::utils::ChoiceFlags;
use crate::WlxCapture; use crate::WlxCapture;
use crate::frame::DRM_FORMAT_ABGR8888;
use crate::frame::DRM_FORMAT_ABGR2101010;
use crate::frame::DRM_FORMAT_ARGB8888;
use crate::frame::DRM_FORMAT_XBGR8888;
use crate::frame::DRM_FORMAT_XBGR2101010;
use crate::frame::DRM_FORMAT_XRGB8888;
use crate::frame::DrmFormat;
use crate::frame::FourCC;
use crate::frame::FrameFormat; use crate::frame::FrameFormat;
use crate::frame::MouseMeta; use crate::frame::MouseMeta;
use crate::frame::Transform; use crate::frame::Transform;
@@ -297,7 +293,15 @@ where
)?; )?;
let _listener = stream let _listener = stream
.add_local_listener_with_user_data(FrameFormat::default()) .add_local_listener_with_user_data(FrameFormat {
width: 0,
height: 0,
drm_format: DrmFormat {
code: DrmFourcc::Argb8888,
modifier: DrmModifier::Invalid,
},
transform: Transform::Undefined,
})
.state_changed({ .state_changed({
let name = name.clone(); let name = name.clone();
move |_, _, old, new| { move |_, _, old, new| {
@@ -320,10 +324,10 @@ where
format.width = info.size().width; format.width = info.size().width;
format.height = info.size().height; format.height = info.size().height;
format.fourcc = spa_to_fourcc(info.format()); format.drm_format.code = spa_to_fourcc(info.format());
format.modifier = info.modifier(); format.drm_format.modifier = DrmModifier::from(info.modifier());
let kind = if format.modifier != 0 { let kind = if info.modifier() != 0 {
"DMA-buf" "DMA-buf"
} else { } else {
"SHM" "SHM"
@@ -425,7 +429,7 @@ where
format: *format, format: *format,
num_planes: planes.len(), num_planes: planes.len(),
mouse: mouse_meta, mouse: mouse_meta,
..Default::default() planes: Default::default(),
}; };
dmabuf.planes[..planes.len()].copy_from_slice(&planes[..planes.len()]); dmabuf.planes[..planes.len()].copy_from_slice(&planes[..planes.len()]);
@@ -494,7 +498,17 @@ where
}) })
.register()?; .register()?;
let mut format_params: Vec<Vec<u8>> = dmabuf_formats let mut fourcc_mods: HashMap<DrmFourcc, Vec<DrmModifier>> = HashMap::new();
for f in dmabuf_formats.iter() {
if let Some(v) = fourcc_mods.get_mut(&f.code) {
v.push(f.modifier);
} else {
fourcc_mods.insert(f.code, vec![f.modifier]);
}
}
let mut format_params: Vec<Vec<u8>> = fourcc_mods
.iter() .iter()
.filter_map(|f| obj_to_bytes(get_format_params(Some(f))).ok()) .filter_map(|f| obj_to_bytes(get_format_params(Some(f))).ok())
.collect(); .collect();
@@ -591,7 +605,7 @@ fn get_meta_object(key: u32, size: usize) -> Object {
) )
} }
fn get_format_params(fmt: Option<&DrmFormat>) -> Object { fn get_format_params(fmt: Option<(&DrmFourcc, &Vec<DrmModifier>)>) -> Object {
let mut obj = spa::pod::object!( let mut obj = spa::pod::object!(
spa::utils::SpaTypes::ObjectParamFormat, spa::utils::SpaTypes::ObjectParamFormat,
spa::param::ParamType::EnumFormat, spa::param::ParamType::EnumFormat,
@@ -638,7 +652,7 @@ fn get_format_params(fmt: Option<&DrmFormat>) -> Object {
); );
if let Some(fmt) = fmt { if let Some(fmt) = fmt {
let spa_fmt = fourcc_to_spa(fmt.fourcc); let spa_fmt = fourcc_to_spa(*fmt.0);
let prop = spa::pod::property!( let prop = spa::pod::property!(
spa::param::format::FormatProperties::VideoFormat, spa::param::format::FormatProperties::VideoFormat,
@@ -657,8 +671,8 @@ fn get_format_params(fmt: Option<&DrmFormat>) -> Object {
value: Value::Choice(ChoiceValue::Long(Choice( value: Value::Choice(ChoiceValue::Long(Choice(
ChoiceFlags::empty(), ChoiceFlags::empty(),
ChoiceEnum::Enum { ChoiceEnum::Enum {
default: fmt.modifiers[0] as _, default: u64::from(fmt.1[0]) as _,
alternatives: fmt.modifiers.iter().map(|m| *m as _).collect(), alternatives: fmt.1.iter().map(|m| u64::from(*m) as _).collect(),
}, },
))), ))),
}; };
@@ -683,27 +697,27 @@ fn get_format_params(fmt: Option<&DrmFormat>) -> Object {
obj obj
} }
fn fourcc_to_spa(fourcc: FourCC) -> VideoFormat { fn fourcc_to_spa(fourcc: DrmFourcc) -> VideoFormat {
match fourcc.value { match fourcc{
DRM_FORMAT_ARGB8888 => VideoFormat::BGRA, DrmFourcc::Argb8888 => VideoFormat::BGRA,
DRM_FORMAT_ABGR8888 => VideoFormat::RGBA, DrmFourcc::Abgr8888 => VideoFormat::RGBA,
DRM_FORMAT_XRGB8888 => VideoFormat::BGRx, DrmFourcc::Xrgb8888 => VideoFormat::BGRx,
DRM_FORMAT_XBGR8888 => VideoFormat::RGBx, DrmFourcc::Xbgr8888 => VideoFormat::RGBx,
DRM_FORMAT_ABGR2101010 => VideoFormat::ABGR_210LE, DrmFourcc::Abgr2101010 => VideoFormat::ABGR_210LE,
DRM_FORMAT_XBGR2101010 => VideoFormat::xBGR_210LE, DrmFourcc::Xbgr2101010 => VideoFormat::xBGR_210LE,
_ => panic!("Unsupported format"), _ => panic!("Unsupported format"),
} }
} }
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
fn spa_to_fourcc(spa: VideoFormat) -> FourCC { fn spa_to_fourcc(spa: VideoFormat) -> DrmFourcc {
match spa { match spa {
VideoFormat::BGRA => DRM_FORMAT_ARGB8888.into(), VideoFormat::BGRA => DrmFourcc::Argb8888,
VideoFormat::RGBA => DRM_FORMAT_ABGR8888.into(), VideoFormat::RGBA => DrmFourcc::Abgr8888,
VideoFormat::BGRx => DRM_FORMAT_XRGB8888.into(), VideoFormat::BGRx => DrmFourcc::Xrgb8888,
VideoFormat::RGBx => DRM_FORMAT_XBGR8888.into(), VideoFormat::RGBx => DrmFourcc::Xbgr8888,
VideoFormat::ABGR_210LE => DRM_FORMAT_ABGR2101010.into(), VideoFormat::ABGR_210LE => DrmFourcc::Abgr2101010,
VideoFormat::xBGR_210LE => DRM_FORMAT_XBGR2101010.into(), VideoFormat::xBGR_210LE => DrmFourcc::Xbgr2101010,
_ => panic!("Unsupported format"), _ => panic!("Unsupported format"),
} }
} }

View File

@@ -6,12 +6,13 @@ use std::{
thread::JoinHandle, thread::JoinHandle,
}; };
use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
use smithay_client_toolkit::reexports::protocols_wlr::export_dmabuf::v1::client::zwlr_export_dmabuf_frame_v1::{self, ZwlrExportDmabufFrameV1}; use smithay_client_toolkit::reexports::protocols_wlr::export_dmabuf::v1::client::zwlr_export_dmabuf_frame_v1::{self, ZwlrExportDmabufFrameV1};
use wayland_client::{Connection, QueueHandle, Dispatch, Proxy}; use wayland_client::{Connection, QueueHandle, Dispatch, Proxy};
use crate::{ use crate::{
WlxCapture, WlxCapture,
frame::{DmabufFrame, DrmFormat, FramePlane, WlxFrame}, frame::{DmabufFrame, FrameFormat, FramePlane, WlxFrame},
wayland::WlxClient, wayland::WlxClient,
}; };
@@ -168,13 +169,27 @@ fn request_dmabuf_frame(
num_objects, num_objects,
.. ..
} => { } => {
let mut new_frame = DmabufFrame::default(); let mut new_frame = DmabufFrame {
new_frame.format.width = width; format: FrameFormat {
new_frame.format.height = height; width,
new_frame.format.fourcc.value = format; height,
drm_format: DrmFormat {
code: match DrmFourcc::try_from(format) {
Ok(code) => code,
Err(e) => {
log::error!("Unrecognized fourcc: {e:?}");
return;
}
},
modifier: DrmModifier::Invalid,
},
transform,
},
mouse: None,
num_planes: num_objects as _,
planes: Default::default(),
};
new_frame.format.set_mod(mod_high, mod_low); new_frame.format.set_mod(mod_high, mod_low);
new_frame.format.transform = transform;
new_frame.num_planes = num_objects as _;
frame = Some(new_frame); frame = Some(new_frame);
} }
zwlr_export_dmabuf_frame_v1::Event::Object { zwlr_export_dmabuf_frame_v1::Event::Object {

View File

@@ -1,3 +1,4 @@
use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
use libc::{O_CREAT, O_RDWR, S_IRUSR, S_IWUSR}; use libc::{O_CREAT, O_RDWR, S_IRUSR, S_IWUSR};
use std::{ use std::{
any::Any, any::Any,
@@ -18,10 +19,7 @@ use smithay_client_toolkit::reexports::protocols_wlr::screencopy::v1::client::zw
use crate::{ use crate::{
WlxCapture, WlxCapture,
frame::{ frame::{FrameFormat, FramePlane, MemFdFrame, WlxFrame},
DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888, DrmFormat, FourCC, FrameFormat, FramePlane,
MemFdFrame, WlxFrame,
},
wayland::WlxClient, wayland::WlxClient,
}; };
@@ -44,7 +42,7 @@ impl Drop for BufData {
enum ScreenCopyEvent { enum ScreenCopyEvent {
Buffer { Buffer {
data: BufData, data: BufData,
fourcc: FourCC, drm_format: DrmFormat,
width: u32, width: u32,
height: u32, height: u32,
stride: u32, stride: u32,
@@ -213,7 +211,7 @@ where
match event { match event {
ScreenCopyEvent::Buffer { ScreenCopyEvent::Buffer {
data, data,
fourcc, drm_format,
width, width,
height, height,
stride, stride,
@@ -222,16 +220,15 @@ where
format: FrameFormat { format: FrameFormat {
width, width,
height, height,
fourcc, drm_format,
transform, transform,
..Default::default()
}, },
plane: FramePlane { plane: FramePlane {
fd: Some(data.fd), fd: Some(data.fd),
offset: 0, offset: 0,
stride: stride as _, stride: stride as _,
}, },
..Default::default() mouse: None,
}; };
log::trace!("{}: Received screencopy buffer, copying", name.as_ref()); log::trace!("{}: Received screencopy buffer, copying", name.as_ref());
if wait_for_damage { if wait_for_damage {
@@ -331,7 +328,10 @@ impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient
wl_pool, wl_pool,
fd, fd,
}, },
fourcc, drm_format: DrmFormat {
code: fourcc,
modifier: DrmModifier::Invalid,
},
width, width,
height, height,
stride, stride,
@@ -346,12 +346,12 @@ impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient
} }
} }
fn fourcc_from_wlshm(shm_format: Format) -> Option<FourCC> { fn fourcc_from_wlshm(shm_format: Format) -> Option<DrmFourcc> {
match shm_format { match shm_format {
Format::Argb8888 => Some(FourCC::from(DRM_FORMAT_ARGB8888)), Format::Argb8888 => Some(DrmFourcc::Argb8888),
Format::Xrgb8888 => Some(FourCC::from(DRM_FORMAT_XRGB8888)), Format::Xrgb8888 => Some(DrmFourcc::Xrgb8888),
Format::Abgr8888 => Some(FourCC::from(DRM_FORMAT_ARGB8888)), Format::Abgr8888 => Some(DrmFourcc::Abgr8888),
Format::Xbgr8888 => Some(FourCC::from(DRM_FORMAT_XRGB8888)), Format::Xbgr8888 => Some(DrmFourcc::Xbgr8888),
_ => None, _ => None,
} }
} }

View File

@@ -8,11 +8,12 @@ use std::{
}, },
}; };
use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
use rxscreen::monitor::Monitor; use rxscreen::monitor::Monitor;
use crate::{ use crate::{
WlxCapture, WlxCapture,
frame::{DRM_FORMAT_XRGB8888, DrmFormat, FrameFormat, MemPtrFrame, MouseMeta, WlxFrame}, frame::{FrameFormat, MemPtrFrame, MouseMeta, Transform, WlxFrame},
}; };
pub struct XshmScreen { pub struct XshmScreen {
@@ -101,8 +102,11 @@ where
format: FrameFormat { format: FrameFormat {
width: image.width() as _, width: image.width() as _,
height: image.height() as _, height: image.height() as _,
fourcc: DRM_FORMAT_XRGB8888.into(), drm_format: DrmFormat {
..Default::default() code: DrmFourcc::Xrgb8888,
modifier: DrmModifier::Invalid,
},
transform: Transform::Normal,
}, },
ptr: unsafe { image.as_ptr() as _ }, ptr: unsafe { image.as_ptr() as _ },
size, size,

View File

@@ -82,23 +82,18 @@ vulkano = { workspace = true }
vulkano-shaders = { workspace = true } vulkano-shaders = { workspace = true }
wgui = { path = "../wgui" } wgui = { path = "../wgui" }
wayvr_ipc = { workspace = true } wayvr_ipc = { workspace = true }
bytes = { version = "1.11.0" }
################################ ################################
#WayVR-only deps # Wayland Server deps
################################ ################################
khronos-egl = { version = "6.0.0", features = ["static"], optional = true }
smithay = { version = "0.7.0", default-features = false, features = [ smithay = { version = "0.7.0", default-features = false, features = [
"renderer_gl", "backend_vulkan",
"backend_egl",
"backend_drm",
"xwayland", "xwayland",
"wayland_frontend", "wayland_frontend",
], optional = true } ], optional = true }
uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true } uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true }
wayland-client = { workspace = true, optional = true } wayland-client = { workspace = true, optional = true }
wayland-egl = { version = "0.32.8", optional = true }
bytes = { version = "1.11.0", optional = true }
wayvr-ipc = { path = "../wayvr-ipc", default-features = false, optional = true }
rust-embed = { workspace = true } rust-embed = { workspace = true }
signal-hook = "0.3.18" signal-hook = "0.3.18"
################################ ################################
@@ -106,6 +101,7 @@ signal-hook = "0.3.18"
[build-dependencies] [build-dependencies]
regex = { version = "1.12.2" } regex = { version = "1.12.2" }
# TODO: rename "wayvr" feature to "wayland-server"
[features] [features]
default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"] default = ["openvr", "openxr", "osc", "x11", "wayland", "wayvr"]
openvr = ["dep:ovr_overlay", "dep:json"] openvr = ["dep:ovr_overlay", "dep:json"]
@@ -116,13 +112,5 @@ wayland = ["pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
pipewire = ["wlx-capture/pipewire"] pipewire = ["wlx-capture/pipewire"]
uidev = ["dep:winit"] uidev = ["dep:winit"]
xcb = ["dep:xcb"] xcb = ["dep:xcb"]
wayvr = [ wayvr = ["dep:smithay", "dep:uuid", "dep:wayland-client"]
"dep:khronos-egl",
"dep:smithay",
"dep:uuid",
"dep:wayland-client",
"dep:wayland-egl",
"dep:bytes",
"dep:wayvr-ipc",
]
as-raw-xcb-connection = [] as-raw-xcb-connection = []

View File

@@ -10,7 +10,7 @@ use smallvec::{SmallVec, smallvec};
use wlx_common::common::LeftRight; use wlx_common::common::LeftRight;
use wlx_common::windowing::{OverlayWindowState, Positioning}; use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::backend::task::OverlayTask; use crate::backend::task::{InputTask, OverlayTask};
use crate::overlays::anchor::{ANCHOR_NAME, GRAB_HELP_NAME}; use crate::overlays::anchor::{ANCHOR_NAME, GRAB_HELP_NAME};
use crate::overlays::watch::WATCH_NAME; use crate::overlays::watch::WATCH_NAME;
use crate::state::{AppSession, AppState}; use crate::state::{AppSession, AppState};
@@ -65,6 +65,18 @@ impl InputState {
} }
} }
pub fn handle_task(&mut self, task: InputTask) {
match task {
InputTask::Haptics { device, haptics } => {
if let Some(pointer) = self.pointers.get_mut(device) {
pointer.pending_haptics = Some(haptics);
} else {
log::warn!("Can't trigger haptics on non-existing device: {device}");
}
}
}
}
pub const fn pre_update(&mut self) { pub const fn pre_update(&mut self) {
self.pointers[0].before = self.pointers[0].now; self.pointers[0].before = self.pointers[0].now;
self.pointers[1].before = self.pointers[1].now; self.pointers[1].before = self.pointers[1].now;
@@ -218,6 +230,7 @@ pub struct Pointer {
pub now: PointerState, pub now: PointerState,
pub before: PointerState, pub before: PointerState,
pub last_click: Instant, pub last_click: Instant,
pub pending_haptics: Option<Haptics>,
pub(super) interaction: InteractionState, pub(super) interaction: InteractionState,
} }
@@ -231,6 +244,7 @@ impl Pointer {
now: PointerState::default(), now: PointerState::default(),
before: PointerState::default(), before: PointerState::default(),
last_click: Instant::now(), last_click: Instant::now(),
pending_haptics: None,
interaction: InteractionState::default(), interaction: InteractionState::default(),
} }
} }
@@ -340,6 +354,8 @@ where
{ {
// already grabbing, ignore everything else // already grabbing, ignore everything else
let mut pointer = &mut app.input_state.pointers[idx]; let mut pointer = &mut app.input_state.pointers[idx];
let pending_haptics = pointer.pending_haptics.take();
if let Some(grab_data) = pointer.interaction.grabbed { if let Some(grab_data) = pointer.interaction.grabbed {
if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) { if let Some(grabbed) = overlays.mut_by_id(grab_data.grabbed_id) {
handle_grabbed(idx, grabbed, app); handle_grabbed(idx, grabbed, app);
@@ -347,13 +363,13 @@ where
log::warn!("Grabbed overlay {:?} does not exist", grab_data.grabbed_id); log::warn!("Grabbed overlay {:?} does not exist", grab_data.grabbed_id);
pointer.interaction.grabbed = None; pointer.interaction.grabbed = None;
} }
return (0.1, None); return (0.1, pending_haptics);
} }
let hovered_id = pointer.interaction.hovered_id.take(); let hovered_id = pointer.interaction.hovered_id.take();
let (Some(mut hit), haptics) = get_nearest_hit(idx, overlays, app) else { let (Some(mut hit), haptics) = get_nearest_hit(idx, overlays, app) else {
handle_no_hit(idx, hovered_id, overlays, app); handle_no_hit(idx, hovered_id, overlays, app);
return (0.0, None); // no hit return (0.0, pending_haptics); // no hit
}; };
// focus change // focus change
@@ -378,7 +394,7 @@ where
let Some(hovered) = overlays.mut_by_id(hit.overlay) else { let Some(hovered) = overlays.mut_by_id(hit.overlay) else {
log::warn!("Hit overlay {:?} does not exist", hit.overlay); log::warn!("Hit overlay {:?} does not exist", hit.overlay);
return (0.0, None); // no hit return (0.0, pending_haptics); // no hit
}; };
pointer = &mut app.input_state.pointers[idx]; pointer = &mut app.input_state.pointers[idx];
pointer.interaction.hovered_id = Some(hit.overlay); pointer.interaction.hovered_id = Some(hit.overlay);
@@ -447,7 +463,7 @@ where
} }
} }
(hit.dist, haptics) (hit.dist, haptics.or(pending_haptics))
} }
fn handle_no_hit<O>( fn handle_no_hit<O>(

View File

@@ -42,9 +42,6 @@ use crate::{
}, },
}; };
#[cfg(feature = "wayvr")]
use crate::{backend::wayvr::WayVRAction, overlays::wayvr::wayvr_action};
pub mod helpers; pub mod helpers;
pub mod input; pub mod input;
pub mod lines; pub mod lines;
@@ -157,37 +154,31 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
} }
FRAME_COUNTER.fetch_add(1, Ordering::Relaxed); FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);
{
// extremely cursed // extremely cursed
const VREVENT_QUIT: u32 = EVREventType::VREvent_Quit as u32; const EV_QUIT: u32 = EVREventType::VREvent_Quit as u32;
const VREVENT_TRACKED_ACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceActivated as u32; const EV_DEV_ACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceActivated as u32;
const VREVENT_TRACKED_DEACTIVATED: u32 = const EV_DEV_DEACTIVATED: u32 = EVREventType::VREvent_TrackedDeviceDeactivated as u32;
EVREventType::VREvent_TrackedDeviceDeactivated as u32; const EV_DEV_UPDATED: u32 = EVREventType::VREvent_TrackedDeviceUpdated as u32;
const VREVENT_TRACKED_UPDATED: u32 = EVREventType::VREvent_TrackedDeviceUpdated as u32; const EV_SEAT_ZERO: u32 = EVREventType::VREvent_SeatedZeroPoseReset as u32;
const VREVENT_SEATED_ZERO: u32 = EVREventType::VREvent_SeatedZeroPoseReset as u32; const EV_STAND_ZERO: u32 = EVREventType::VREvent_StandingZeroPoseReset as u32;
const VREVENT_STANDING_ZERO: u32 = EVREventType::VREvent_StandingZeroPoseReset as u32; const EV_CHAP_CHANGED: u32 = EVREventType::VREvent_ChaperoneUniverseHasChanged as u32;
const VREVENT_CHAPERONE_CHANGED: u32 = const EV_SCENE_CHANGED: u32 = EVREventType::VREvent_SceneApplicationChanged as u32;
EVREventType::VREvent_ChaperoneUniverseHasChanged as u32; const EV_IPD_CHANGED: u32 = EVREventType::VREvent_IpdChanged as u32;
const VREVENT_SCENE_APP_CHANGED: u32 = EVREventType::VREvent_SceneApplicationChanged as u32;
const VREVENT_IPD_CHANGED: u32 = EVREventType::VREvent_IpdChanged as u32;
while let Some(event) = system_mgr.poll_next_event() { while let Some(event) = system_mgr.poll_next_event() {
match event.event_type { match event.event_type {
VREVENT_QUIT => { EV_QUIT => {
log::warn!("Received quit event, shutting down."); log::warn!("Received quit event, shutting down.");
break 'main_loop; break 'main_loop;
} }
VREVENT_TRACKED_ACTIVATED EV_DEV_ACTIVATED | EV_DEV_DEACTIVATED | EV_DEV_UPDATED => {
| VREVENT_TRACKED_DEACTIVATED
| VREVENT_TRACKED_UPDATED => {
next_device_update = Instant::now(); next_device_update = Instant::now();
} }
VREVENT_SEATED_ZERO EV_SEAT_ZERO | EV_STAND_ZERO | EV_CHAP_CHANGED | EV_SCENE_CHANGED => {
| VREVENT_STANDING_ZERO
| VREVENT_CHAPERONE_CHANGED
| VREVENT_SCENE_APP_CHANGED => {
playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr); playspace.playspace_changed(&mut compositor_mgr, &mut chaperone_mgr);
} }
VREVENT_IPD_CHANGED => { EV_IPD_CHANGED => {
if let Ok(ipd) = system_mgr.get_tracked_device_property::<f32>( if let Ok(ipd) = system_mgr.get_tracked_device_property::<f32>(
TrackedDeviceIndex::HMD, TrackedDeviceIndex::HMD,
ETrackedDeviceProperty::Prop_UserIpdMeters_Float, ETrackedDeviceProperty::Prop_UserIpdMeters_Float,
@@ -195,7 +186,11 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
let ipd = (ipd * 1000.0).round(); let ipd = (ipd * 1000.0).round();
if (ipd - app.input_state.ipd).abs() > 0.05 { if (ipd - app.input_state.ipd).abs() > 0.05 {
log::info!("IPD: {:.1} mm -> {:.1} mm", app.input_state.ipd, ipd); log::info!("IPD: {:.1} mm -> {:.1} mm", app.input_state.ipd, ipd);
Toast::new(ToastTopic::IpdChange, "IPD".into(), format!("{ipd:.1} mm")) Toast::new(
ToastTopic::IpdChange,
"IPD".into(),
format!("{ipd:.1} mm"),
)
.submit(&mut app); .submit(&mut app);
} }
app.input_state.ipd = ipd; app.input_state.ipd = ipd;
@@ -204,6 +199,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
_ => {} _ => {}
} }
} }
}
if next_device_update <= Instant::now() { if next_device_update <= Instant::now() {
let changed = input_source.update_devices(&mut system_mgr, &mut app); let changed = input_source.update_devices(&mut system_mgr, &mut app);
@@ -220,6 +216,9 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
while let Some(task) = due_tasks.pop_front() { while let Some(task) = due_tasks.pop_front() {
match task { match task {
TaskType::Input(task) => {
app.input_state.handle_task(task);
}
TaskType::Overlay(task) => { TaskType::Overlay(task) => {
overlays.handle_task(&mut app, task)?; overlays.handle_task(&mut app, task)?;
} }
@@ -232,9 +231,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
} }
}, },
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
TaskType::WayVR(action) => { TaskType::WayVR(_action) => { /* TODO */ }
wayvr_action(&mut app, &mut overlays, &action);
}
} }
} }
@@ -264,9 +261,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
.pointers .pointers
.iter() .iter()
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard) .any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
{ { /* TODO */ }
wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard);
}
overlays overlays
.values_mut() .values_mut()
@@ -303,11 +298,10 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
let _ = sender.send_params(&overlays, &app.input_state.devices); let _ = sender.send_params(&overlays, &app.input_state.devices);
} }
#[cfg(feature = "wayvr")]
if let Err(e) = if let Err(e) =
crate::overlays::wayvr::tick_events::<OpenVrOverlayData>(&mut app, &mut overlays) crate::ipc::events::tick_events::<OpenVrOverlayData>(&mut app, &mut overlays)
{ {
log::error!("WayVR tick_events failed: {e:?}"); log::error!("WayVR IPC tick_events failed: {e:?}");
} }
log::trace!("Rendering frame"); log::trace!("Rendering frame");
@@ -335,11 +329,6 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
.values_mut() .values_mut()
.for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &app.gfx)); .for_each(|o| o.after_render(universe.clone(), &mut overlay_mgr, &app.gfx));
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &app.wayvr {
wayvr.borrow_mut().data.tick_finish()?;
}
// chaperone // chaperone
} // main_loop } // main_loop

View File

@@ -35,9 +35,6 @@ use crate::{
}, },
}; };
#[cfg(feature = "wayvr")]
use crate::{backend::wayvr::WayVRAction, overlays::wayvr::wayvr_action};
mod blocker; mod blocker;
mod helpers; mod helpers;
mod input; mod input;
@@ -149,8 +146,8 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
}; };
let pointer_lines = [ let pointer_lines = [
lines.allocate(&xr_state, &mut app)?, lines.allocate(&xr_state, &app)?,
lines.allocate(&xr_state, &mut app)?, lines.allocate(&xr_state, &app)?,
]; ];
let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic
@@ -303,9 +300,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
.pointers .pointers
.iter() .iter()
.any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard) .any(|p| p.now.toggle_dashboard && !p.before.toggle_dashboard)
{ { /* TODO */ }
wayvr_action(&mut app, &mut overlays, &WayVRAction::ToggleDashboard);
}
watch_fade(&mut app, overlays.mut_by_id(watch_id).unwrap()); // want panic watch_fade(&mut app, overlays.mut_by_id(watch_id).unwrap()); // want panic
if let Some(ref mut space_mover) = playspace { if let Some(ref mut space_mover) = playspace {
@@ -373,9 +368,9 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
if let Err(e) = if let Err(e) =
crate::overlays::wayvr::tick_events::<OpenXrOverlayData>(&mut app, &mut overlays) crate::ipc::events::tick_events::<OpenXrOverlayData>(&mut app, &mut overlays)
{ {
log::error!("WayVR tick_events failed: {e:?}"); log::error!("WayVR IPC tick_events failed: {e:?}");
} }
// Begin rendering // Begin rendering
@@ -458,11 +453,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
} }
// End layer composition // End layer composition
#[cfg(feature = "wayvr")]
if let Some(wayvr) = &app.wayvr {
wayvr.borrow_mut().data.tick_finish()?;
}
// Begin layer submit // Begin layer submit
layers.sort_by(|a, b| b.0.total_cmp(&a.0)); layers.sort_by(|a, b| b.0.total_cmp(&a.0));
@@ -489,6 +479,9 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
app.tasks.retrieve_due(&mut due_tasks); app.tasks.retrieve_due(&mut due_tasks);
while let Some(task) = due_tasks.pop_front() { while let Some(task) = due_tasks.pop_front() {
match task { match task {
TaskType::Input(task) => {
app.input_state.handle_task(task);
}
TaskType::Overlay(task) => { TaskType::Overlay(task) => {
overlays.handle_task(&mut app, task)?; overlays.handle_task(&mut app, task)?;
} }
@@ -499,10 +492,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
} }
#[cfg(feature = "openvr")] #[cfg(feature = "openvr")]
TaskType::OpenVR(_) => {} TaskType::OpenVR(_) => {}
#[cfg(feature = "wayvr")] TaskType::WayVR(_action) => { /* TODO */ }
TaskType::WayVR(action) => {
wayvr_action(&mut app, &mut overlays, &action);
}
} }
} }

View File

@@ -8,6 +8,7 @@ use std::{
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
backend::input,
state::AppState, state::AppState,
windowing::{OverlaySelector, window::OverlayWindowConfig}, windowing::{OverlaySelector, window::OverlayWindowConfig},
}; };
@@ -43,6 +44,13 @@ impl Ord for AppTask {
} }
} }
pub enum InputTask {
Haptics {
device: usize,
haptics: input::Haptics,
},
}
#[cfg(feature = "openvr")] #[cfg(feature = "openvr")]
pub enum OpenVrTask { pub enum OpenVrTask {
ColorGain(ColorChannel, f32), ColorGain(ColorChannel, f32),
@@ -87,6 +95,7 @@ pub enum OverlayTask {
} }
pub enum TaskType { pub enum TaskType {
Input(InputTask),
Overlay(OverlayTask), Overlay(OverlayTask),
Playspace(PlayspaceTask), Playspace(PlayspaceTask),
#[cfg(feature = "openvr")] #[cfg(feature = "openvr")]

View File

@@ -14,12 +14,11 @@ use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask};
use super::{ use super::{
ProcessWayVREnv, ProcessWayVREnv,
comp::{self, ClientState}, comp::{self, ClientState},
display, process, process,
}; };
pub struct WayVRClient { pub struct WayVRClient {
pub client: wayland_server::Client, pub client: wayland_server::Client,
pub display_handle: display::DisplayHandle,
pub pid: u32, pub pid: u32,
} }
@@ -103,10 +102,13 @@ impl WayVRCompositor {
}); });
} }
pub fn cleanup_handles(&mut self) {
self.state.cleanup();
}
fn accept_connection( fn accept_connection(
&mut self, &mut self,
stream: UnixStream, stream: UnixStream,
displays: &mut display::DisplayVec,
processes: &mut process::ProcessVec, processes: &mut process::ProcessVec,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let client = self let client = self
@@ -126,19 +128,15 @@ impl WayVRCompositor {
{ {
// Find process with matching auth key // Find process with matching auth key
if process.auth_key.as_str() == auth_key { if process.auth_key.as_str() == auth_key {
// Check if display handle is valid
if displays.get(&process.display_handle).is_some() {
// Add client // Add client
self.add_client(WayVRClient { self.add_client(WayVRClient {
client, client,
display_handle: process.display_handle,
pid: creds.pid as u32, pid: creds.pid as u32,
}); });
return Ok(()); return Ok(());
} }
} }
} }
}
// This is a new process which we didn't met before. // This is a new process which we didn't met before.
// Treat external processes exclusively (spawned by the user or external program) // Treat external processes exclusively (spawned by the user or external program)
@@ -158,13 +156,9 @@ impl WayVRCompositor {
Ok(()) Ok(())
} }
fn accept_connections( fn accept_connections(&mut self, processes: &mut process::ProcessVec) -> anyhow::Result<()> {
&mut self,
displays: &mut display::DisplayVec,
processes: &mut process::ProcessVec,
) -> anyhow::Result<()> {
if let Some(stream) = self.listener.accept()? if let Some(stream) = self.listener.accept()?
&& let Err(e) = self.accept_connection(stream, displays, processes) && let Err(e) = self.accept_connection(stream, processes)
{ {
log::error!("Failed to accept connection: {e}"); log::error!("Failed to accept connection: {e}");
} }
@@ -172,12 +166,8 @@ impl WayVRCompositor {
Ok(()) Ok(())
} }
pub fn tick_wayland( pub fn tick_wayland(&mut self, processes: &mut process::ProcessVec) -> anyhow::Result<()> {
&mut self, if let Err(e) = self.accept_connections(processes) {
displays: &mut display::DisplayVec,
processes: &mut process::ProcessVec,
) -> anyhow::Result<()> {
if let Err(e) = self.accept_connections(displays, processes) {
log::error!("accept_connections failed: {e}"); log::error!("accept_connections failed: {e}");
} }

View File

@@ -1,18 +1,17 @@
use smithay::backend::allocator::dmabuf::Dmabuf; use smithay::backend::allocator::dmabuf::Dmabuf;
use smithay::backend::renderer::ImportDma; use smithay::backend::renderer::{BufferType, buffer_type};
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::backend::renderer::utils::on_commit_buffer_handler;
use smithay::input::{Seat, SeatHandler, SeatState}; use smithay::input::{Seat, SeatHandler, SeatState};
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::reexports::wayland_server; use smithay::reexports::wayland_server;
use smithay::reexports::wayland_server::Resource; use smithay::reexports::wayland_server::Resource;
use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_seat, wl_surface}; use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_output, wl_seat, wl_surface};
use smithay::wayland::buffer::BufferHandler; use smithay::wayland::buffer::BufferHandler;
use smithay::wayland::dmabuf::{ use smithay::wayland::dmabuf::{
DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,
}; };
use smithay::wayland::output::OutputHandler; use smithay::wayland::output::OutputHandler;
use smithay::wayland::shm::{ShmHandler, ShmState}; use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents};
use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer;
use smithay::{ use smithay::{
delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_output, delegate_seat, delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_output, delegate_seat,
delegate_shm, delegate_xdg_shell, delegate_shm, delegate_xdg_shell,
@@ -23,7 +22,7 @@ use std::sync::{Arc, Mutex};
use smithay::utils::Serial; use smithay::utils::Serial;
use smithay::wayland::compositor::{ use smithay::wayland::compositor::{
self, SurfaceAttributes, TraversalAction, with_surface_tree_downward, self, BufferAssignment, SurfaceAttributes, TraversalAction, with_surface_tree_downward,
}; };
use smithay::wayland::selection::SelectionHandler; use smithay::wayland::selection::SelectionHandler;
@@ -37,11 +36,14 @@ use wayland_server::Client;
use wayland_server::backend::{ClientData, ClientId, DisconnectReason}; use wayland_server::backend::{ClientData, ClientId, DisconnectReason};
use wayland_server::protocol::wl_surface::WlSurface; use wayland_server::protocol::wl_surface::WlSurface;
use crate::backend::wayvr::SurfaceBufWithImage;
use crate::backend::wayvr::image_importer::ImageImporter;
use crate::ipc::event_queue::SyncEventQueue;
use super::WayVRTask; use super::WayVRTask;
use super::event_queue::SyncEventQueue;
pub struct Application { pub struct Application {
pub gles_renderer: GlesRenderer, pub image_importer: ImageImporter,
pub dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>), pub dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
pub compositor: compositor::CompositorState, pub compositor: compositor::CompositorState,
pub xdg_shell: XdgShellState, pub xdg_shell: XdgShellState,
@@ -56,6 +58,10 @@ impl Application {
pub fn check_redraw(&mut self, surface: &WlSurface) -> bool { pub fn check_redraw(&mut self, surface: &WlSurface) -> bool {
self.redraw_requests.remove(&surface.id()) self.redraw_requests.remove(&surface.id())
} }
pub fn cleanup(&mut self) {
self.image_importer.cleanup();
}
} }
impl compositor::CompositorHandler for Application { impl compositor::CompositorHandler for Application {
@@ -71,7 +77,74 @@ impl compositor::CompositorHandler for Application {
} }
fn commit(&mut self, surface: &WlSurface) { fn commit(&mut self, surface: &WlSurface) {
on_commit_buffer_handler::<Self>(surface); smithay::wayland::compositor::with_states(surface, |states| {
let mut guard = states.cached_state.get::<SurfaceAttributes>();
let attrs = guard.current();
match attrs.buffer.take() {
Some(BufferAssignment::NewBuffer(buffer)) => {
let current = SurfaceBufWithImage::get_from_surface(states);
if current.is_none_or(|c| c.buffer != buffer) {
match buffer_type(&buffer) {
Some(BufferType::Dma) => {
let dmabuf = get_dmabuf(&buffer).unwrap(); // always Ok due to buffer_type
if let Ok(image) =
self.image_importer.get_or_import_dmabuf(dmabuf.clone())
{
let sbwi = SurfaceBufWithImage {
image,
buffer,
transform: wl_transform_to_frame_transform(
attrs.buffer_transform,
),
scale: attrs.buffer_scale,
};
sbwi.apply_to_surface(states);
}
}
Some(BufferType::Shm) => {
with_buffer_contents(&buffer, |data, size, buf| {
if let Ok(image) =
self.image_importer.import_shm(data, size, buf)
{
let sbwi = SurfaceBufWithImage {
image,
buffer: buffer.clone(),
transform: wl_transform_to_frame_transform(
attrs.buffer_transform,
),
scale: attrs.buffer_scale,
};
sbwi.apply_to_surface(states);
}
});
}
Some(BufferType::SinglePixel) => {
let spb = get_single_pixel_buffer(&buffer).unwrap(); // always Ok
if let Ok(image) = self.image_importer.import_spb(spb) {
let sbwi = SurfaceBufWithImage {
image,
buffer,
transform: wl_transform_to_frame_transform(
// does this even matter
attrs.buffer_transform,
),
scale: attrs.buffer_scale,
};
sbwi.apply_to_surface(states);
}
}
Some(other) => log::warn!("Unsupported wl_buffer format: {other:?}"),
None => { /* don't draw anything */ }
}
}
}
Some(BufferAssignment::Removed) => {}
None => {}
}
});
self.redraw_requests.insert(surface.id()); self.redraw_requests.insert(surface.id());
} }
} }
@@ -197,7 +270,7 @@ impl DmabufHandler for Application {
dmabuf: Dmabuf, dmabuf: Dmabuf,
notifier: ImportNotifier, notifier: ImportNotifier,
) { ) {
if self.gles_renderer.import_dmabuf(&dmabuf, None).is_ok() { if self.image_importer.get_or_import_dmabuf(dmabuf).is_ok() {
let _ = notifier.successful::<Self>(); let _ = notifier.successful::<Self>();
} else { } else {
notifier.failed(); notifier.failed();
@@ -234,3 +307,19 @@ pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
|_, _, &()| true, |_, _, &()| true,
); );
} }
fn wl_transform_to_frame_transform(
transform: wl_output::Transform,
) -> wlx_capture::frame::Transform {
match transform {
wl_output::Transform::Normal => wlx_capture::frame::Transform::Normal,
wl_output::Transform::_90 => wlx_capture::frame::Transform::Rotated90,
wl_output::Transform::_180 => wlx_capture::frame::Transform::Rotated180,
wl_output::Transform::_270 => wlx_capture::frame::Transform::Rotated270,
wl_output::Transform::Flipped => wlx_capture::frame::Transform::Flipped,
wl_output::Transform::Flipped90 => wlx_capture::frame::Transform::Flipped90,
wl_output::Transform::Flipped180 => wlx_capture::frame::Transform::Flipped180,
wl_output::Transform::Flipped270 => wlx_capture::frame::Transform::Flipped270,
_ => wlx_capture::frame::Transform::Undefined,
}
}

View File

@@ -1,605 +0,0 @@
use std::{cell::RefCell, rc::Rc, sync::Arc};
use smithay::{
backend::renderer::{
Bind, Color32F, Frame, Renderer,
element::{
Kind,
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
},
gles::{GlesRenderer, GlesTexture, ffi},
utils::draw_render_elements,
},
input,
utils::{Logical, Point, Rectangle, Size, Transform},
wayland::shell::xdg::ToplevelSurface,
};
use wayvr_ipc::packet_server;
use crate::{
backend::wayvr::time::get_millis, gen_id, subsystem::hid::WheelDelta, windowing::OverlayID,
};
use super::{
BlitMethod, WayVRSignal, client::WayVRCompositor, comp::send_frames_surface_tree, egl_data,
event_queue::SyncEventQueue, process, smithay_wrapper, time, window,
};
fn generate_auth_key() -> String {
let uuid = uuid::Uuid::new_v4();
uuid.to_string()
}
#[derive(Debug)]
pub struct DisplayWindow {
pub window_handle: window::WindowHandle,
pub toplevel: ToplevelSurface,
pub process_handle: process::ProcessHandle,
}
pub struct SpawnProcessResult {
pub auth_key: String,
pub child: std::process::Child,
}
#[derive(Debug)]
pub enum DisplayTask {
ProcessCleanup(process::ProcessHandle),
}
const MAX_DISPLAY_SIZE: u16 = 8192;
#[derive(Debug)]
pub struct Display {
// Display info stuff
pub width: u16,
pub height: u16,
pub name: String,
pub visible: bool,
pub layout: packet_server::WvrDisplayWindowLayout,
pub overlay_id: Option<OverlayID>,
pub wants_redraw: bool,
pub rendered_frame_count: u32,
pub primary: bool,
pub wm: Rc<RefCell<window::WindowManager>>,
pub displayed_windows: Vec<DisplayWindow>,
wayland_env: super::WaylandEnv,
last_pressed_time_ms: u64,
pub no_windows_since: Option<u64>,
// Render data stuff
gles_texture: GlesTexture, // TODO: drop texture
egl_image: khronos_egl::Image,
egl_data: Rc<egl_data::EGLData>,
pub render_data: egl_data::RenderData,
pub tasks: SyncEventQueue<DisplayTask>,
}
impl Drop for Display {
fn drop(&mut self) {
let _ = self
.egl_data
.egl
.destroy_image(self.egl_data.display, self.egl_image);
}
}
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,
pub width: u16,
pub height: u16,
pub name: &'a str,
pub primary: bool,
}
impl Display {
pub fn new(params: DisplayInitParams) -> anyhow::Result<Self> {
if params.width > MAX_DISPLAY_SIZE {
anyhow::bail!(
"display width ({}) is larger than {}",
params.width,
MAX_DISPLAY_SIZE
);
}
if params.height > MAX_DISPLAY_SIZE {
anyhow::bail!(
"display height ({}) is larger than {}",
params.height,
MAX_DISPLAY_SIZE
);
}
let tex_format = ffi::RGBA;
let internal_format = ffi::RGBA8;
let tex_id = params.renderer.with_context(|gl| {
smithay_wrapper::create_framebuffer_texture(
gl,
u32::from(params.width),
u32::from(params.height),
tex_format,
internal_format,
)
})?;
let egl_image = params.egl_data.create_egl_image(tex_id)?;
let render_data = match params.config.blit_method {
BlitMethod::Dmabuf => match params.egl_data.create_dmabuf_data(&egl_image) {
Ok(dmabuf_data) => egl_data::RenderData::Dmabuf(dmabuf_data),
Err(e) => {
log::error!(
"create_dmabuf_data failed: {e:?}. Using software blitting (This will be slow!)"
);
egl_data::RenderData::Software(None)
}
},
BlitMethod::Software => egl_data::RenderData::Software(None),
};
let opaque = false;
let size = (i32::from(params.width), i32::from(params.height)).into();
let gles_texture = unsafe {
GlesTexture::from_raw(params.renderer, Some(tex_format), opaque, tex_id, size)
};
Ok(Self {
egl_data: params.egl_data,
width: params.width,
height: params.height,
name: String::from(params.name),
primary: params.primary,
wayland_env: params.wayland_env,
wm: params.wm,
displayed_windows: Vec::new(),
render_data,
egl_image,
gles_texture,
last_pressed_time_ms: 0,
no_windows_since: None,
overlay_id: None,
tasks: SyncEventQueue::new(),
visible: true,
wants_redraw: true,
rendered_frame_count: 0,
layout: packet_server::WvrDisplayWindowLayout::Tiling,
})
}
pub fn as_packet(&self, handle: DisplayHandle) -> packet_server::WvrDisplay {
packet_server::WvrDisplay {
width: self.width,
height: self.height,
name: self.name.clone(),
visible: self.visible,
handle: handle.as_packet(),
}
}
pub fn add_window(
&mut self,
window_handle: window::WindowHandle,
process_handle: process::ProcessHandle,
toplevel: &ToplevelSurface,
) {
log::debug!("Attaching toplevel surface into display");
self.displayed_windows.push(DisplayWindow {
window_handle,
process_handle,
toplevel: toplevel.clone(),
});
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();
match &self.layout {
packet_server::WvrDisplayWindowLayout::Tiling => {
let mut i = 0;
for win in &mut self.displayed_windows {
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
if !window.visible {
continue;
}
let d_cur = i as f32 / window_count as f32;
let d_next = (i + 1) as f32 / window_count as f32;
let left = (d_cur * f32::from(self.width)) as i32;
let right = (d_next * f32::from(self.width)) as i32;
window.set_pos(left, 0);
window.set_size((right - left) as u32, u32::from(self.height));
i += 1;
}
}
}
packet_server::WvrDisplayWindowLayout::Stacking(opts) => {
let do_margins = |margins: &packet_server::Margins, window: &mut window::Window| {
let top = i32::from(margins.top);
let bottom = i32::from(self.height) - i32::from(margins.bottom);
let left = i32::from(margins.left);
let right = i32::from(self.width) - i32::from(margins.right);
let width = right - left;
let height = bottom - top;
if width < 0 || height < 0 {
return; // wrong parameters, do nothing!
}
window.set_pos(left, top);
window.set_size(width as u32, height as u32);
};
let mut i = 0;
for win in &mut self.displayed_windows {
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&win.window_handle) {
if !window.visible {
continue;
}
do_margins(
if i == 0 {
&opts.margins_first
} else {
&opts.margins_rest
},
window,
);
i += 1;
}
}
}
}
}
pub fn tick(
&mut self,
config: &super::Config,
handle: &DisplayHandle,
signals: &mut SyncEventQueue<WayVRSignal>,
) {
if self.visible {
if !self.displayed_windows.is_empty() {
self.no_windows_since = None;
} else if let Some(auto_hide_delay) = config.auto_hide_delay
&& let Some(s) = self.no_windows_since
&& s + u64::from(auto_hide_delay) < get_millis()
{
// Auto-hide after specific time
signals.send(WayVRSignal::DisplayVisibility(*handle, false));
}
}
while let Some(task) = self.tasks.read() {
match task {
DisplayTask::ProcessCleanup(process_handle) => {
let count = self.displayed_windows.len();
self.displayed_windows
.retain(|win| win.process_handle != process_handle);
log::info!(
"Cleanup finished for display \"{}\". Current window count: {}",
self.name,
self.displayed_windows.len()
);
self.no_windows_since = Some(get_millis());
if count != self.displayed_windows.len() {
signals.send(WayVRSignal::BroadcastStateChanged(
packet_server::WvrStateChanged::WindowRemoved,
));
}
self.reposition_windows();
}
}
}
}
#[allow(clippy::significant_drop_tightening)]
pub fn tick_render(&mut self, renderer: &mut GlesRenderer, time_ms: u64) -> anyhow::Result<()> {
let mut gles_texture = self.gles_texture.clone();
let mut target = renderer.bind(&mut gles_texture)?;
let size = Size::from((i32::from(self.width), i32::from(self.height)));
let damage: Rectangle<i32, smithay::utils::Physical> = Rectangle::from_size(size);
let elements: Vec<WaylandSurfaceRenderElement<GlesRenderer>> = self
.displayed_windows
.iter()
.flat_map(|display_window| {
let wm = self.wm.borrow_mut();
if let Some(window) = wm.windows.get(&display_window.window_handle) {
if !window.visible {
return vec![];
}
render_elements_from_surface_tree(
renderer,
display_window.toplevel.wl_surface(),
(window.pos_x, window.pos_y),
1.0,
1.0,
Kind::Unspecified,
)
} else {
// Failed to fetch window
vec![]
}
})
.collect();
let mut frame = renderer.render(&mut target, size, Transform::Normal)?;
let clear_color = if self.displayed_windows.is_empty() {
Color32F::new(0.5, 0.5, 0.5, 0.5)
} else {
Color32F::new(0.0, 0.0, 0.0, 0.0)
};
frame.clear(clear_color, &[damage])?;
draw_render_elements(&mut frame, 1.0, &elements, &[damage])?;
let _sync_point = frame.finish()?;
for window in &self.displayed_windows {
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,
i32::from(self.width),
i32::from(self.height),
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,
}));
}
self.rendered_frame_count += 1;
Ok(())
}
fn get_hovered_window(&self, cursor_x: u32, cursor_y: u32) -> Option<window::WindowHandle> {
let wm = self.wm.borrow();
for cell in self.displayed_windows.iter().rev() {
if let Some(window) = wm.windows.get(&cell.window_handle) {
if !window.visible {
continue;
}
if (cursor_x as i32) >= window.pos_x
&& (cursor_x as i32) < window.pos_x + window.size_x as i32
&& (cursor_y as i32) >= window.pos_y
&& (cursor_y as i32) < window.pos_y + window.size_y as i32
{
return Some(cell.window_handle);
}
}
}
None
}
pub const fn trigger_rerender(&mut self) {
self.wants_redraw = true;
}
pub fn set_visible(&mut self, visible: bool) {
log::info!("Display \"{}\" visible: {}", self.name.as_str(), visible);
if self.visible == visible {
return;
}
self.visible = visible;
if visible {
self.no_windows_since = None;
self.trigger_rerender();
}
}
pub fn set_layout(&mut self, layout: packet_server::WvrDisplayWindowLayout) {
log::info!("Display \"{}\" layout: {:?}", self.name.as_str(), layout);
if self.layout == layout {
return;
}
self.layout = layout;
self.trigger_rerender();
self.reposition_windows();
}
pub fn send_mouse_move(
&self,
config: &super::Config,
manager: &mut WayVRCompositor,
x: u32,
y: u32,
) {
let current_ms = time::get_millis();
if self.last_pressed_time_ms + u64::from(config.click_freeze_time_ms) > current_ms {
return;
}
if let Some(window_handle) = self.get_hovered_window(x, y) {
let wm = self.wm.borrow();
if let Some(window) = wm.windows.get(&window_handle) {
let surf = window.toplevel.wl_surface().clone();
let point = Point::<f64, Logical>::from((
f64::from(x as i32 - window.pos_x),
f64::from(y as i32 - window.pos_y),
));
manager.seat_pointer.motion(
&mut manager.state,
Some((surf, Point::from((0.0, 0.0)))),
&input::pointer::MotionEvent {
serial: manager.serial_counter.next_serial(),
time: 0,
location: point,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
}
}
const fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
match index {
super::MouseIndex::Left => 0x110, /* BTN_LEFT */
super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */
super::MouseIndex::Right => 0x111, /* BTN_RIGHT */
}
}
pub fn send_mouse_down(&mut self, manager: &mut WayVRCompositor, index: super::MouseIndex) {
// Change keyboard focus to pressed window
let loc = manager.seat_pointer.current_location();
self.last_pressed_time_ms = time::get_millis();
if let Some(window_handle) =
self.get_hovered_window(loc.x.max(0.0) as u32, loc.y.max(0.0) as u32)
{
let wm = self.wm.borrow();
if let Some(window) = wm.windows.get(&window_handle) {
let surf = window.toplevel.wl_surface().clone();
manager.seat_keyboard.set_focus(
&mut manager.state,
Some(surf),
manager.serial_counter.next_serial(),
);
}
}
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
button: Self::get_mouse_index_number(index),
serial: manager.serial_counter.next_serial(),
time: 0,
state: smithay::backend::input::ButtonState::Pressed,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) {
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
button: Self::get_mouse_index_number(index),
serial: manager.serial_counter.next_serial(),
time: 0,
state: smithay::backend::input::ButtonState::Released,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta: WheelDelta) {
manager.seat_pointer.axis(
&mut manager.state,
input::pointer::AxisFrame {
source: None,
relative_direction: (
smithay::backend::input::AxisRelativeDirection::Identical,
smithay::backend::input::AxisRelativeDirection::Identical,
),
time: 0,
axis: (f64::from(delta.x), f64::from(-delta.y)),
v120: Some((0, (delta.y * -64.0) as i32)),
stop: (false, false),
},
);
manager.seat_pointer.frame(&mut manager.state);
}
fn configure_env(&self, cmd: &mut std::process::Command, auth_key: &str) {
cmd.env_remove("DISPLAY"); // Goodbye X11
cmd.env("WAYLAND_DISPLAY", self.wayland_env.display_num_string());
cmd.env("WAYVR_DISPLAY_AUTH", auth_key);
}
pub fn spawn_process(
&mut self,
exec_path: &str,
args: &[&str],
env: &[(&str, &str)],
working_dir: Option<&str>,
) -> anyhow::Result<SpawnProcessResult> {
log::info!("Spawning subprocess with exec path \"{exec_path}\"");
let auth_key = generate_auth_key();
let mut cmd = std::process::Command::new(exec_path);
self.configure_env(&mut cmd, auth_key.as_str());
cmd.args(args);
if let Some(working_dir) = working_dir {
cmd.current_dir(working_dir);
}
for e in env {
cmd.env(e.0, e.1);
}
match cmd.spawn() {
Ok(child) => Ok(SpawnProcessResult { auth_key, child }),
Err(e) => {
anyhow::bail!(
"Failed to launch process with path \"{exec_path}\": {e}. Make sure your exec path exists."
);
}
}
}
}
gen_id!(DisplayVec, Display, DisplayCell, DisplayHandle);
impl DisplayHandle {
pub const fn from_packet(handle: packet_server::WvrDisplayHandle) -> Self {
Self {
generation: handle.generation,
idx: handle.idx,
}
}
pub const fn as_packet(&self) -> packet_server::WvrDisplayHandle {
packet_server::WvrDisplayHandle {
idx: self.idx,
generation: self.generation,
}
}
}

View File

@@ -1,315 +0,0 @@
use std::sync::Arc;
use crate::backend::wayvr::egl_ex::{
PFNEGLGETPLATFORMDISPLAYEXTPROC, PFNEGLQUERYDMABUFFORMATSEXTPROC,
PFNEGLQUERYDMABUFMODIFIERSEXTPROC,
};
use super::egl_ex;
use anyhow::Context;
#[derive(Debug)]
pub struct EGLData {
pub egl: khronos_egl::Instance<khronos_egl::Static>,
pub display: khronos_egl::Display,
pub config: khronos_egl::Config,
pub context: khronos_egl::Context,
}
#[macro_export]
macro_rules! bind_egl_function {
($func_type:ident, $func:expr) => {
std::mem::transmute_copy::<_, $func_type>($func).unwrap()
};
}
#[derive(Debug, Clone)]
pub struct DMAbufModifierInfo {
pub modifiers: Vec<u64>,
pub fourcc: u32,
}
#[derive(Debug, Clone)]
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
}
fn load_egl_func(
egl: &khronos_egl::Instance<khronos_egl::Static>,
func_name: &str,
) -> anyhow::Result<extern "system" fn()> {
let raw_fn = egl
.get_proc_address(func_name)
.ok_or_else(|| anyhow::anyhow!("Required EGL function {func_name} not found"))?;
Ok(raw_fn)
}
fn get_disp(
egl: &khronos_egl::Instance<khronos_egl::Static>,
) -> anyhow::Result<khronos_egl::Display> {
unsafe {
if let Ok(func) = load_egl_func(egl, "eglGetPlatformDisplayEXT") {
let egl_get_platform_display_ext =
bind_egl_function!(PFNEGLGETPLATFORMDISPLAYEXTPROC, &func);
let display_ext = egl_get_platform_display_ext(
egl_ex::EGL_PLATFORM_WAYLAND_EXT, // platform
std::ptr::null_mut(), // void *native_display
std::ptr::null_mut(), // EGLint *attrib_list
);
if display_ext.is_null() {
log::warn!("eglGetPlatformDisplayEXT failed, using eglGetDisplay instead");
} else {
return Ok(khronos_egl::Display::from_ptr(display_ext));
}
}
egl
.get_display(khronos_egl::DEFAULT_DISPLAY)
.context(
"Both eglGetPlatformDisplayEXT and eglGetDisplay failed. This shouldn't happen unless you don't have any display manager running. Cannot continue, check your EGL installation."
)
}
}
impl EGLData {
pub fn new() -> anyhow::Result<Self> {
let egl = khronos_egl::Instance::new(khronos_egl::Static);
let display = get_disp(&egl)?;
let (major, minor) = egl.initialize(display)?;
log::debug!("EGL version: {major}.{minor}");
let attrib_list = [
khronos_egl::RED_SIZE,
8,
khronos_egl::GREEN_SIZE,
8,
khronos_egl::BLUE_SIZE,
8,
khronos_egl::SURFACE_TYPE,
khronos_egl::WINDOW_BIT,
khronos_egl::RENDERABLE_TYPE,
khronos_egl::OPENGL_BIT,
khronos_egl::NONE,
];
let config = egl
.choose_first_config(display, &attrib_list)?
.context("Failed to get EGL config")?;
egl.bind_api(khronos_egl::OPENGL_ES_API)?;
log::debug!("eglCreateContext");
// Require OpenGL ES 3.0
let context_attrib_list = [
khronos_egl::CONTEXT_MAJOR_VERSION,
3,
khronos_egl::CONTEXT_MINOR_VERSION,
0,
khronos_egl::NONE,
];
let context = egl.create_context(display, config, None, &context_attrib_list)?;
log::debug!("eglMakeCurrent");
egl.make_current(display, None, None, Some(context))?;
Ok(Self {
egl,
display,
config,
context,
})
}
fn query_dmabuf_mod_info(&self) -> anyhow::Result<DMAbufModifierInfo> {
let target_fourcc = 0x3432_4258; //XB24
unsafe {
let egl_query_dmabuf_formats_ext = bind_egl_function!(
PFNEGLQUERYDMABUFFORMATSEXTPROC,
&load_egl_func(&self.egl, "eglQueryDmaBufFormatsEXT")?
);
// Query format count
let mut num_formats: khronos_egl::Int = 0;
egl_query_dmabuf_formats_ext(
self.display.as_ptr(),
0,
std::ptr::null_mut(),
&raw mut num_formats,
);
// Retrieve format list
let mut formats: Vec<i32> = vec![0; num_formats as usize];
egl_query_dmabuf_formats_ext(
self.display.as_ptr(),
num_formats,
formats.as_mut_ptr(),
&raw mut num_formats,
);
/*for (idx, format) in formats.iter().enumerate() {
let bytes = format.to_le_bytes();
log::trace!(
"idx {}, format {}{}{}{} (hex {:#x})",
idx,
bytes[0] as char,
bytes[1] as char,
bytes[2] as char,
bytes[3] as char,
format
);
}*/
let egl_query_dmabuf_modifiers_ext = bind_egl_function!(
PFNEGLQUERYDMABUFMODIFIERSEXTPROC,
&load_egl_func(&self.egl, "eglQueryDmaBufModifiersEXT")?
);
let mut num_mods: khronos_egl::Int = 0;
// Query modifier count
egl_query_dmabuf_modifiers_ext(
self.display.as_ptr(),
target_fourcc,
0,
std::ptr::null_mut(),
std::ptr::null_mut(),
&raw mut num_mods,
);
if num_mods == 0 {
anyhow::bail!("eglQueryDmaBufModifiersEXT modifier count is zero");
}
let mut mods: Vec<u64> = vec![0; num_mods as usize];
egl_query_dmabuf_modifiers_ext(
self.display.as_ptr(),
target_fourcc,
num_mods,
mods.as_mut_ptr(),
std::ptr::null_mut(),
&raw mut num_mods,
);
if mods[0] == 0xFFFF_FFFF_FFFF_FFFF {
anyhow::bail!("modifier is -1")
}
log::trace!("Modifier list:");
for modifier in &mods {
log::trace!("{modifier:#x}");
}
// We should not change these modifier values. Passing all of them to the Vulkan dmabuf
// texture system causes significant graphical corruption due to invalid memory layout and
// tiling on this specific GPU model (very probably others also have the same issue).
// It is not guaranteed that this modifier will be present in other models.
// If not, the full list of modifiers will be passed. Further testing is required.
// For now, it looks like only NAVI32-based gpus have this problem.
let mod_whitelist: [u64; 2] = [
0x200_0000_2086_bf04, /* AMD RX 7800 XT, Navi32 */
0x200_0000_1866_bf04, /* AMD RX 7600 XT, Navi33 */
];
for modifier in &mod_whitelist {
if mods.contains(modifier) {
log::warn!("Using whitelisted dmabuf tiling modifier: {modifier:#x}");
mods = vec![*modifier, 0x0 /* also important (???) */];
break;
}
}
Ok(DMAbufModifierInfo {
modifiers: mods,
fourcc: target_fourcc as u32,
})
}
}
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 =
bind_egl_function!(FUNC, &load_egl_func(&self.egl, "eglExportDMABUFImageMESA")?);
let mut fds: [i32; 3] = [0; 3];
let mut strides: [i32; 3] = [0; 3];
let mut offsets: [i32; 3] = [0; 3];
let ret = egl_export_dmabuf_image_mesa(
self.display.as_ptr(),
egl_image.as_ptr(),
fds.as_mut_ptr(),
strides.as_mut_ptr(),
offsets.as_mut_ptr(),
);
if ret != khronos_egl::TRUE {
anyhow::bail!("eglExportDMABUFImageMESA failed with return code {ret}");
}
if fds[0] <= 0 {
anyhow::bail!("fd is <=0 (got {})", fds[0]);
}
// many planes in RGB data?
if fds[1] != 0 || strides[1] != 0 || offsets[1] != 0 {
anyhow::bail!("multi-planar data received, packed RGB expected");
}
if strides[0] < 0 {
anyhow::bail!("strides is < 0");
}
if offsets[0] < 0 {
anyhow::bail!("offsets is < 0");
}
let mod_info = self.query_dmabuf_mod_info()?;
Ok(RenderDMAbufData {
fd: fds[0],
stride: strides[0],
offset: offsets[0],
mod_info,
})
}
}
pub fn create_egl_image(&self, gl_tex_id: u32) -> anyhow::Result<khronos_egl::Image> {
unsafe {
Ok(self.egl.create_image(
self.display,
self.context,
khronos_egl::GL_TEXTURE_2D as std::ffi::c_uint,
khronos_egl::ClientBuffer::from_ptr(gl_tex_id as *mut std::ffi::c_void),
&[khronos_egl::ATTRIB_NONE],
)?)
}
}
}

View File

@@ -1,49 +0,0 @@
#![allow(clippy::all)]
pub const EGL_PLATFORM_WAYLAND_EXT: khronos_egl::Enum = 0x31D8;
// eglGetPlatformDisplayEXT
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_platform_base.txt
pub type PFNEGLGETPLATFORMDISPLAYEXTPROC = Option<
unsafe extern "C" fn(
platform: khronos_egl::Enum,
native_display: *mut std::ffi::c_void,
attrib_list: *mut khronos_egl::Enum,
) -> khronos_egl::EGLDisplay,
>;
// eglExportDMABUFImageMESA
// https://registry.khronos.org/EGL/extensions/MESA/EGL_MESA_image_dma_buf_export.txt
pub type PFNEGLEXPORTDMABUFIMAGEMESAPROC = Option<
unsafe extern "C" fn(
dpy: khronos_egl::EGLDisplay,
image: khronos_egl::EGLImage,
fds: *mut i32,
strides: *mut khronos_egl::Int,
offsets: *mut khronos_egl::Int,
) -> khronos_egl::Boolean,
>;
// eglQueryDmaBufModifiersEXT
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
pub type PFNEGLQUERYDMABUFMODIFIERSEXTPROC = Option<
unsafe extern "C" fn(
dpy: khronos_egl::EGLDisplay,
format: khronos_egl::Int,
max_modifiers: khronos_egl::Int,
modifiers: *mut u64,
external_only: *mut khronos_egl::Boolean,
num_modifiers: *mut khronos_egl::Int,
) -> khronos_egl::Boolean,
>;
// eglQueryDmaBufFormatsEXT
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
pub type PFNEGLQUERYDMABUFFORMATSEXTPROC = Option<
unsafe extern "C" fn(
dpy: khronos_egl::EGLDisplay,
max_formats: khronos_egl::Int,
formats: *mut khronos_egl::Int,
num_formats: *mut khronos_egl::Int,
) -> khronos_egl::Boolean,
>;

View File

@@ -0,0 +1,111 @@
use std::{collections::HashMap, os::fd::AsRawFd, sync::Arc};
use anyhow::Context;
use smithay::{
backend::allocator::{
Buffer,
dmabuf::{Dmabuf, WeakDmabuf},
},
wayland::{
shm::{BufferData, shm_format_to_fourcc},
single_pixel_buffer::SinglePixelBufferUserData,
},
};
use vulkano::{format::Format, image::view::ImageView};
use wgui::gfx::WGfx;
use wlx_capture::frame::{DmabufFrame, FrameFormat, Transform};
use crate::graphics::dmabuf::{WGfxDmabuf, fourcc_to_vk};
pub struct ImageImporter {
gfx: Arc<WGfx>,
dmabufs: HashMap<WeakDmabuf, Arc<ImageView>>,
}
impl ImageImporter {
pub fn new(gfx: Arc<WGfx>) -> Self {
Self {
gfx,
dmabufs: HashMap::new(),
}
}
pub fn import_spb(
&mut self,
spb: &SinglePixelBufferUserData,
) -> anyhow::Result<Arc<ImageView>> {
let mut cmd_buf = self.gfx.create_xfer_command_buffer(
vulkano::command_buffer::CommandBufferUsage::OneTimeSubmit,
)?;
let rgba = spb.rgba8888();
let image = cmd_buf.upload_image(1, 1, Format::R8G8B8A8_UNORM, &rgba)?;
cmd_buf.build_and_execute_now()?; //TODO: async
let image_view = ImageView::new_default(image)?;
Ok(image_view)
}
pub fn import_shm(
&mut self,
data: *const u8,
size: usize,
bd: BufferData,
) -> anyhow::Result<Arc<ImageView>> {
let mut cmd_buf = self.gfx.create_xfer_command_buffer(
vulkano::command_buffer::CommandBufferUsage::OneTimeSubmit,
)?;
let fourcc = shm_format_to_fourcc(bd.format)
.with_context(|| format!("Could not conver {:?} to fourcc", bd.format))?;
let format = fourcc_to_vk(fourcc)
.with_context(|| format!("Could not convert {fourcc} to vkFormat"))?;
let data = unsafe { std::slice::from_raw_parts(data, size) };
let image = cmd_buf.upload_image(bd.width as _, bd.height as _, format, data)?;
cmd_buf.build_and_execute_now()?; //TODO: async
let image_view = ImageView::new_default(image)?;
Ok(image_view)
}
pub fn get_or_import_dmabuf(&mut self, dmabuf: Dmabuf) -> anyhow::Result<Arc<ImageView>> {
let mut frame = DmabufFrame {
format: FrameFormat {
width: dmabuf.width(),
height: dmabuf.height(),
drm_format: dmabuf.format(),
transform: Transform::Undefined,
},
num_planes: dmabuf.num_planes(),
planes: Default::default(),
mouse: None,
};
for (i, handle) in dmabuf.handles().enumerate() {
// even if the original OwnedFd is dropped, the vkImage will hold reference on the DMA-buf
frame.planes[i].fd = Some(handle.as_raw_fd());
}
for (i, offset) in dmabuf.offsets().enumerate() {
frame.planes[i].offset = offset;
}
for (i, stride) in dmabuf.strides().enumerate() {
frame.planes[i].stride = stride as _;
}
let image = self.gfx.dmabuf_texture(frame)?;
let image_view = ImageView::new_default(image)?;
self.dmabufs.insert(dmabuf.weak(), image_view.clone());
Ok(image_view)
}
pub fn cleanup(&mut self) {
self.dmabufs.retain(|k, _| !k.is_gone());
}
}

View File

@@ -1,33 +1,22 @@
pub mod client; pub mod client;
mod comp; mod comp;
pub mod display;
pub mod egl_data;
mod egl_ex;
pub mod event_queue;
mod handle; mod handle;
mod process; mod image_importer;
pub mod server_ipc; pub mod process;
mod smithay_wrapper;
mod time; mod time;
mod window; pub mod window;
use anyhow::Context; use anyhow::Context;
use comp::Application; use comp::Application;
use display::{Display, DisplayInitParams, DisplayVec};
use event_queue::SyncEventQueue;
use process::ProcessVec; use process::ProcessVec;
use serde::Deserialize; use serde::Deserialize;
use server_ipc::WayVRServer; use slotmap::SecondaryMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use smithay::{ use smithay::{
backend::{
egl,
renderer::{ImportDma, gles::GlesRenderer},
},
input::{SeatState, keyboard::XkbConfig}, input::{SeatState, keyboard::XkbConfig},
output::{Mode, Output}, output::{Mode, Output},
reexports::wayland_server::{self, backend::ClientId}, reexports::wayland_server::{self, backend::ClientId, protocol::wl_buffer},
wayland::{ wayland::{
compositor, compositor::{self, SurfaceData},
dmabuf::{DmabufFeedbackBuilder, DmabufState}, dmabuf::{DmabufFeedbackBuilder, DmabufState},
selection::data_device::DataDeviceState, selection::data_device::DataDeviceState,
shell::xdg::{ToplevelSurface, XdgShellState}, shell::xdg::{ToplevelSurface, XdgShellState},
@@ -37,19 +26,28 @@ use smithay::{
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
mem::MaybeUninit,
rc::Rc, rc::Rc,
sync::Arc, sync::Arc,
time::{Duration, Instant},
}; };
use time::get_millis; use time::get_millis;
use wayvr_ipc::{ use vulkano::image::view::ImageView;
packet_client::{self}, use wayvr_ipc::packet_server;
packet_server, use wgui::gfx::WGfx;
}; use wlx_capture::frame::Transform;
use xkbcommon::xkb; use xkbcommon::xkb;
use crate::{ use crate::{
backend::{
task::{OverlayTask, TaskType},
wayvr::{image_importer::ImageImporter, window::Window},
},
graphics::WGfxExtras,
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
state::AppState, state::AppState,
subsystem::hid::{MODS_TO_KEYS, WheelDelta}, subsystem::hid::{MODS_TO_KEYS, WheelDelta},
windowing::{OverlayID, OverlaySelector},
}; };
const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle"; const STR_INVALID_HANDLE_DISP: &str = "Invalid display handle";
@@ -87,19 +85,6 @@ pub enum WayVRTask {
ProcessTerminationRequest(process::ProcessHandle), ProcessTerminationRequest(process::ProcessHandle),
} }
#[derive(Clone)]
pub enum WayVRSignal {
DisplayVisibility(display::DisplayHandle, bool),
DisplayWindowLayout(
display::DisplayHandle,
packet_server::WvrDisplayWindowLayout,
),
BroadcastStateChanged(packet_server::WvrStateChanged),
DropOverlay(crate::windowing::OverlayID),
Haptics(super::input::Haptics),
CustomTask(crate::backend::task::ModifyPanelTask),
}
pub enum BlitMethod { pub enum BlitMethod {
Dmabuf, Dmabuf,
Software, Software,
@@ -125,22 +110,21 @@ pub struct Config {
pub struct WayVRState { pub struct WayVRState {
time_start: u64, time_start: u64,
pub displays: display::DisplayVec,
pub manager: client::WayVRCompositor, pub manager: client::WayVRCompositor,
wm: Rc<RefCell<window::WindowManager>>, pub wm: Rc<RefCell<window::WindowManager>>,
egl_data: Rc<egl_data::EGLData>,
pub processes: process::ProcessVec, pub processes: process::ProcessVec,
pub config: Config, pub config: Config,
dashboard_display: Option<display::DisplayHandle>,
pub tasks: SyncEventQueue<WayVRTask>, pub tasks: SyncEventQueue<WayVRTask>,
pub signals: SyncEventQueue<WayVRSignal>,
ticks: u64, ticks: u64,
cur_modifiers: u8, cur_modifiers: u8,
signals: SyncEventQueue<WayVRSignal>,
mouse_freeze: Instant,
window_to_overlay: HashMap<window::WindowHandle, OverlayID>,
overlay_to_window: SecondaryMap<OverlayID, window::WindowHandle>,
} }
pub struct WayVR { pub struct WayVR {
pub state: WayVRState, pub state: WayVRState,
pub ipc_server: WayVRServer,
} }
pub enum MouseIndex { pub enum MouseIndex {
@@ -151,15 +135,16 @@ pub enum MouseIndex {
pub enum TickTask { pub enum TickTask {
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
NewDisplay(
packet_client::WvrDisplayCreateParams,
Option<display::DisplayHandle>, /* existing handle? */
),
} }
impl WayVR { impl WayVR {
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn new(config: Config) -> anyhow::Result<Self> { pub fn new(
gfx: Arc<WGfx>,
gfx_extras: &WGfxExtras,
config: Config,
signals: SyncEventQueue<WayVRSignal>,
) -> anyhow::Result<Self> {
log::info!("Initializing WayVR"); log::info!("Initializing WayVR");
let display: wayland_server::Display<Application> = wayland_server::Display::new()?; let display: wayland_server::Display<Application> = wayland_server::Display::new()?;
let dh = display.handle(); let dh = display.handle();
@@ -170,8 +155,8 @@ impl WayVR {
let data_device = DataDeviceState::new::<Application>(&dh); let data_device = DataDeviceState::new::<Application>(&dh);
let mut seat = seat_state.new_wl_seat(&dh, "wayvr"); let mut seat = seat_state.new_wl_seat(&dh, "wayvr");
let dummy_width = 1280; let dummy_width = 1920;
let dummy_height = 720; let dummy_height = 1080;
let dummy_milli_hz = 60000; /* refresh rate in millihertz */ let dummy_milli_hz = 60000; /* refresh rate in millihertz */
let output = Output::new( let output = Output::new(
@@ -193,41 +178,26 @@ impl WayVR {
output.change_current_state(Some(mode), None, None, None); output.change_current_state(Some(mode), None, None, None);
output.set_preferred(mode); output.set_preferred(mode);
let egl_data = egl_data::EGLData::new()?; let main_device = {
let (major, minor) = gfx_extras.drm_device.as_ref().context("No DRM device!")?;
let smithay_display = smithay_wrapper::get_egl_display(&egl_data)?; libc::makedev(*major as _, *minor as _)
let smithay_context = smithay_wrapper::get_egl_context(&egl_data, &smithay_display)?;
let render_node = egl::EGLDevice::device_for_display(&smithay_display)
.and_then(|device| device.try_get_render_node());
let gles_renderer = unsafe { GlesRenderer::new(smithay_context)? };
let dmabuf_default_feedback = match render_node {
Ok(Some(node)) => {
let dmabuf_formats = gles_renderer.dmabuf_formats();
let dmabuf_default_feedback =
DmabufFeedbackBuilder::new(node.dev_id(), dmabuf_formats)
.build()
.unwrap();
Some(dmabuf_default_feedback)
}
Ok(None) => {
log::warn!("dmabuf: Failed to query render node");
None
}
Err(err) => {
log::warn!("dmabuf: Failed to get egl device for display: {err}");
None
}
}; };
let dmabuf_state = dmabuf_default_feedback.map_or_else( // this will throw a compile-time error if smithay's drm-fourcc is out of sync with wlx-capture's
|| { let mut formats: Vec<smithay::backend::allocator::Format> = vec![];
let dmabuf_formats = gles_renderer.dmabuf_formats();
for f in gfx_extras.drm_formats.iter() {
formats.push(f.clone());
}
let dmabuf_state = DmabufFeedbackBuilder::new(main_device, formats.clone())
.build()
.map_or_else(
|_| {
log::info!("Falling back to zwp_linux_dmabuf_v1 version 3.");
let mut dmabuf_state = DmabufState::new(); let mut dmabuf_state = DmabufState::new();
let dmabuf_global = let dmabuf_global =
dmabuf_state.create_global::<Application>(&display.handle(), dmabuf_formats); dmabuf_state.create_global::<Application>(&display.handle(), formats);
(dmabuf_state, dmabuf_global, None) (dmabuf_state, dmabuf_global, None)
}, },
|default_feedback| { |default_feedback| {
@@ -250,7 +220,10 @@ impl WayVR {
let tasks = SyncEventQueue::new(); let tasks = SyncEventQueue::new();
let dma_importer = ImageImporter::new(gfx);
let state = Application { let state = Application {
image_importer: dma_importer,
compositor, compositor,
xdg_shell, xdg_shell,
seat_state, seat_state,
@@ -259,109 +232,54 @@ impl WayVR {
wayvr_tasks: tasks.clone(), wayvr_tasks: tasks.clone(),
redraw_requests: HashSet::new(), redraw_requests: HashSet::new(),
dmabuf_state, dmabuf_state,
gles_renderer,
}; };
let time_start = get_millis(); let time_start = get_millis();
let ipc_server = WayVRServer::new()?;
let state = WayVRState { let state = WayVRState {
time_start, time_start,
manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?, manager: client::WayVRCompositor::new(state, display, seat_keyboard, seat_pointer)?,
displays: DisplayVec::new(),
processes: ProcessVec::new(), processes: ProcessVec::new(),
egl_data: Rc::new(egl_data),
wm: Rc::new(RefCell::new(window::WindowManager::new())), wm: Rc::new(RefCell::new(window::WindowManager::new())),
config, config,
dashboard_display: None,
ticks: 0, ticks: 0,
tasks, tasks,
signals: SyncEventQueue::new(),
cur_modifiers: 0, cur_modifiers: 0,
signals,
mouse_freeze: Instant::now(),
window_to_overlay: HashMap::new(),
overlay_to_window: SecondaryMap::new(),
}; };
Ok(Self { state, ipc_server }) Ok(Self { state })
}
pub fn render_display(&mut self, display: display::DisplayHandle) -> anyhow::Result<bool> {
let display = self
.state
.displays
.get_mut(&display)
.context(STR_INVALID_HANDLE_DISP)?;
/* Buffer warm-up is required, always two first calls of this function are always rendered */
if !display.wants_redraw && display.rendered_frame_count >= 2 {
// Nothing changed, do not render
return Ok(false);
}
if !display.visible {
// Display is invisible, do not render
return Ok(false);
}
// millis since the start of wayvr
let time_ms = get_millis() - self.state.time_start;
display.tick_render(&mut self.state.manager.state.gles_renderer, time_ms)?;
display.wants_redraw = false;
Ok(true)
} }
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn tick_events(&mut self, app: &AppState) -> anyhow::Result<Vec<TickTask>> { pub fn tick_events(&mut self, app: &mut AppState) -> anyhow::Result<Vec<TickTask>> {
let mut tasks: Vec<TickTask> = Vec::new(); let mut tasks: Vec<TickTask> = Vec::new();
self.ipc_server.tick(&mut server_ipc::TickParams { app.ipc_server.tick(&mut ipc_server::TickParams {
state: &mut self.state, wayland_state: &mut self.state,
input_state: &app.input_state,
tasks: &mut tasks, tasks: &mut tasks,
app, signals: &app.wayvr_signals,
}); });
// Check for redraw events
for (_, disp) in self.state.displays.iter_mut() {
for disp_window in &disp.displayed_windows {
if self
.state
.manager
.state
.check_redraw(disp_window.toplevel.wl_surface())
{
disp.wants_redraw = true;
}
}
}
// Tick all child processes // Tick all child processes
let mut to_remove: SmallVec<[(process::ProcessHandle, display::DisplayHandle); 2]> = let mut to_remove: SmallVec<[process::ProcessHandle; 2]> = SmallVec::new();
SmallVec::new();
for (handle, process) in self.state.processes.iter_mut() { for (handle, process) in self.state.processes.iter_mut() {
if !process.is_running() { if !process.is_running() {
to_remove.push((handle, process.display_handle())); to_remove.push(handle);
} }
} }
for (p_handle, disp_handle) in &to_remove { for p_handle in &to_remove {
self.state.processes.remove(p_handle); self.state.processes.remove(p_handle);
if let Some(display) = self.state.displays.get_mut(disp_handle) {
display
.tasks
.send(display::DisplayTask::ProcessCleanup(*p_handle));
display.wants_redraw = true;
}
}
for (handle, display) in self.state.displays.iter_mut() {
display.tick(&self.state.config, &handle, &mut self.state.signals);
} }
if !to_remove.is_empty() { if !to_remove.is_empty() {
self.state.signals.send(WayVRSignal::BroadcastStateChanged( app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged(
packet_server::WvrStateChanged::ProcessRemoved, packet_server::WvrStateChanged::ProcessRemoved,
)); ));
} }
@@ -392,17 +310,11 @@ impl WayVR {
.state .state
.wm .wm
.borrow_mut() .borrow_mut()
.create_window(client.display_handle, &toplevel); .create_window(&toplevel, process_handle);
let Some(display) = self.state.displays.get_mut(&client.display_handle) //TODO: create overlay
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); app.wayvr_signals.send(WayVRSignal::BroadcastStateChanged(
self.state.signals.send(WayVRSignal::BroadcastStateChanged(
packet_server::WvrStateChanged::WindowCreated, packet_server::WvrStateChanged::WindowCreated,
)); ));
} }
@@ -419,18 +331,15 @@ impl WayVR {
continue; continue;
}; };
let Some(display) = self.state.displays.get_mut(&client.display_handle) if let Some(oid) = self.state.window_to_overlay.get(&window_handle) {
else { app.tasks.enqueue(TaskType::Overlay(OverlayTask::Drop(
log::warn!("DropToplevel: Couldn't find matching display"); OverlaySelector::Id(*oid),
continue; )));
}; }
display.remove_window(window_handle);
wm.remove_window(window_handle); wm.remove_window(window_handle);
drop(wm); drop(wm);
display.reposition_windows();
} }
} }
WayVRTask::ProcessTerminationRequest(process_handle) => { WayVRTask::ProcessTerminationRequest(process_handle) => {
@@ -441,12 +350,11 @@ impl WayVR {
} }
} }
self.state self.state.manager.tick_wayland(&mut self.state.processes)?;
.manager
.tick_wayland(&mut self.state.displays, &mut self.state.processes)?;
if self.state.ticks.is_multiple_of(200) { if self.state.ticks.is_multiple_of(200) {
self.state.manager.cleanup_clients(); self.state.manager.cleanup_clients();
self.state.manager.cleanup_handles();
} }
self.state.ticks += 1; self.state.ticks += 1;
@@ -454,44 +362,6 @@ impl WayVR {
Ok(tasks) Ok(tasks)
} }
pub fn tick_finish(&mut self) -> anyhow::Result<()> {
self.state
.manager
.state
.gles_renderer
.with_context(|gl| unsafe {
gl.Flush();
gl.Finish();
})?;
Ok(())
}
#[allow(dead_code)]
pub fn get_primary_display(displays: &DisplayVec) -> Option<display::DisplayHandle> {
for (idx, cell) in displays.vec.iter().enumerate() {
if let Some(cell) = cell
&& cell.obj.primary
{
return Some(DisplayVec::get_handle(cell, idx));
}
}
None
}
pub fn get_display_by_name(
displays: &DisplayVec,
name: &str,
) -> Option<display::DisplayHandle> {
for (idx, cell) in displays.vec.iter().enumerate() {
if let Some(cell) = cell
&& cell.obj.name == name
{
return Some(DisplayVec::get_handle(cell, idx));
}
}
None
}
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) { pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) {
self.state self.state
.tasks .tasks
@@ -500,24 +370,30 @@ impl WayVR {
} }
impl WayVRState { impl WayVRState {
pub fn send_mouse_move(&mut self, display: display::DisplayHandle, x: u32, y: u32) { pub fn send_mouse_move(&mut self, handle: window::WindowHandle, x: u32, y: u32) {
if let Some(display) = self.displays.get(&display) { if self.mouse_freeze > Instant::now() {
display.send_mouse_move(&self.config, &mut self.manager, x, y); return;
}
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&handle) {
window.send_mouse_move(&mut self.manager, x, y);
} }
} }
pub fn send_mouse_down(&mut self, display: display::DisplayHandle, index: MouseIndex) { pub fn send_mouse_down(&mut self, handle: window::WindowHandle, index: MouseIndex) {
if let Some(display) = self.displays.get_mut(&display) { self.mouse_freeze =
display.send_mouse_down(&mut self.manager, index); Instant::now() + Duration::from_millis(self.config.click_freeze_time_ms as _);
if let Some(window) = self.wm.borrow_mut().windows.get_mut(&handle) {
window.send_mouse_down(&mut self.manager, index);
} }
} }
pub fn send_mouse_up(&mut self, index: MouseIndex) { pub fn send_mouse_up(&mut self, index: MouseIndex) {
Display::send_mouse_up(&mut self.manager, index); Window::send_mouse_up(&mut self.manager, index);
} }
pub fn send_mouse_scroll(&mut self, delta: WheelDelta) { pub fn send_mouse_scroll(&mut self, delta: WheelDelta) {
Display::send_mouse_scroll(&mut self.manager, delta); Window::send_mouse_scroll(&mut self.manager, delta);
} }
pub fn send_key(&mut self, virtual_key: u32, down: bool) { pub fn send_key(&mut self, virtual_key: u32, down: bool) {
@@ -541,125 +417,9 @@ impl WayVRState {
self.cur_modifiers = modifiers; 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);
}
}
pub fn set_display_layout(
&mut self,
display: display::DisplayHandle,
layout: packet_server::WvrDisplayWindowLayout,
) {
if let Some(display) = self.displays.get_mut(&display) {
display.set_layout(layout);
}
}
pub fn get_render_data(
&self,
display: display::DisplayHandle,
) -> Option<&egl_data::RenderData> {
self.displays
.get(&display)
.map(|display| &display.render_data)
}
pub fn create_display(
&mut self,
width: u16,
height: u16,
name: &str,
primary: bool,
) -> anyhow::Result<display::DisplayHandle> {
let display = display::Display::new(DisplayInitParams {
wm: self.wm.clone(),
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(
packet_server::WvrStateChanged::DisplayCreated,
));
Ok(handle)
}
pub fn destroy_display(&mut self, handle: display::DisplayHandle) -> anyhow::Result<()> {
let Some(display) = self.displays.get(&handle) else {
anyhow::bail!("Display not found");
};
if let Some(overlay_id) = display.overlay_id {
self.signals.send(WayVRSignal::DropOverlay(overlay_id));
} else {
log::warn!("Destroying display without OverlayID set"); // This shouldn't happen, but log it anyways.
}
let mut process_names = Vec::<String>::new();
for (_, process) in self.processes.iter_mut() {
if process.display_handle() == handle {
process_names.push(process.get_name());
}
}
if !display.displayed_windows.is_empty() || !process_names.is_empty() {
anyhow::bail!(
"Display is not empty. Attached processes: {}",
process_names.join(", ")
);
}
self.manager.cleanup_clients();
for client in &self.manager.clients {
if client.display_handle == handle {
// This shouldn't happen, but make sure we are all set to destroy this display
anyhow::bail!("Wayland client still exists");
}
}
self.displays.remove(&handle);
self.signals.send(WayVRSignal::BroadcastStateChanged(
packet_server::WvrStateChanged::DisplayRemoved,
));
Ok(())
}
pub fn get_or_create_dashboard_display(
&mut self,
width: u16,
height: u16,
name: &str,
) -> anyhow::Result<(bool /* newly created? */, display::DisplayHandle)> {
if let Some(handle) = &self.dashboard_display {
// ensure it still exists
if self.displays.get(handle).is_some() {
return Ok((false, *handle));
}
}
let new_disp = self.create_display(width, height, name, false)?;
self.dashboard_display = Some(new_disp);
Ok((true, new_disp))
}
// Check if process with given arguments already exists // Check if process with given arguments already exists
pub fn process_query( pub fn process_query(
&self, &self,
display_handle: display::DisplayHandle,
exec_path: &str, exec_path: &str,
args: &[&str], args: &[&str],
_env: &[(&str, &str)], _env: &[(&str, &str)],
@@ -668,10 +428,7 @@ impl WayVRState {
if let Some(cell) = &cell if let Some(cell) = &cell
&& let process::Process::Managed(process) = &cell.obj && let process::Process::Managed(process) = &cell.obj
{ {
if process.display_handle != display_handle if process.exec_path != exec_path || process.args != args {
|| process.exec_path != exec_path
|| process.args != args
{
continue; continue;
} }
return Some(process::ProcessVec::get_handle(cell, idx)); return Some(process::ProcessVec::get_handle(cell, idx));
@@ -681,40 +438,39 @@ impl WayVRState {
None None
} }
pub fn add_external_process( pub fn add_external_process(&mut self, pid: u32) -> process::ProcessHandle {
&mut self,
display_handle: display::DisplayHandle,
pid: u32,
) -> process::ProcessHandle {
self.processes self.processes
.add(process::Process::External(process::ExternalProcess { .add(process::Process::External(process::ExternalProcess { pid }))
pid,
display_handle,
}))
} }
pub fn spawn_process( pub fn spawn_process(
&mut self, &mut self,
display_handle: display::DisplayHandle,
exec_path: &str, exec_path: &str,
args: &[&str], args: &[&str],
env: &[(&str, &str)], env: &[(&str, &str)],
working_dir: Option<&str>, working_dir: Option<&str>,
userdata: HashMap<String, String>, userdata: HashMap<String, String>,
) -> anyhow::Result<process::ProcessHandle> { ) -> anyhow::Result<process::ProcessHandle> {
let display = self let auth_key = generate_auth_key();
.displays
.get_mut(&display_handle)
.context(STR_INVALID_HANDLE_DISP)?;
let res = display.spawn_process(exec_path, args, env, working_dir)?; let mut cmd = std::process::Command::new(exec_path);
self.configure_env(&mut cmd, auth_key.as_str());
cmd.args(args);
if let Some(working_dir) = working_dir {
cmd.current_dir(working_dir);
}
for e in env {
cmd.env(e.0, e.1);
}
let child = cmd.spawn().context("Failed to spawn child process")?;
let handle = self let handle = self
.processes .processes
.add(process::Process::Managed(process::WayVRProcess { .add(process::Process::Managed(process::WayVRProcess {
auth_key: res.auth_key, auth_key,
child: res.child, child,
display_handle,
exec_path: String::from(exec_path), exec_path: String::from(exec_path),
userdata, userdata,
args: args.iter().map(|x| String::from(*x)).collect(), args: args.iter().map(|x| String::from(*x)).collect(),
@@ -731,6 +487,25 @@ impl WayVRState {
Ok(handle) Ok(handle)
} }
fn configure_env(&self, cmd: &mut std::process::Command, auth_key: &str) {
cmd.env_remove("DISPLAY"); // Goodbye X11
cmd.env(
"WAYLAND_DISPLAY",
self.manager.wayland_env.display_num_string(),
);
cmd.env("WAYVR_DISPLAY_AUTH", auth_key);
}
}
fn generate_auth_key() -> String {
let uuid = uuid::Uuid::new_v4();
uuid.to_string()
}
pub struct SpawnProcessResult {
pub auth_key: String,
pub child: std::process::Child,
} }
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
@@ -751,3 +526,34 @@ pub enum WayVRAction {
}, },
ToggleDashboard, ToggleDashboard,
} }
struct SurfaceBufWithImageContainer {
inner: RefCell<SurfaceBufWithImage>,
}
#[derive(Clone)]
pub struct SurfaceBufWithImage {
buffer: wl_buffer::WlBuffer,
pub image: Arc<ImageView>,
pub transform: Transform,
pub scale: i32,
}
impl SurfaceBufWithImage {
fn apply_to_surface(self, surface_data: &SurfaceData) {
let container = surface_data.data_map.get_or_insert(|| unsafe {
SurfaceBufWithImageContainer {
inner: RefCell::new(MaybeUninit::uninit().assume_init()),
}
});
container.inner.replace(self);
}
pub fn get_from_surface(surface_data: &SurfaceData) -> Option<Self> {
surface_data
.data_map
.get::<SurfaceBufWithImageContainer>()
.map(|x| x.inner.borrow().clone())
}
}

View File

@@ -4,14 +4,11 @@ use wayvr_ipc::packet_server;
use crate::gen_id; use crate::gen_id;
use super::display;
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct WayVRProcess { pub struct WayVRProcess {
pub auth_key: String, pub auth_key: String,
pub child: std::process::Child, pub child: std::process::Child,
pub display_handle: display::DisplayHandle,
pub exec_path: String, pub exec_path: String,
pub args: Vec<String>, pub args: Vec<String>,
@@ -24,7 +21,6 @@ pub struct WayVRProcess {
#[derive(Debug)] #[derive(Debug)]
pub struct ExternalProcess { pub struct ExternalProcess {
pub pid: u32, pub pid: u32,
pub display_handle: display::DisplayHandle,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -34,13 +30,6 @@ pub enum Process {
} }
impl Process { impl Process {
pub const fn display_handle(&self) -> display::DisplayHandle {
match self {
Self::Managed(p) => p.display_handle,
Self::External(p) => p.display_handle,
}
}
pub fn is_running(&mut self) -> bool { pub fn is_running(&mut self) -> bool {
match self { match self {
Self::Managed(p) => p.is_running(), Self::Managed(p) => p.is_running(),
@@ -67,13 +56,11 @@ impl Process {
Self::Managed(p) => packet_server::WvrProcess { Self::Managed(p) => packet_server::WvrProcess {
name: p.get_name().unwrap_or_else(|| String::from("unknown")), name: p.get_name().unwrap_or_else(|| String::from("unknown")),
userdata: p.userdata.clone(), userdata: p.userdata.clone(),
display_handle: p.display_handle.as_packet(),
handle: handle.as_packet(), handle: handle.as_packet(),
}, },
Self::External(p) => packet_server::WvrProcess { Self::External(p) => packet_server::WvrProcess {
name: p.get_name().unwrap_or_else(|| String::from("unknown")), name: p.get_name().unwrap_or_else(|| String::from("unknown")),
userdata: HashMap::default(), userdata: HashMap::default(),
display_handle: p.display_handle.as_packet(),
handle: handle.as_packet(), handle: handle.as_packet(),
}, },
} }

View File

@@ -1,54 +0,0 @@
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())? })
}
pub fn get_egl_context(
data: &egl_data::EGLData,
display: &smithay_egl::EGLDisplay,
) -> anyhow::Result<smithay_egl::EGLContext> {
let display_ptr = display.get_display_handle().handle;
debug_assert!(std::ptr::eq(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,
) -> u32 {
unsafe {
let mut tex = 0;
gl.GenTextures(1, &raw 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
}
}

View File

@@ -1,39 +1,36 @@
use smithay::wayland::shell::xdg::ToplevelSurface; use smithay::{
input,
utils::{Logical, Point},
wayland::shell::xdg::ToplevelSurface,
};
use wayvr_ipc::packet_server; use wayvr_ipc::packet_server;
use crate::gen_id; use crate::{
backend::wayvr::{client::WayVRCompositor, process},
use super::display; gen_id,
subsystem::hid::WheelDelta,
};
#[derive(Debug)] #[derive(Debug)]
pub struct Window { pub struct Window {
pub pos_x: i32,
pub pos_y: i32,
pub size_x: u32, pub size_x: u32,
pub size_y: u32, pub size_y: u32,
pub visible: bool, pub visible: bool,
pub toplevel: ToplevelSurface, pub toplevel: ToplevelSurface,
pub display_handle: display::DisplayHandle, pub process: process::ProcessHandle,
} }
impl Window { impl Window {
pub fn new(display_handle: display::DisplayHandle, toplevel: &ToplevelSurface) -> Self { fn new(toplevel: &ToplevelSurface, process: process::ProcessHandle) -> Self {
Self { Self {
pos_x: 0,
pos_y: 0,
size_x: 0, size_x: 0,
size_y: 0, size_y: 0,
visible: true, visible: true,
toplevel: toplevel.clone(), toplevel: toplevel.clone(),
display_handle, process,
} }
} }
pub const fn set_pos(&mut self, pos_x: i32, pos_y: i32) {
self.pos_x = pos_x;
self.pos_y = pos_y;
}
pub fn set_size(&mut self, size_x: u32, size_y: u32) { pub fn set_size(&mut self, size_x: u32, size_y: u32) {
self.toplevel.with_pending_state(|state| { self.toplevel.with_pending_state(|state| {
//state.bounds = Some((size_x as i32, size_y as i32).into()); //state.bounds = Some((size_x as i32, size_y as i32).into());
@@ -44,6 +41,86 @@ impl Window {
self.size_x = size_x; self.size_x = size_x;
self.size_y = size_y; self.size_y = size_y;
} }
pub fn send_mouse_move(&self, manager: &mut WayVRCompositor, x: u32, y: u32) {
let surf = self.toplevel.wl_surface().clone();
let point = Point::<f64, Logical>::from((f64::from(x as i32), f64::from(y as i32)));
manager.seat_pointer.motion(
&mut manager.state,
Some((surf, Point::from((0.0, 0.0)))),
&input::pointer::MotionEvent {
serial: manager.serial_counter.next_serial(),
time: 0,
location: point,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
const fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
match index {
super::MouseIndex::Left => 0x110, /* BTN_LEFT */
super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */
super::MouseIndex::Right => 0x111, /* BTN_RIGHT */
}
}
pub fn send_mouse_down(&mut self, manager: &mut WayVRCompositor, index: super::MouseIndex) {
let surf = self.toplevel.wl_surface().clone();
// Change keyboard focus to pressed window
manager.seat_keyboard.set_focus(
&mut manager.state,
Some(surf),
manager.serial_counter.next_serial(),
);
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
button: Self::get_mouse_index_number(index),
serial: manager.serial_counter.next_serial(),
time: 0,
state: smithay::backend::input::ButtonState::Pressed,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
pub fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) {
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
button: Self::get_mouse_index_number(index),
serial: manager.serial_counter.next_serial(),
time: 0,
state: smithay::backend::input::ButtonState::Released,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
pub fn send_mouse_scroll(manager: &mut WayVRCompositor, delta: WheelDelta) {
manager.seat_pointer.axis(
&mut manager.state,
input::pointer::AxisFrame {
source: None,
relative_direction: (
smithay::backend::input::AxisRelativeDirection::Identical,
smithay::backend::input::AxisRelativeDirection::Identical,
),
time: 0,
axis: (f64::from(delta.x), f64::from(-delta.y)),
v120: Some((0, (delta.y * -64.0) as i32)),
stop: (false, false),
},
);
manager.seat_pointer.frame(&mut manager.state);
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -72,10 +149,10 @@ impl WindowManager {
pub fn create_window( pub fn create_window(
&mut self, &mut self,
display_handle: display::DisplayHandle,
toplevel: &ToplevelSurface, toplevel: &ToplevelSurface,
process: process::ProcessHandle,
) -> WindowHandle { ) -> WindowHandle {
self.windows.add(Window::new(display_handle, toplevel)) self.windows.add(Window::new(toplevel, process))
} }
pub fn remove_window(&mut self, window_handle: WindowHandle) { pub fn remove_window(&mut self, window_handle: WindowHandle) {

View File

@@ -10,6 +10,7 @@ use std::{
use anyhow::Context; use anyhow::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wgui::gfx::WGfx;
use wlx_common::{common::LeftRight, config::GeneralConfig, windowing::Positioning}; use wlx_common::{common::LeftRight, config::GeneralConfig, windowing::Positioning};
use crate::{ use crate::{
@@ -19,7 +20,9 @@ use crate::{
}, },
config::load_config_with_conf_d, config::load_config_with_conf_d,
config_io, config_io,
overlays::wayvr::{WayVRData, executable_exists_in_path}, graphics::WGfxExtras,
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
overlays::wayvr::WayVRData,
}; };
// Flat version of RelativeTo // Flat version of RelativeTo
@@ -135,9 +138,6 @@ pub struct WayVRDashboard {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct WayVRConfig { pub struct WayVRConfig {
#[serde(default = "def_true")]
pub run_compositor_at_start: bool,
#[serde(default = "Default::default")] #[serde(default = "Default::default")]
pub catalogs: HashMap<String, WayVRCatalog>, pub catalogs: HashMap<String, WayVRCatalog>,
@@ -201,9 +201,12 @@ impl WayVRConfig {
pub fn post_load( pub fn post_load(
&self, &self,
gfx: Arc<WGfx>,
gfx_extras: &WGfxExtras,
config: &GeneralConfig, config: &GeneralConfig,
tasks: &mut TaskContainer, tasks: &mut TaskContainer,
) -> anyhow::Result<Option<Rc<RefCell<WayVRData>>>> { signals: SyncEventQueue<WayVRSignal>,
) -> anyhow::Result<Rc<RefCell<WayVRData>>> {
let primary_count = self let primary_count = self
.displays .displays
.iter() .iter()
@@ -212,10 +215,6 @@ impl WayVRConfig {
if primary_count > 1 { if primary_count > 1 {
anyhow::bail!("Number of primary displays is more than 1") anyhow::bail!("Number of primary displays is more than 1")
} else if primary_count == 0 {
log::warn!(
"No primary display specified. External Wayland applications will not be attached."
);
} }
for (catalog_name, catalog) in &self.catalogs { for (catalog_name, catalog) in &self.catalogs {
@@ -231,15 +230,12 @@ impl WayVRConfig {
} }
} }
if self.run_compositor_at_start { Ok(Rc::new(RefCell::new(WayVRData::new(
// Start Wayland server instantly gfx,
Ok(Some(Rc::new(RefCell::new(WayVRData::new( gfx_extras,
Self::get_wayvr_config(config, self)?, Self::get_wayvr_config(config, self)?,
)?)))) signals,
} else { )?)))
// Lazy-init WayVR later if the user requested
Ok(None)
}
} }
} }
@@ -258,6 +254,19 @@ fn get_default_dashboard_exec() -> (
(String::from("wayvr-dashboard"), None) (String::from("wayvr-dashboard"), None)
} }
pub fn executable_exists_in_path(command: &str) -> bool {
let Ok(path) = std::env::var("PATH") else {
return false; // very unlikely to happen
};
for dir in path.split(':') {
let exec_path = std::path::PathBuf::from(dir).join(command);
if exec_path.exists() && exec_path.is_file() {
return true; // executable found
}
}
false
}
pub fn load_wayvr() -> WayVRConfig { pub fn load_wayvr() -> WayVRConfig {
let config_root_path = config_io::ConfigRoot::WayVR.ensure_dir(); let config_root_path = config_io::ConfigRoot::WayVR.ensure_dir();
log::info!("WayVR Config root path: {}", config_root_path.display()); log::info!("WayVR Config root path: {}", config_root_path.display());

View File

@@ -5,7 +5,7 @@ use std::{
}; };
use anyhow::Context; use anyhow::Context;
use smallvec::SmallVec; use smallvec::{SmallVec, smallvec};
use vulkano::{ use vulkano::{
VulkanError, VulkanObject, VulkanError, VulkanObject,
device::Device, device::Device,
@@ -19,12 +19,7 @@ use vulkano::{
sync::Sharing, sync::Sharing,
}; };
use wgui::gfx::WGfx; use wgui::gfx::WGfx;
use wlx_capture::frame::{ use wlx_capture::{DrmFormat, DrmFourcc, DrmModifier, frame::DmabufFrame};
DRM_FORMAT_ABGR8888, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888,
DRM_FORMAT_XBGR2101010, DRM_FORMAT_XRGB8888, DmabufFrame, DrmFormat, FourCC,
};
pub const DRM_FORMAT_MOD_INVALID: u64 = 0xff_ffff_ffff_ffff;
pub trait WGfxDmabuf { pub trait WGfxDmabuf {
fn dmabuf_texture_ex( fn dmabuf_texture_ex(
@@ -47,7 +42,7 @@ impl WGfxDmabuf for WGfx {
modifiers: &[u64], modifiers: &[u64],
) -> anyhow::Result<Arc<Image>> { ) -> anyhow::Result<Arc<Image>> {
let extent = [frame.format.width, frame.format.height, 1]; let extent = [frame.format.width, frame.format.height, 1];
let format = fourcc_to_vk(frame.format.fourcc)?; let format = fourcc_to_vk(frame.format.drm_format.code)?;
let image = unsafe { let image = unsafe {
create_dmabuf_image( create_dmabuf_image(
@@ -116,11 +111,11 @@ impl WGfxDmabuf for WGfx {
} }
fn dmabuf_texture(&self, frame: DmabufFrame) -> anyhow::Result<Arc<Image>> { fn dmabuf_texture(&self, frame: DmabufFrame) -> anyhow::Result<Arc<Image>> {
let mut modifiers: Vec<u64> = vec![]; let mut modifiers: SmallVec<[u64; 4]> = smallvec![];
let mut tiling: ImageTiling = ImageTiling::Optimal; let mut tiling: ImageTiling = ImageTiling::Optimal;
let mut layouts: Vec<SubresourceLayout> = vec![]; let mut layouts: Vec<SubresourceLayout> = vec![];
if frame.format.modifier != DRM_FORMAT_MOD_INVALID { if !matches!(frame.format.drm_format.modifier, DrmModifier::Invalid) {
(0..frame.num_planes).for_each(|i| { (0..frame.num_planes).for_each(|i| {
let plane = &frame.planes[i]; let plane = &frame.planes[i];
layouts.push(SubresourceLayout { layouts.push(SubresourceLayout {
@@ -130,7 +125,7 @@ impl WGfxDmabuf for WGfx {
array_pitch: None, array_pitch: None,
depth_pitch: None, depth_pitch: None,
}); });
modifiers.push(frame.format.modifier); modifiers.push(frame.format.drm_format.modifier.into());
}); });
tiling = ImageTiling::DrmFormatModifier; tiling = ImageTiling::DrmFormatModifier;
} }
@@ -304,50 +299,56 @@ pub(super) unsafe fn create_dmabuf_image(
} }
} }
pub fn get_drm_formats(device: Arc<Device>) -> Vec<DrmFormat> { pub(super) fn get_drm_formats(device: Arc<Device>) -> Vec<DrmFormat> {
let possible_formats = [ let possible_formats = [
DRM_FORMAT_ABGR8888.into(), DrmFourcc::Abgr8888,
DRM_FORMAT_XBGR8888.into(), DrmFourcc::Xbgr8888,
DRM_FORMAT_ARGB8888.into(), DrmFourcc::Argb8888,
DRM_FORMAT_XRGB8888.into(), DrmFourcc::Xrgb8888,
DRM_FORMAT_ABGR2101010.into(), DrmFourcc::Abgr2101010,
DRM_FORMAT_XBGR2101010.into(), DrmFourcc::Xbgr2101010,
]; ];
let mut final_formats = vec![]; let mut out_formats = vec![];
for &f in &possible_formats { for &code in &possible_formats {
let Ok(vk_fmt) = fourcc_to_vk(f) else { let Ok(vk_fmt) = fourcc_to_vk(code) else {
continue; continue;
}; };
let Ok(props) = device.physical_device().format_properties(vk_fmt) else { let Ok(props) = device.physical_device().format_properties(vk_fmt) else {
continue; continue;
}; };
let mut fmt = DrmFormat {
fourcc: f, for m in props
modifiers: props
.drm_format_modifier_properties .drm_format_modifier_properties
.iter() .iter()
// important bit: only allow single-plane
.filter(|m| m.drm_format_modifier_plane_count == 1) .filter(|m| m.drm_format_modifier_plane_count == 1)
.map(|m| m.drm_format_modifier) .map(|m| m.drm_format_modifier)
.collect(), {
}; out_formats.push(DrmFormat {
fmt.modifiers.push(DRM_FORMAT_MOD_INVALID); // implicit modifiers support code,
final_formats.push(fmt); modifier: DrmModifier::from(m),
} });
log::debug!("Supported DRM formats:");
for f in &final_formats {
log::debug!(" {} {:?}", f.fourcc, f.modifiers);
}
final_formats
} }
pub fn fourcc_to_vk(fourcc: FourCC) -> anyhow::Result<Format> { // accept implicit modifiers
match fourcc.value { out_formats.push(DrmFormat {
DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(Format::R8G8B8A8_UNORM), code,
DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(Format::B8G8R8A8_UNORM), modifier: DrmModifier::Invalid,
DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => Ok(Format::A2B10G10R10_UNORM_PACK32), });
}
log::debug!("Supported DRM formats:");
for f in &out_formats {
log::debug!(" {} {:?}", f.code, f.modifier);
}
out_formats
}
pub fn fourcc_to_vk(fourcc: DrmFourcc) -> anyhow::Result<Format> {
match fourcc {
DrmFourcc::Abgr8888 | DrmFourcc::Xbgr8888 => Ok(Format::R8G8B8A8_UNORM),
DrmFourcc::Argb8888 | DrmFourcc::Xrgb8888 => Ok(Format::B8G8R8A8_UNORM),
DrmFourcc::Abgr2101010 | DrmFourcc::Xbgr2101010 => Ok(Format::A2B10G10R10_UNORM_PACK32),
_ => anyhow::bail!("Unsupported format {fourcc}"), _ => anyhow::bail!("Unsupported format {fourcc}"),
} }
} }

View File

@@ -19,7 +19,7 @@ use wgui::gfx::WGfx;
#[cfg(feature = "openvr")] #[cfg(feature = "openvr")]
use vulkano::instance::InstanceCreateFlags; use vulkano::instance::InstanceCreateFlags;
use wlx_capture::frame::DrmFormat; use wlx_capture::DrmFormat;
use crate::shaders::{frag_color, frag_grid, frag_screen, frag_srgb, vert_quad}; use crate::shaders::{frag_color, frag_grid, frag_screen, frag_srgb, vert_quad};
@@ -73,6 +73,7 @@ pub struct WGfxExtras {
pub queue_capture: Option<Arc<Queue>>, pub queue_capture: Option<Arc<Queue>>,
pub quad_verts: Vert2Buf, pub quad_verts: Vert2Buf,
pub fallback_image: Arc<ImageView>, pub fallback_image: Arc<ImageView>,
pub drm_device: Option<(i64, i64)>,
} }
impl WGfxExtras { impl WGfxExtras {
@@ -135,12 +136,23 @@ impl WGfxExtras {
let fallback_image = ImageView::new_default(fallback_image)?; let fallback_image = ImageView::new_default(fallback_image)?;
let p = gfx.device.physical_device().properties();
let drm_device = if let (Some(maj), Some(min)) = (p.render_major, p.render_minor) {
log::info!("DRM render device: {maj} {min}");
Some((maj, min))
} else {
log::warn!("No DRM device.");
None
};
Ok(Self { Ok(Self {
shaders, shaders,
drm_formats, drm_formats,
queue_capture, queue_capture,
quad_verts, quad_verts,
fallback_image, fallback_image,
drm_device,
}) })
} }
} }
@@ -271,6 +283,13 @@ pub fn init_openxr_graphics(
.ext_image_drm_format_modifier; .ext_image_drm_format_modifier;
} }
if physical_device
.supported_extensions()
.ext_physical_device_drm
{
device_extensions.ext_physical_device_drm = true;
}
let device_extensions_raw = device_extensions let device_extensions_raw = device_extensions
.into_iter() .into_iter()
.filter_map(|(name, enabled)| { .filter_map(|(name, enabled)| {
@@ -432,6 +451,11 @@ pub fn init_openvr_graphics(
my_extensions.ext_filter_cubic = true; my_extensions.ext_filter_cubic = true;
} }
if p.supported_extensions().ext_physical_device_drm {
// needed for wayland_server
my_extensions.ext_physical_device_drm = true;
}
log::debug!( log::debug!(
"Device exts for {}: {:?}", "Device exts for {}: {:?}",
p.properties().device_name, p.properties().device_name,

View File

@@ -0,0 +1,99 @@
use std::{cell::RefCell, rc::Rc};
use wayvr_ipc::packet_server;
#[cfg(feature = "wayvr")]
use crate::{backend::wayvr, overlays::wayvr::WayVRData};
use crate::{
backend::{
self,
task::{InputTask, OverlayTask, TaskType},
},
ipc::signal::WayVRSignal,
state::AppState,
windowing::{OverlaySelector, manager::OverlayWindowManager},
};
#[cfg(feature = "wayvr")]
fn process_tick_tasks(
tick_tasks: Vec<backend::wayvr::TickTask>,
r_wayvr: &Rc<RefCell<WayVRData>>,
) -> anyhow::Result<()> {
for tick_task in tick_tasks {
match tick_task {
backend::wayvr::TickTask::NewExternalProcess(request) => {
let mut wayvr = r_wayvr.borrow_mut();
log::info!("Registering external process with PID {}", request.pid);
wayvr.data.state.add_external_process(request.pid);
wayvr
.data
.state
.manager
.add_client(wayvr::client::WayVRClient {
client: request.client,
pid: request.pid,
});
}
}
}
Ok(())
}
pub fn tick_events<O>(
app: &mut AppState,
_overlays: &mut OverlayWindowManager<O>,
) -> anyhow::Result<()>
where
O: Default,
{
#[cfg(feature = "wayvr")]
let wayland_server = app.wayland_server.clone();
while let Some(signal) = app.wayvr_signals.read() {
match signal {
#[cfg(feature = "wayvr")]
WayVRSignal::BroadcastStateChanged(packet) => {
app.ipc_server
.broadcast(packet_server::PacketServer::WvrStateChanged(packet));
}
WayVRSignal::DeviceHaptics(device, haptics) => {
app.tasks
.enqueue(TaskType::Input(InputTask::Haptics { device, haptics }));
}
WayVRSignal::DropOverlay(overlay_id) => {
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Id(
overlay_id,
))));
}
WayVRSignal::CustomTask(custom_task) => {
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::ModifyPanel(custom_task)));
}
}
}
#[cfg(feature = "wayvr")]
{
if let Some(wayland_server) = wayland_server {
let tick_tasks = wayland_server.borrow_mut().data.tick_events(app)?;
process_tick_tasks(tick_tasks, &wayland_server)?;
}
}
#[cfg(not(feature = "wayvr"))]
{
use super::ipc_server::TickParams;
app.ipc_server.tick(&mut TickParams {
input_state: &app.input_state,
signals: &app.wayvr_signals,
});
}
Ok(())
}

View File

@@ -1,6 +1,10 @@
use crate::state::AppState; #[cfg(feature = "wayvr")]
use crate::backend::wayvr::{self, WayVRState};
use super::{TickTask, WayVRSignal, display, process, window}; use crate::{
backend::input::InputState,
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
};
use bytes::BufMut; use bytes::BufMut;
use glam::Vec3A; use glam::Vec3A;
use interprocess::local_socket::{self, ToNsName, traits::Listener}; use interprocess::local_socket::{self, ToNsName, traits::Listener};
@@ -71,9 +75,12 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> Option<Payload> {
} }
pub struct TickParams<'a> { pub struct TickParams<'a> {
pub state: &'a mut super::WayVRState, #[cfg(feature = "wayvr")]
pub tasks: &'a mut Vec<TickTask>, pub wayland_state: &'a mut WayVRState,
pub app: &'a AppState, #[cfg(feature = "wayvr")]
pub tasks: &'a mut Vec<wayvr::TickTask>,
pub signals: &'a SyncEventQueue<WayVRSignal>,
pub input_state: &'a InputState,
} }
pub fn gen_args_vec(input: &str) -> Vec<&str> { pub fn gen_args_vec(input: &str) -> Vec<&str> {
@@ -149,44 +156,11 @@ impl Connection {
Ok(()) Ok(())
} }
fn handle_wvr_display_list(
&mut self,
params: &TickParams,
serial: ipc::Serial,
) -> anyhow::Result<()> {
let list: Vec<packet_server::WvrDisplay> = params
.state
.displays
.vec
.iter()
.enumerate()
.filter_map(|(idx, opt_cell)| {
let Some(cell) = opt_cell else {
return None;
};
let display = &cell.obj;
Some(display.as_packet(display::DisplayHandle::new(idx as u32, cell.generation)))
})
.collect();
send_packet(
&mut self.conn,
&ipc::data_encode(&PacketServer::WvrDisplayListResponse(
serial,
packet_server::WvrDisplayList { list },
)),
)?;
Ok(())
}
fn handle_wlx_input_state( fn handle_wlx_input_state(
&mut self, &mut self,
params: &TickParams, params: &TickParams,
serial: ipc::Serial, serial: ipc::Serial,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let input_state = &params.app.input_state;
let to_arr = |vec: &Vec3A| -> [f32; 3] { [vec.x, vec.y, vec.z] }; let to_arr = |vec: &Vec3A| -> [f32; 3] { [vec.x, vec.y, vec.z] };
send_packet( send_packet(
@@ -194,12 +168,12 @@ impl Connection {
&ipc::data_encode(&PacketServer::WlxInputStateResponse( &ipc::data_encode(&PacketServer::WlxInputStateResponse(
serial, serial,
packet_server::WlxInputState { packet_server::WlxInputState {
hmd_pos: to_arr(&input_state.hmd.translation), hmd_pos: to_arr(&params.input_state.hmd.translation),
left: WlxInputStatePointer { left: WlxInputStatePointer {
pos: to_arr(&input_state.pointers[0].raw_pose.translation), pos: to_arr(&params.input_state.pointers[0].raw_pose.translation),
}, },
right: WlxInputStatePointer { right: WlxInputStatePointer {
pos: to_arr(&input_state.pointers[0].raw_pose.translation), pos: to_arr(&params.input_state.pointers[0].raw_pose.translation),
}, },
}, },
)), )),
@@ -208,148 +182,55 @@ impl Connection {
Ok(()) Ok(())
} }
fn handle_wvr_display_create( #[cfg(feature = "wayvr")]
fn handle_wvr_window_list(
&mut self, &mut self,
params: &mut TickParams, params: &mut TickParams,
serial: ipc::Serial, serial: ipc::Serial,
packet_params: packet_client::WvrDisplayCreateParams,
) -> anyhow::Result<()> {
let display_handle = params.state.create_display(
packet_params.width,
packet_params.height,
&packet_params.name,
false,
)?;
params
.tasks
.push(TickTask::NewDisplay(packet_params, Some(display_handle)));
send_packet(
&mut self.conn,
&ipc::data_encode(&PacketServer::WvrDisplayCreateResponse(
serial,
display_handle.as_packet(),
)),
)?;
Ok(())
}
fn handle_wvr_display_remove(
&mut self,
params: &mut TickParams,
serial: ipc::Serial,
handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
let res = params
.state
.destroy_display(display::DisplayHandle::from_packet(handle))
.map_err(|e| format!("{e:?}"));
send_packet(
&mut self.conn,
&ipc::data_encode(&PacketServer::WvrDisplayRemoveResponse(serial, res)),
)?;
Ok(())
}
fn handle_wvr_display_set_visible(
params: &mut TickParams,
handle: packet_server::WvrDisplayHandle,
visible: bool,
) {
params.state.signals.send(WayVRSignal::DisplayVisibility(
display::DisplayHandle::from_packet(handle),
visible,
));
}
fn handle_wvr_display_set_window_layout(
params: &mut TickParams,
handle: packet_server::WvrDisplayHandle,
layout: packet_server::WvrDisplayWindowLayout,
) {
params.state.signals.send(WayVRSignal::DisplayWindowLayout(
display::DisplayHandle::from_packet(handle),
layout,
));
}
fn handle_wvr_display_window_list(
&mut self,
params: &mut TickParams,
serial: ipc::Serial,
display_handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut send = |list: Option<packet_server::WvrWindowList>| -> anyhow::Result<()> { let mut send = |list: Option<packet_server::WvrWindowList>| -> anyhow::Result<()> {
send_packet( send_packet(
&mut self.conn, &mut self.conn,
&ipc::data_encode(&PacketServer::WvrDisplayWindowListResponse(serial, list)), &ipc::data_encode(&PacketServer::WvrWindowListResponse(serial, list)),
) )
}; };
let Some(display) = params
.state
.displays
.get(&display::DisplayHandle::from_packet(display_handle.clone()))
else {
return send(None);
};
send(Some(packet_server::WvrWindowList { send(Some(packet_server::WvrWindowList {
list: display list: params
.displayed_windows .wayland_state
.iter()
.filter_map(|disp_win| {
params
.state
.wm .wm
.borrow_mut() .borrow_mut()
.windows .windows
.get(&disp_win.window_handle) .iter()
.map(|win| packet_server::WvrWindow { .map(|(handle, win)| packet_server::WvrWindow {
handle: window::WindowHandle::as_packet(&disp_win.window_handle), handle: wayvr::window::WindowHandle::as_packet(&handle),
process_handle: process::ProcessHandle::as_packet( process_handle: wayvr::process::ProcessHandle::as_packet(&win.process),
&disp_win.process_handle,
),
pos_x: win.pos_x,
pos_y: win.pos_y,
size_x: win.size_x, size_x: win.size_x,
size_y: win.size_y, size_y: win.size_y,
visible: win.visible, visible: win.visible,
display_handle: display_handle.clone(),
})
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
})) }))
} }
#[cfg(feature = "wayvr")]
fn handle_wvr_window_set_visible( fn handle_wvr_window_set_visible(
params: &mut TickParams, params: &mut TickParams,
handle: packet_server::WvrWindowHandle, handle: packet_server::WvrWindowHandle,
visible: bool, visible: bool,
) { ) {
let to_resize = if let Some(window) = params if let Some(window) = params
.state .wayland_state
.wm .wm
.borrow_mut() .borrow_mut()
.windows .windows
.get_mut(&window::WindowHandle::from_packet(handle)) .get_mut(&wayvr::window::WindowHandle::from_packet(handle))
{ {
window.visible = visible; window.visible = visible;
Some(window.display_handle)
} else {
None
};
if let Some(to_resize) = to_resize
&& let Some(display) = params.state.displays.get_mut(&to_resize)
{
display.reposition_windows();
display.trigger_rerender();
} }
} }
#[cfg(feature = "wayvr")]
fn handle_wvr_process_launch( fn handle_wvr_process_launch(
&mut self, &mut self,
params: &mut TickParams, params: &mut TickParams,
@@ -359,8 +240,7 @@ impl Connection {
let args_vec = gen_args_vec(&packet_params.args); let args_vec = gen_args_vec(&packet_params.args);
let env_vec = gen_env_vec(&packet_params.env); let env_vec = gen_env_vec(&packet_params.env);
let res = params.state.spawn_process( let res = params.wayland_state.spawn_process(
super::display::DisplayHandle::from_packet(packet_params.target_display),
&packet_params.exec, &packet_params.exec,
&args_vec, &args_vec,
&env_vec, &env_vec,
@@ -378,34 +258,14 @@ impl Connection {
Ok(()) Ok(())
} }
fn handle_wvr_display_get( #[cfg(feature = "wayvr")]
&mut self,
params: &TickParams,
serial: ipc::Serial,
display_handle: packet_server::WvrDisplayHandle,
) -> anyhow::Result<()> {
let native_handle = &display::DisplayHandle::from_packet(display_handle);
let disp = params
.state
.displays
.get(native_handle)
.map(|disp| disp.as_packet(*native_handle));
send_packet(
&mut self.conn,
&ipc::data_encode(&PacketServer::WvrDisplayGetResponse(serial, disp)),
)?;
Ok(())
}
fn handle_wvr_process_list( fn handle_wvr_process_list(
&mut self, &mut self,
params: &TickParams, params: &TickParams,
serial: ipc::Serial, serial: ipc::Serial,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let list: Vec<packet_server::WvrProcess> = params let list: Vec<packet_server::WvrProcess> = params
.state .wayland_state
.processes .processes
.vec .vec
.iter() .iter()
@@ -415,7 +275,10 @@ impl Connection {
return None; return None;
}; };
let process = &cell.obj; let process = &cell.obj;
Some(process.to_packet(process::ProcessHandle::new(idx as u32, cell.generation))) Some(process.to_packet(wayvr::process::ProcessHandle::new(
idx as u32,
cell.generation,
)))
}) })
.collect(); .collect();
@@ -431,12 +294,13 @@ impl Connection {
} }
// This request doesn't return anything to the client // This request doesn't return anything to the client
#[cfg(feature = "wayvr")]
fn handle_wvr_process_terminate( fn handle_wvr_process_terminate(
params: &mut TickParams, params: &mut TickParams,
process_handle: packet_server::WvrProcessHandle, process_handle: packet_server::WvrProcessHandle,
) { ) {
let native_handle = &process::ProcessHandle::from_packet(process_handle); let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle);
let process = params.state.processes.get_mut(native_handle); let process = params.wayland_state.processes.get_mut(native_handle);
let Some(process) = process else { let Some(process) = process else {
return; return;
@@ -445,15 +309,16 @@ impl Connection {
process.terminate(); process.terminate();
} }
#[cfg(feature = "wayvr")]
fn handle_wvr_process_get( fn handle_wvr_process_get(
&mut self, &mut self,
params: &TickParams, params: &TickParams,
serial: ipc::Serial, serial: ipc::Serial,
process_handle: packet_server::WvrProcessHandle, process_handle: packet_server::WvrProcessHandle,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let native_handle = &process::ProcessHandle::from_packet(process_handle); let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle);
let process = params let process = params
.state .wayland_state
.processes .processes
.get(native_handle) .get(native_handle)
.map(|process| process.to_packet(*native_handle)); .map(|process| process.to_packet(*native_handle));
@@ -466,11 +331,13 @@ impl Connection {
Ok(()) Ok(())
} }
fn handle_wlx_haptics( fn handle_wlx_device_haptics(
params: &mut TickParams, params: &mut TickParams,
device: usize,
haptics_params: packet_client::WlxHapticsParams, haptics_params: packet_client::WlxHapticsParams,
) { ) {
params.state.signals.send(super::WayVRSignal::Haptics( params.signals.send(WayVRSignal::DeviceHaptics(
device,
crate::backend::input::Haptics { crate::backend::input::Haptics {
duration: haptics_params.duration, duration: haptics_params.duration,
frequency: haptics_params.frequency, frequency: haptics_params.frequency,
@@ -486,9 +353,8 @@ impl Connection {
use crate::backend::task::{ModifyPanelCommand, ModifyPanelTask}; use crate::backend::task::{ModifyPanelCommand, ModifyPanelTask};
params params
.state
.signals .signals
.send(super::WayVRSignal::CustomTask(ModifyPanelTask { .send(WayVRSignal::CustomTask(ModifyPanelTask {
overlay: custom_params.overlay, overlay: custom_params.overlay,
element: custom_params.element, element: custom_params.element,
command: match custom_params.command { command: match custom_params.command {
@@ -511,6 +377,9 @@ impl Connection {
})); }));
} }
// FIXME: we should probably respond an error to the client in case if wayland server feature is disabled
// fix this after we're done with the webkit-based wayvr-dashboard
#[allow(unused_variables)]
fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> { fn process_payload(&mut self, params: &mut TickParams, payload: Payload) -> anyhow::Result<()> {
let packet: PacketClient = ipc::data_decode(&payload)?; let packet: PacketClient = ipc::data_decode(&payload)?;
@@ -524,44 +393,32 @@ impl Connection {
PacketClient::WlxInputState(serial) => { PacketClient::WlxInputState(serial) => {
self.handle_wlx_input_state(params, serial)?; self.handle_wlx_input_state(params, serial)?;
} }
PacketClient::WvrDisplayList(serial) => { PacketClient::WvrWindowList(serial) => {
self.handle_wvr_display_list(params, serial)?; #[cfg(feature = "wayvr")]
} self.handle_wvr_window_list(params, serial)?;
PacketClient::WvrDisplayGet(serial, display_handle) => {
self.handle_wvr_display_get(params, serial, display_handle)?;
}
PacketClient::WvrDisplayRemove(serial, display_handle) => {
self.handle_wvr_display_remove(params, serial, display_handle)?;
}
PacketClient::WvrDisplaySetVisible(display_handle, visible) => {
Self::handle_wvr_display_set_visible(params, display_handle, visible);
}
PacketClient::WvrDisplaySetWindowLayout(display_handle, layout) => {
Self::handle_wvr_display_set_window_layout(params, display_handle, layout);
}
PacketClient::WvrDisplayWindowList(serial, display_handle) => {
self.handle_wvr_display_window_list(params, serial, display_handle)?;
} }
PacketClient::WvrWindowSetVisible(window_handle, visible) => { PacketClient::WvrWindowSetVisible(window_handle, visible) => {
#[cfg(feature = "wayvr")]
Self::handle_wvr_window_set_visible(params, window_handle, visible); Self::handle_wvr_window_set_visible(params, window_handle, visible);
} }
PacketClient::WvrProcessGet(serial, process_handle) => { PacketClient::WvrProcessGet(serial, process_handle) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_process_get(params, serial, process_handle)?; self.handle_wvr_process_get(params, serial, process_handle)?;
} }
PacketClient::WvrProcessList(serial) => { PacketClient::WvrProcessList(serial) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_process_list(params, serial)?; self.handle_wvr_process_list(params, serial)?;
} }
PacketClient::WvrProcessLaunch(serial, packet_params) => { PacketClient::WvrProcessLaunch(serial, packet_params) => {
#[cfg(feature = "wayvr")]
self.handle_wvr_process_launch(params, serial, packet_params)?; self.handle_wvr_process_launch(params, serial, packet_params)?;
} }
PacketClient::WvrDisplayCreate(serial, packet_params) => {
self.handle_wvr_display_create(params, serial, packet_params)?;
}
PacketClient::WvrProcessTerminate(process_handle) => { PacketClient::WvrProcessTerminate(process_handle) => {
#[cfg(feature = "wayvr")]
Self::handle_wvr_process_terminate(params, process_handle); Self::handle_wvr_process_terminate(params, process_handle);
} }
PacketClient::WlxHaptics(haptics_params) => { PacketClient::WlxDeviceHaptics(device, haptics_params) => {
Self::handle_wlx_haptics(params, haptics_params); Self::handle_wlx_device_haptics(params, device, haptics_params);
} }
PacketClient::WlxModifyPanel(custom_params) => { PacketClient::WlxModifyPanel(custom_params) => {
Self::handle_wlx_panel(params, custom_params); Self::handle_wlx_panel(params, custom_params);

View File

@@ -0,0 +1,4 @@
pub mod event_queue;
pub mod events;
pub mod ipc_server;
pub mod signal;

View File

@@ -0,0 +1,8 @@
#[derive(Clone)]
pub enum WayVRSignal {
#[cfg(feature = "wayvr")]
BroadcastStateChanged(wayvr_ipc::packet_server::WvrStateChanged),
DeviceHaptics(usize, crate::backend::input::Haptics),
DropOverlay(crate::windowing::OverlayID),
CustomTask(crate::backend::task::ModifyPanelTask),
}

View File

@@ -22,6 +22,7 @@ mod config;
mod config_io; mod config_io;
mod graphics; mod graphics;
mod gui; mod gui;
mod ipc;
mod overlays; mod overlays;
mod shaders; mod shaders;
mod state; mod state;

View File

@@ -222,9 +222,13 @@ impl OverlayBackend for ScreenBackend {
fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> {
// want panic; must be some if should_render was not Unable // want panic; must be some if should_render was not Unable
let capture = self.cur_frame.as_ref().unwrap(); let capture = self.cur_frame.as_ref().unwrap();
let image = capture.image.clone();
// want panic; must be Some if cur_frame is also Some // want panic; must be Some if cur_frame is also Some
self.pipeline.as_mut().unwrap().render(&capture, app, rdr)?; self.pipeline
.as_mut()
.unwrap()
.render(image, capture.mouse.as_ref(), app, rdr)?;
self.capture.request_new_frame(); self.capture.request_new_frame();
Ok(()) Ok(())
} }

View File

@@ -17,8 +17,8 @@ use wgui::gfx::{
pipeline::{WGfxPipeline, WPipelineCreateInfo}, pipeline::{WGfxPipeline, WPipelineCreateInfo},
}; };
use wlx_capture::{ use wlx_capture::{
WlxCapture, DrmFormat, WlxCapture,
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame}, frame::{self as wlx_frame, FrameFormat, MouseMeta, WlxFrame},
}; };
use wlx_common::{config::GeneralConfig, overlays::StereoMode}; use wlx_common::{config::GeneralConfig, overlays::StereoMode};
@@ -39,7 +39,8 @@ struct BufPass {
buf_vert: Subbuffer<[Vert2Uv]>, buf_vert: Subbuffer<[Vert2Uv]>,
} }
pub(super) struct ScreenPipeline { /// A render pipeline that supports mouse + stereo
pub struct ScreenPipeline {
mouse: BufPass, mouse: BufPass,
pass: SmallVec<[BufPass; 2]>, pass: SmallVec<[BufPass; 2]>,
pipeline: Arc<WGfxPipeline<Vert2Uv>>, pipeline: Arc<WGfxPipeline<Vert2Uv>>,
@@ -49,11 +50,7 @@ pub(super) struct ScreenPipeline {
} }
impl ScreenPipeline { impl ScreenPipeline {
pub(super) fn new( pub fn new(meta: &FrameMeta, app: &mut AppState, stereo: StereoMode) -> anyhow::Result<Self> {
meta: &FrameMeta,
app: &mut AppState,
stereo: StereoMode,
) -> anyhow::Result<Self> {
let extentf = [meta.extent[0] as f32, meta.extent[1] as f32]; let extentf = [meta.extent[0] as f32, meta.extent[1] as f32];
let pipeline = app.gfx.create_pipeline( let pipeline = app.gfx.create_pipeline(
@@ -198,13 +195,13 @@ impl ScreenPipeline {
Ok(BufPass { pass, buf_vert }) Ok(BufPass { pass, buf_vert })
} }
pub(super) fn render( pub fn render(
&mut self, &mut self,
capture: &WlxCaptureOut, image: Arc<ImageView>,
mouse: Option<&MouseMeta>,
app: &mut AppState, app: &mut AppState,
rdr: &mut RenderResources, rdr: &mut RenderResources,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let view = ImageView::new_default(capture.image.clone())?;
self.buf_alpha.write()?[0] = rdr.alpha; self.buf_alpha.write()?[0] = rdr.alpha;
for (eye, cmd_buf) in rdr.cmd_bufs.iter_mut().enumerate() { for (eye, cmd_buf) in rdr.cmd_bufs.iter_mut().enumerate() {
@@ -212,11 +209,11 @@ impl ScreenPipeline {
current current
.pass .pass
.update_sampler(0, view.clone(), app.gfx.texture_filter)?; .update_sampler(0, image.clone(), app.gfx.texture_filter)?;
cmd_buf.run_ref(&current.pass)?; cmd_buf.run_ref(&current.pass)?;
if let Some(mouse) = capture.mouse.as_ref() { if let Some(mouse) = mouse.as_ref() {
let size = CURSOR_SIZE * self.extentf[1]; let size = CURSOR_SIZE * self.extentf[1];
let half_size = size * 0.5; let half_size = size * 0.5;
@@ -325,10 +322,10 @@ impl WlxCaptureIn {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct WlxCaptureOut { pub(super) struct WlxCaptureOut {
image: Arc<Image>, pub(super) image: Arc<ImageView>,
format: FrameFormat, pub(super) format: FrameFormat,
mouse: Option<MouseMeta>, pub(super) mouse: Option<MouseMeta>,
} }
impl WlxCaptureOut { impl WlxCaptureOut {
@@ -340,10 +337,6 @@ impl WlxCaptureOut {
format: self.image.format(), format: self.image.format(),
} }
} }
pub(super) const fn get_transform(&self) -> Transform {
self.format.transform
}
} }
fn upload_image( fn upload_image(
@@ -390,7 +383,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
let format = frame.format; let format = frame.format;
match me.gfx.dmabuf_texture(frame) { match me.gfx.dmabuf_texture(frame) {
Ok(image) => Some(WlxCaptureOut { Ok(image) => Some(WlxCaptureOut {
image, image: ImageView::new_default(image).ok()?,
format, format,
mouse: None, mouse: None,
}), }),
@@ -406,7 +399,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
return None; return None;
}; };
let format = match fourcc_to_vk(frame.format.fourcc) { let format = match fourcc_to_vk(frame.format.drm_format.code) {
Ok(x) => x, Ok(x) => x,
Err(e) => { Err(e) => {
log::error!("{}: {}", me.name, e); log::error!("{}: {}", me.name, e);
@@ -439,7 +432,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
}?; }?;
Some(WlxCaptureOut { Some(WlxCaptureOut {
image, image: ImageView::new_default(image).ok()?,
format: frame.format, format: frame.format,
mouse: None, mouse: None,
}) })
@@ -447,7 +440,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
WlxFrame::MemPtr(frame) => { WlxFrame::MemPtr(frame) => {
log::trace!("{}: New MemPtr frame", me.name); log::trace!("{}: New MemPtr frame", me.name);
let format = match fourcc_to_vk(frame.format.fourcc) { let format = match fourcc_to_vk(frame.format.drm_format.code) {
Ok(x) => x, Ok(x) => x,
Err(e) => { Err(e) => {
log::error!("{}: {}", me.name, e); log::error!("{}: {}", me.name, e);
@@ -459,7 +452,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
let image = upload_image(me, frame.format.width, frame.format.height, format, data)?; let image = upload_image(me, frame.format.width, frame.format.height, format, data)?;
Some(WlxCaptureOut { Some(WlxCaptureOut {
image, image: ImageView::new_default(image).ok()?,
format: frame.format, format: frame.format,
mouse: frame.mouse, mouse: frame.mouse,
}) })

View File

@@ -14,7 +14,7 @@ use crate::{
}; };
pub mod backend; pub mod backend;
mod capture; pub mod capture;
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub mod mirror; pub mod mirror;
#[cfg(feature = "pipewire")] #[cfg(feature = "pipewire")]

File diff suppressed because it is too large Load Diff

View File

@@ -12,11 +12,6 @@ version: 1
# "software": Read pixel data to memory via glReadPixels() every time a content has been updated. Minor performance impact on large resolutions # "software": Read pixel data to memory via glReadPixels() every time a content has been updated. Minor performance impact on large resolutions
blit_method: "dmabuf" 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)
run_compositor_at_start: false
# Automatically close overlays with zero window count? # Automatically close overlays with zero window count?
auto_hide: true auto_hide: true

View File

@@ -27,6 +27,7 @@ use crate::{
config_io::{self, get_config_file_path}, config_io::{self, get_config_file_path},
graphics::WGfxExtras, graphics::WGfxExtras,
gui, gui,
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
subsystem::{audio::AudioOutput, dbus::DbusConnector, input::HidWrapper}, subsystem::{audio::AudioOutput, dbus::DbusConnector, input::HidWrapper},
}; };
@@ -53,11 +54,14 @@ pub struct AppState {
pub xr_backend: XrBackend, pub xr_backend: XrBackend,
pub ipc_server: ipc_server::WayVRServer,
pub wayvr_signals: SyncEventQueue<WayVRSignal>,
#[cfg(feature = "osc")] #[cfg(feature = "osc")]
pub osc_sender: Option<OscSender>, pub osc_sender: Option<OscSender>,
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
pub wayvr: Option<Rc<RefCell<WayVRData>>>, // Dynamically created if requested pub wayland_server: Option<Rc<RefCell<WayVRData>>>,
} }
#[allow(unused_mut)] #[allow(unused_mut)]
@@ -71,17 +75,26 @@ impl AppState {
let mut tasks = TaskContainer::new(); let mut tasks = TaskContainer::new();
let session = AppSession::load(); let session = AppSession::load();
let wayvr_signals = SyncEventQueue::new();
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
let wayvr = session let wayland_server = session
.wayvr_config .wayvr_config
.post_load(&session.config, &mut tasks)?; .post_load(
gfx.clone(),
&gfx_extras,
&session.config,
&mut tasks,
wayvr_signals.clone(),
)
.inspect_err(|e| log::error!("Could not initialize wayland server: {e:?}"))
.ok();
let mut hid_provider = HidWrapper::new(); let mut hid_provider = HidWrapper::new();
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
if let Some(wayvr) = wayvr.as_ref() { if let Some(wayland_server) = wayland_server.as_ref() {
hid_provider.set_wayvr(wayvr.clone()); hid_provider.set_wayvr(wayland_server.clone());
} }
#[cfg(feature = "osc")] #[cfg(feature = "osc")]
@@ -114,6 +127,8 @@ impl AppState {
let dbus = DbusConnector::default(); let dbus = DbusConnector::default();
let ipc_server = ipc_server::WayVRServer::new()?;
Ok(Self { Ok(Self {
session, session,
tasks, tasks,
@@ -135,30 +150,17 @@ impl AppState {
)?, )?,
dbus, dbus,
xr_backend, xr_backend,
ipc_server,
wayvr_signals,
#[cfg(feature = "osc")] #[cfg(feature = "osc")]
osc_sender, osc_sender,
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
wayvr, wayland_server,
}) })
} }
#[cfg(feature = "wayvr")]
#[allow(dead_code)]
pub fn get_wayvr(&mut self) -> anyhow::Result<Rc<RefCell<WayVRData>>> {
if let Some(wvr) = &self.wayvr {
Ok(wvr.clone())
} else {
let wayvr = Rc::new(RefCell::new(WayVRData::new(
WayVRConfig::get_wayvr_config(&self.session.config, &self.session.wayvr_config)?,
)?));
self.hid_provider.set_wayvr(wayvr.clone());
self.wayvr = Some(wayvr.clone());
Ok(wayvr)
}
}
pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] { pub fn try_load_bytes(path: &str, fallback_data: &'static [u8]) -> &'static [u8] {
if path.is_empty() { if path.is_empty() {
return fallback_data; return fallback_data;
@@ -187,7 +189,7 @@ pub struct AppSession {
pub config: GeneralConfig, pub config: GeneralConfig,
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]
pub wayvr_config: WayVRConfig, pub wayvr_config: WayVRConfig, // TODO: rename to "wayland_server_config"
pub toast_topics: IdMap<ToastTopic, ToastDisplayMethod>, pub toast_topics: IdMap<ToastTopic, ToastDisplayMethod>,
} }