sbs 3d support
This commit is contained in:
1
wlx-overlay-s/src/assets/edit/3d.svg
Normal file
1
wlx-overlay-s/src/assets/edit/3d.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M13 15h3q.825 0 1.413-.587T18 13v-2q0-.825-.587-1.412T16 9h-3zm1.5-1.5v-3H16q.2 0 .35.15t.15.35v2q0 .2-.15.35t-.35.15zm-8 1.5H10q.425 0 .713-.288T11 14v-1q0-.425-.288-.712T10 12q.425 0 .713-.288T11 11v-1q0-.425-.288-.712T10 9H6.5v1.5h3v.75h-2v1.5h2v.75h-3zM4 20q-.825 0-1.412-.587T2 18V6q0-.825.588-1.412T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.587 1.413T20 20z"/></svg>
|
||||||
|
After Width: | Height: | Size: 591 B |
1
wlx-overlay-s/src/assets/edit/3d_bottomtop.svg
Normal file
1
wlx-overlay-s/src/assets/edit/3d_bottomtop.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M5 21q-.825 0-1.412-.587T3 19v-4q0-.825.588-1.412T5 13h14q.825 0 1.413.588T21 15v4q0 .825-.587 1.413T19 21zm0-10q-.825 0-1.412-.587T3 9V5q0-.825.588-1.412T5 3h14q.825 0 1.413.588T21 5v4q0 .825-.587 1.413T19 11zm14-6H5v4h14z"/></svg>
|
||||||
|
After Width: | Height: | Size: 453 B |
1
wlx-overlay-s/src/assets/edit/3d_leftright.svg
Normal file
1
wlx-overlay-s/src/assets/edit/3d_leftright.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h4q.825 0 1.413.588T11 5v14q0 .825-.587 1.413T9 21zm10 0q-.825 0-1.412-.587T13 19V5q0-.825.588-1.412T15 3h4q.825 0 1.413.588T21 5v14q0 .825-.587 1.413T19 21zm4-16h-4v14h4z"/></svg>
|
||||||
|
After Width: | Height: | Size: 453 B |
1
wlx-overlay-s/src/assets/edit/3d_rightleft.svg
Normal file
1
wlx-overlay-s/src/assets/edit/3d_rightleft.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M15 21q-.825 0-1.412-.587T13 19V5q0-.825.588-1.412T15 3h4q.825 0 1.413.588T21 5v14q0 .825-.587 1.413T19 21zM5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h4q.825 0 1.413.588T11 5v14q0 .825-.587 1.413T9 21zM5 5v14h4V5z"/></svg>
|
||||||
|
After Width: | Height: | Size: 452 B |
1
wlx-overlay-s/src/assets/edit/3d_topbottom.svg
Normal file
1
wlx-overlay-s/src/assets/edit/3d_topbottom.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="white" d="M5 11q-.825 0-1.412-.587T3 9V5q0-.825.588-1.412T5 3h14q.825 0 1.413.588T21 5v4q0 .825-.587 1.413T19 11zm0 10q-.825 0-1.412-.587T3 19v-4q0-.825.588-1.412T5 13h14q.825 0 1.413.588T21 15v4q0 .825-.587 1.413T19 21zm0-2h14v-4H5z"/></svg>
|
||||||
|
After Width: | Height: | Size: 453 B |
1
wlx-overlay-s/src/assets/edit/grabbable.svg
Normal file
1
wlx-overlay-s/src/assets/edit/grabbable.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="currentColor" d="m2.1 2.075l19.8 19.8l-1.425 1.425l-1.1-1.1q-.5.375-1.1.588T17 23h-6.95q-.75 0-1.4-.337T7.575 21.7L1.2 12.375l.6-.575q.475-.475 1.125-.55t1.175.3L7 13.575v-3.75L.675 3.5zM21 18.15l-6-5.975V3q0-.425.288-.712T16 2t.713.288T17 3v9.175h2V5q0-.425.288-.712T20 4t.713.288T21 5zm-8-8l-2-2V2q0-.425.288-.712T12 1t.713.288T13 2zm-4-4l-2-2V4q0-.425.288-.712T8 3t.713.288T9 4z"/></svg>
|
||||||
|
After Width: | Height: | Size: 600 B |
@@ -44,6 +44,7 @@
|
|||||||
<TopButton sticky="0" id="top_pos" src="edit/anchor.svg" tooltip="EDIT_MODE.POSITIONING" press="::EditModeTab pos" />
|
<TopButton sticky="0" id="top_pos" src="edit/anchor.svg" tooltip="EDIT_MODE.POSITIONING" press="::EditModeTab pos" />
|
||||||
<TopButton sticky="0" id="top_alpha" src="edit/fade.svg" tooltip="EDIT_MODE.OPACITY" press="::EditModeTab alpha" />
|
<TopButton sticky="0" id="top_alpha" src="edit/fade.svg" tooltip="EDIT_MODE.OPACITY" press="::EditModeTab alpha" />
|
||||||
<TopButton sticky="0" id="top_curve" src="edit/curve.svg" tooltip="EDIT_MODE.ADJUST_CURVATURE" press="::EditModeTab curve" />
|
<TopButton sticky="0" id="top_curve" src="edit/curve.svg" tooltip="EDIT_MODE.ADJUST_CURVATURE" press="::EditModeTab curve" />
|
||||||
|
<TopButton sticky="0" id="top_stereo" src="edit/3d.svg" tooltip="EDIT_MODE.STEREO_MODE" press="::EditModeTab stereo" />
|
||||||
<!-- TopButton sticky="0" id="top_move" src="edit/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" / -->
|
<!-- TopButton sticky="0" id="top_move" src="edit/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" / -->
|
||||||
<!-- TopButton sticky="0" id="top_resize" src="edit/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" / -->
|
<!-- TopButton sticky="0" id="top_resize" src="edit/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" / -->
|
||||||
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
||||||
@@ -67,6 +68,15 @@
|
|||||||
<Slider id="lerp_slider" width="250" height="16" min_value="0.05" max_value="1" value="1" step="0.05" />
|
<Slider id="lerp_slider" width="250" height="16" min_value="0.05" max_value="1" value="1" step="0.05" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="tab_stereo" display="none" height="100" flex_direction="column">
|
||||||
|
<div padding="8" gap="8" justify_content="center" align_items="center">
|
||||||
|
<PosButton id="stereo_none" src="edit/3d.svg" tooltip="EDIT_MODE.STEREO_NONE" press="::EditModeSetStereo none" />
|
||||||
|
<PosButton id="stereo_leftright" src="edit/3d_leftright.svg" tooltip="EDIT_MODE.STEREO_LEFTRIGHT" press="::EditModeSetStereo leftright" />
|
||||||
|
<PosButton id="stereo_rightleft" src="edit/3d_rightleft.svg" tooltip="EDIT_MODE.STEREO_RIGHTLEFT" press="::EditModeSetStereo rightleft" />
|
||||||
|
<PosButton id="stereo_topbottom" src="edit/3d_topbottom.svg" tooltip="EDIT_MODE.STEREO_TOPBOTTOM" press="::EditModeSetStereo topbottom" />
|
||||||
|
<PosButton id="stereo_bottomtop" src="edit/3d_bottomtop.svg" tooltip="EDIT_MODE.STEREO_BOTTOMTOP" press="::EditModeSetStereo bottomtop" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="tab_alpha" display="none" height="100" padding="8" gap="8" justify_content="center" align_items="center">
|
<div id="tab_alpha" display="none" height="100" padding="8" gap="8" justify_content="center" align_items="center">
|
||||||
<div width="100%" padding="8" gap="8" justify_content="center" align_items="center">
|
<div width="100%" padding="8" gap="8" justify_content="center" align_items="center">
|
||||||
<label translation="EDIT_MODE.OPACITY" />
|
<label translation="EDIT_MODE.OPACITY" />
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ pub mod task;
|
|||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub enum XrBackend {
|
||||||
|
OpenXR,
|
||||||
|
OpenVR,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum BackendError {
|
pub enum BackendError {
|
||||||
#[error("backend not supported")]
|
#[error("backend not supported")]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ash::vk::SubmitInfo;
|
use ash::vk::SubmitInfo;
|
||||||
use glam::{Affine3A, Vec3, Vec3A, Vec4};
|
use glam::{Affine3A, Vec3, Vec3A, Vec4};
|
||||||
@@ -8,7 +8,6 @@ use idmap::IdMap;
|
|||||||
use ovr_overlay::overlay::OverlayManager;
|
use ovr_overlay::overlay::OverlayManager;
|
||||||
use ovr_overlay::sys::ETrackingUniverseOrigin;
|
use ovr_overlay::sys::ETrackingUniverseOrigin;
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
VulkanObject,
|
|
||||||
command_buffer::{
|
command_buffer::{
|
||||||
CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer,
|
CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer,
|
||||||
},
|
},
|
||||||
@@ -16,20 +15,22 @@ use vulkano::{
|
|||||||
image::view::ImageView,
|
image::view::ImageView,
|
||||||
image::{Image, ImageLayout},
|
image::{Image, ImageLayout},
|
||||||
sync::{
|
sync::{
|
||||||
AccessFlags, DependencyInfo, ImageMemoryBarrier, PipelineStages,
|
|
||||||
fence::{Fence, FenceCreateInfo},
|
fence::{Fence, FenceCreateInfo},
|
||||||
|
AccessFlags, DependencyInfo, ImageMemoryBarrier, PipelineStages,
|
||||||
},
|
},
|
||||||
|
VulkanObject,
|
||||||
};
|
};
|
||||||
use wgui::gfx::WGfx;
|
use wgui::gfx::WGfx;
|
||||||
|
|
||||||
use crate::backend::input::{HoverResult, PointerHit};
|
use crate::backend::input::{HoverResult, PointerHit};
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
use crate::subsystem::hid::WheelDelta;
|
use crate::subsystem::hid::WheelDelta;
|
||||||
use crate::windowing::Z_ORDER_LINES;
|
|
||||||
use crate::windowing::backend::{
|
use crate::windowing::backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
BackendAttrib, BackendAttribValue, FrameMeta, OverlayBackend, OverlayEventData,
|
||||||
|
RenderResources, ShouldRender,
|
||||||
};
|
};
|
||||||
use crate::windowing::window::{OverlayWindowConfig, OverlayWindowData};
|
use crate::windowing::window::{OverlayWindowConfig, OverlayWindowData};
|
||||||
|
use crate::windowing::Z_ORDER_LINES;
|
||||||
|
|
||||||
use super::overlay::OpenVrOverlayData;
|
use super::overlay::OpenVrOverlayData;
|
||||||
|
|
||||||
@@ -217,6 +218,12 @@ impl OverlayBackend for LineBackend {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn get_attrib(&self, _attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, _: &mut AppState, _value: BackendAttribValue) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transition_layout(
|
pub fn transition_layout(
|
||||||
|
|||||||
@@ -5,40 +5,41 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use ovr_overlay::{
|
use ovr_overlay::{
|
||||||
TrackedDeviceIndex,
|
|
||||||
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
|
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
|
||||||
|
TrackedDeviceIndex,
|
||||||
};
|
};
|
||||||
use vulkano::{Handle, VulkanObject, device::physical::PhysicalDevice};
|
use smallvec::smallvec;
|
||||||
|
use vulkano::{device::physical::PhysicalDevice, Handle, VulkanObject};
|
||||||
use wlx_common::overlays::ToastTopic;
|
use wlx_common::overlays::ToastTopic;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
RUNNING,
|
|
||||||
backend::{
|
backend::{
|
||||||
BackendError,
|
|
||||||
input::interact,
|
input::interact,
|
||||||
openvr::{
|
openvr::{
|
||||||
helpers::adjust_gain,
|
helpers::adjust_gain,
|
||||||
input::{OpenVrInputSource, set_action_manifest},
|
input::{set_action_manifest, OpenVrInputSource},
|
||||||
lines::LinePool,
|
lines::LinePool,
|
||||||
manifest::{install_manifest, uninstall_manifest},
|
manifest::{install_manifest, uninstall_manifest},
|
||||||
overlay::OpenVrOverlayData,
|
overlay::OpenVrOverlayData,
|
||||||
},
|
},
|
||||||
task::{OpenVrTask, OverlayTask, TaskType},
|
task::{OpenVrTask, OverlayTask, TaskType},
|
||||||
|
BackendError, XrBackend,
|
||||||
},
|
},
|
||||||
config::save_state,
|
config::save_state,
|
||||||
graphics::{GpuFutures, init_openvr_graphics},
|
graphics::{init_openvr_graphics, GpuFutures},
|
||||||
overlays::{
|
overlays::{
|
||||||
toast::Toast,
|
toast::Toast,
|
||||||
watch::{WATCH_NAME, watch_fade},
|
watch::{watch_fade, WATCH_NAME},
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::notifications::NotificationManager,
|
subsystem::notifications::NotificationManager,
|
||||||
windowing::{
|
windowing::{
|
||||||
backend::{RenderResources, ShouldRender},
|
backend::{RenderResources, RenderTarget, ShouldRender, StereoMode},
|
||||||
manager::OverlayWindowManager,
|
manager::OverlayWindowManager,
|
||||||
},
|
},
|
||||||
|
RUNNING,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
#[cfg(feature = "wayvr")]
|
||||||
@@ -93,7 +94,7 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
|
|
||||||
let mut app = {
|
let mut app = {
|
||||||
let (gfx, gfx_extras) = init_openvr_graphics(instance_extensions, device_extensions_fn)?;
|
let (gfx, gfx_extras) = init_openvr_graphics(instance_extensions, device_extensions_fn)?;
|
||||||
AppState::from_graphics(gfx, gfx_extras)?
|
AppState::from_graphics(gfx, gfx_extras, XrBackend::OpenVR)?
|
||||||
};
|
};
|
||||||
|
|
||||||
if show_by_default {
|
if show_by_default {
|
||||||
@@ -303,11 +304,13 @@ pub fn openvr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let meta = o.config.backend.frame_meta().unwrap();
|
let meta = o.config.backend.frame_meta().unwrap();
|
||||||
let tgt = o.ensure_staging_image(&mut app, meta.extent)?;
|
let tgt = RenderTarget {
|
||||||
|
views: smallvec![o.ensure_staging_image(&mut app, meta.extent)?],
|
||||||
|
};
|
||||||
let mut rdr = RenderResources::new(app.gfx.clone(), tgt, &meta, 1.0)?;
|
let mut rdr = RenderResources::new(app.gfx.clone(), tgt, &meta, 1.0)?;
|
||||||
o.render(&mut app, &mut rdr)?;
|
o.render(&mut app, &mut rdr)?;
|
||||||
o.data.image_dirty = true;
|
o.data.image_dirty = true;
|
||||||
futures.execute(rdr.end()?)?;
|
futures.execute_results(rdr.end()?)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
use glam::{Affine3A, Vec3, Vec3A};
|
use glam::{Affine3A, Vec3, Vec3A};
|
||||||
use idmap::IdMap;
|
use idmap::IdMap;
|
||||||
use openxr as xr;
|
use openxr as xr;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
f32::consts::PI,
|
f32::consts::PI,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use wgui::gfx::{
|
use wgui::gfx::{
|
||||||
WGfx,
|
|
||||||
cmd::WGfxClearMode,
|
cmd::WGfxClearMode,
|
||||||
pass::WGfxPass,
|
pass::WGfxPass,
|
||||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||||
|
WGfx,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -27,8 +28,8 @@ use vulkano::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
swapchain::{create_swapchain, SwapchainOpts, WlxSwapchain},
|
||||||
CompositionLayer, XrState,
|
CompositionLayer, XrState,
|
||||||
swapchain::{SwapchainOpts, WlxSwapchain, create_swapchain},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static LINE_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
|
static LINE_AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(1);
|
||||||
@@ -156,7 +157,13 @@ impl LinePool {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
for line in self.lines.values_mut() {
|
for line in self.lines.values_mut() {
|
||||||
if let Some(inner) = line.maybe_line.as_mut() {
|
if let Some(inner) = line.maybe_line.as_mut() {
|
||||||
let tgt = line.swapchain.acquire_wait_image()?;
|
let tgt = line
|
||||||
|
.swapchain
|
||||||
|
.acquire_wait_image()?
|
||||||
|
.views
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
self.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
|
self.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
|
||||||
|
|
||||||
@@ -167,7 +174,7 @@ impl LinePool {
|
|||||||
cmd_buffer.run_ref(&self.pass)?;
|
cmd_buffer.run_ref(&self.pass)?;
|
||||||
cmd_buffer.end_rendering()?;
|
cmd_buffer.end_rendering()?;
|
||||||
|
|
||||||
futures.execute((cmd_buffer.queue.clone(), cmd_buffer.build()?))?;
|
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,8 +184,8 @@ impl LinePool {
|
|||||||
pub(super) fn present<'a>(
|
pub(super) fn present<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
xr: &'a XrState,
|
xr: &'a XrState,
|
||||||
) -> anyhow::Result<Vec<CompositionLayer<'a>>> {
|
) -> anyhow::Result<SmallVec<[CompositionLayer<'a>; 2]>> {
|
||||||
let mut quads = Vec::new();
|
let mut quads = SmallVec::new_const();
|
||||||
|
|
||||||
for line in self.lines.values_mut() {
|
for line in self.lines.values_mut() {
|
||||||
line.swapchain.ensure_image_released()?;
|
line.swapchain.ensure_image_released()?;
|
||||||
@@ -186,7 +193,7 @@ impl LinePool {
|
|||||||
if let Some(inner) = line.maybe_line.take() {
|
if let Some(inner) = line.maybe_line.take() {
|
||||||
let quad = xr::CompositionLayerQuad::new()
|
let quad = xr::CompositionLayerQuad::new()
|
||||||
.pose(inner.pose)
|
.pose(inner.pose)
|
||||||
.sub_image(line.swapchain.get_subimage())
|
.sub_image(line.swapchain.get_subimage(0))
|
||||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||||
.space(&xr.stage)
|
.space(&xr.stage)
|
||||||
.size(xr::Extent2Df {
|
.size(xr::Extent2Df {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
ops::Add,
|
ops::Add,
|
||||||
sync::{Arc, atomic::Ordering},
|
sync::{atomic::Ordering, Arc},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -14,25 +14,25 @@ use vulkano::{Handle, VulkanObject};
|
|||||||
use wlx_common::overlays::ToastTopic;
|
use wlx_common::overlays::ToastTopic;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
FRAME_COUNTER, RUNNING,
|
|
||||||
backend::{
|
backend::{
|
||||||
BackendError,
|
|
||||||
input::interact,
|
input::interact,
|
||||||
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
|
openxr::{lines::LinePool, overlay::OpenXrOverlayData},
|
||||||
task::{OverlayTask, TaskType},
|
task::{OverlayTask, TaskType},
|
||||||
|
BackendError, XrBackend,
|
||||||
},
|
},
|
||||||
config::save_state,
|
config::save_state,
|
||||||
graphics::{GpuFutures, init_openxr_graphics},
|
graphics::{init_openxr_graphics, GpuFutures},
|
||||||
overlays::{
|
overlays::{
|
||||||
toast::Toast,
|
toast::Toast,
|
||||||
watch::{WATCH_NAME, watch_fade},
|
watch::{watch_fade, WATCH_NAME},
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::notifications::NotificationManager,
|
subsystem::notifications::NotificationManager,
|
||||||
windowing::{
|
windowing::{
|
||||||
backend::{RenderResources, ShouldRender},
|
backend::{RenderResources, RenderTarget, ShouldRender},
|
||||||
manager::OverlayWindowManager,
|
manager::OverlayWindowManager,
|
||||||
},
|
},
|
||||||
|
FRAME_COUNTER, RUNNING,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "wayvr")]
|
#[cfg(feature = "wayvr")]
|
||||||
@@ -70,7 +70,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
|
|
||||||
let mut app = {
|
let mut app = {
|
||||||
let (gfx, gfx_extras) = init_openxr_graphics(xr_instance.clone(), system)?;
|
let (gfx, gfx_extras) = init_openxr_graphics(xr_instance.clone(), system)?;
|
||||||
AppState::from_graphics(gfx, gfx_extras)?
|
AppState::from_graphics(gfx, gfx_extras, XrBackend::OpenXR)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let environment_blend_mode = {
|
let environment_blend_mode = {
|
||||||
@@ -403,11 +403,12 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
|
|
||||||
if should_render {
|
if should_render {
|
||||||
let meta = o.config.backend.frame_meta().unwrap(); // want panic
|
let meta = o.config.backend.frame_meta().unwrap(); // want panic
|
||||||
let tgt = o.ensure_swapchain_acquire(&app, &xr_state, meta.extent)?;
|
let wsi = o.ensure_swapchain_acquire(&app, &xr_state, meta.extent)?;
|
||||||
|
let tgt = RenderTarget { views: wsi.views };
|
||||||
let mut rdr = RenderResources::new(app.gfx.clone(), tgt, &meta, alpha)?;
|
let mut rdr = RenderResources::new(app.gfx.clone(), tgt, &meta, alpha)?;
|
||||||
o.render(&mut app, &mut rdr)?;
|
o.render(&mut app, &mut rdr)?;
|
||||||
o.data.last_alpha = alpha;
|
o.data.last_alpha = alpha;
|
||||||
futures.execute(rdr.end()?)?;
|
futures.execute_results(rdr.end()?)?;
|
||||||
} else if o.data.swapchain.is_none() {
|
} else if o.data.swapchain.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -439,18 +440,13 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
o.data.swapchain.as_mut().unwrap().ensure_image_released()?;
|
o.data.swapchain.as_mut().unwrap().ensure_image_released()?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let maybe_layer = o.present(&xr_state)?;
|
for layer in o.present(&xr_state)? {
|
||||||
if matches!(maybe_layer, CompositionLayer::None) {
|
layers.push((dist_sq, layer));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
layers.push((dist_sq, maybe_layer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for maybe_layer in lines.present(&xr_state)? {
|
for layer in lines.present(&xr_state)? {
|
||||||
if matches!(maybe_layer, CompositionLayer::None) {
|
layers.push((0.0, layer));
|
||||||
continue;
|
|
||||||
}
|
|
||||||
layers.push((0.0, maybe_layer));
|
|
||||||
}
|
}
|
||||||
// End layer composition
|
// End layer composition
|
||||||
|
|
||||||
@@ -468,7 +464,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
CompositionLayer::Quad(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
CompositionLayer::Quad(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||||
CompositionLayer::Cylinder(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
CompositionLayer::Cylinder(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||||
CompositionLayer::Equirect2(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
CompositionLayer::Equirect2(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||||
CompositionLayer::None => unreachable!(),
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
@@ -523,7 +518,6 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) enum CompositionLayer<'a> {
|
pub(super) enum CompositionLayer<'a> {
|
||||||
None,
|
|
||||||
Quad(xr::CompositionLayerQuad<'a, xr::Vulkan>),
|
Quad(xr::CompositionLayerQuad<'a, xr::Vulkan>),
|
||||||
Cylinder(xr::CompositionLayerCylinderKHR<'a, xr::Vulkan>),
|
Cylinder(xr::CompositionLayerCylinderKHR<'a, xr::Vulkan>),
|
||||||
Equirect2(xr::CompositionLayerEquirect2KHR<'a, xr::Vulkan>),
|
Equirect2(xr::CompositionLayerEquirect2KHR<'a, xr::Vulkan>),
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use glam::Vec3A;
|
use glam::Vec3A;
|
||||||
use openxr::{self as xr, CompositionLayerFlags};
|
use openxr::{self as xr, CompositionLayerFlags};
|
||||||
use std::{f32::consts::PI, sync::Arc};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use vulkano::image::view::ImageView;
|
use std::f32::consts::PI;
|
||||||
use xr::EyeVisibility;
|
use xr::EyeVisibility;
|
||||||
|
|
||||||
use super::{CompositionLayer, XrState, helpers, swapchain::WlxSwapchain};
|
use super::{helpers, swapchain::WlxSwapchain, CompositionLayer, XrState};
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::openxr::swapchain::{SwapchainOpts, create_swapchain},
|
backend::openxr::swapchain::{create_swapchain, SwapchainOpts, WlxSwapchainImage},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::window::OverlayWindowData,
|
windowing::window::OverlayWindowData,
|
||||||
};
|
};
|
||||||
@@ -26,7 +26,7 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
|||||||
app: &AppState,
|
app: &AppState,
|
||||||
xr: &'a XrState,
|
xr: &'a XrState,
|
||||||
extent: [u32; 3],
|
extent: [u32; 3],
|
||||||
) -> anyhow::Result<Arc<ImageView>> {
|
) -> anyhow::Result<WlxSwapchainImage> {
|
||||||
if let Some(swapchain) = self.data.swapchain.as_mut()
|
if let Some(swapchain) = self.data.swapchain.as_mut()
|
||||||
&& swapchain.extent == extent
|
&& swapchain.extent == extent
|
||||||
{
|
{
|
||||||
@@ -34,10 +34,11 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"{}: recreating swapchain at {}x{}",
|
"{}: recreating swapchain at {}x{}x{}",
|
||||||
self.config.name,
|
self.config.name,
|
||||||
extent[0],
|
extent[0],
|
||||||
extent[1],
|
extent[1],
|
||||||
|
extent[2],
|
||||||
);
|
);
|
||||||
let mut swapchain = create_swapchain(xr, app.gfx.clone(), extent, SwapchainOpts::new())?;
|
let mut swapchain = create_swapchain(xr, app.gfx.clone(), extent, SwapchainOpts::new())?;
|
||||||
let tgt = swapchain.acquire_wait_image()?;
|
let tgt = swapchain.acquire_wait_image()?;
|
||||||
@@ -48,21 +49,31 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
|||||||
pub(super) fn present<'a>(
|
pub(super) fn present<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
xr: &'a XrState,
|
xr: &'a XrState,
|
||||||
) -> anyhow::Result<CompositionLayer<'a>> {
|
) -> anyhow::Result<SmallVec<[CompositionLayer<'a>; 2]>> {
|
||||||
|
let mut layers = SmallVec::new_const();
|
||||||
|
|
||||||
let Some(swapchain) = self.data.swapchain.as_mut() else {
|
let Some(swapchain) = self.data.swapchain.as_mut() else {
|
||||||
log::warn!("{}: swapchain not ready", self.config.name);
|
log::trace!("{}: no swapchain", self.config.name);
|
||||||
return Ok(CompositionLayer::None);
|
return Ok(layers);
|
||||||
};
|
};
|
||||||
if !swapchain.ever_acquired {
|
if !swapchain.ever_acquired {
|
||||||
log::warn!("{}: swapchain not rendered", self.config.name);
|
log::warn!("{}: swapchain not rendered", self.config.name);
|
||||||
return Ok(CompositionLayer::None);
|
return Ok(layers);
|
||||||
}
|
}
|
||||||
swapchain.ensure_image_released()?;
|
swapchain.ensure_image_released()?;
|
||||||
|
|
||||||
// overlays without active_state don't get queued for present
|
// overlays without active_state don't get queued for present
|
||||||
let state = self.config.active_state.as_ref().unwrap();
|
let state = self.config.active_state.as_ref().unwrap();
|
||||||
|
|
||||||
let sub_image = swapchain.get_subimage();
|
let sub_images: SmallVec<[_; 2]> = if swapchain.extent[2] > 1 {
|
||||||
|
smallvec![
|
||||||
|
(swapchain.get_subimage(0), EyeVisibility::LEFT),
|
||||||
|
(swapchain.get_subimage(1), EyeVisibility::RIGHT),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
smallvec![(swapchain.get_subimage(0), EyeVisibility::BOTH),]
|
||||||
|
};
|
||||||
|
|
||||||
let transform = state.transform * self.config.backend.frame_meta().unwrap().transform; // contract
|
let transform = state.transform * self.config.backend.frame_meta().unwrap().transform; // contract
|
||||||
|
|
||||||
let aspect_ratio = swapchain.extent[1] as f32 / swapchain.extent[0] as f32;
|
let aspect_ratio = swapchain.extent[1] as f32 / swapchain.extent[0] as f32;
|
||||||
@@ -89,30 +100,35 @@ impl OverlayWindowData<OpenXrOverlayData> {
|
|||||||
let posef = helpers::translation_rotation_to_posef(center_point, quat);
|
let posef = helpers::translation_rotation_to_posef(center_point, quat);
|
||||||
let angle = 2.0 * (scale_x / (2.0 * radius));
|
let angle = 2.0 * (scale_x / (2.0 * radius));
|
||||||
|
|
||||||
let cylinder = xr::CompositionLayerCylinderKHR::new()
|
for sub_image in sub_images {
|
||||||
.layer_flags(flags)
|
let cylinder = xr::CompositionLayerCylinderKHR::new()
|
||||||
.pose(posef)
|
.layer_flags(flags)
|
||||||
.sub_image(sub_image)
|
.pose(posef)
|
||||||
.eye_visibility(EyeVisibility::BOTH)
|
.sub_image(sub_image.0)
|
||||||
.space(&xr.stage)
|
.eye_visibility(sub_image.1)
|
||||||
.radius(radius)
|
.space(&xr.stage)
|
||||||
.central_angle(angle)
|
.radius(radius)
|
||||||
.aspect_ratio(aspect_ratio);
|
.central_angle(angle)
|
||||||
Ok(CompositionLayer::Cylinder(cylinder))
|
.aspect_ratio(aspect_ratio);
|
||||||
|
layers.push(CompositionLayer::Cylinder(cylinder))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let posef = helpers::transform_to_posef(&transform);
|
let posef = helpers::transform_to_posef(&transform);
|
||||||
let quad = xr::CompositionLayerQuad::new()
|
for sub_image in sub_images {
|
||||||
.layer_flags(flags)
|
let quad = xr::CompositionLayerQuad::new()
|
||||||
.pose(posef)
|
.layer_flags(flags)
|
||||||
.sub_image(sub_image)
|
.pose(posef)
|
||||||
.eye_visibility(EyeVisibility::BOTH)
|
.sub_image(sub_image.0)
|
||||||
.space(&xr.stage)
|
.eye_visibility(sub_image.1)
|
||||||
.size(xr::Extent2Df {
|
.space(&xr.stage)
|
||||||
width: scale_x,
|
.size(xr::Extent2Df {
|
||||||
height: scale_y,
|
width: scale_x,
|
||||||
});
|
height: scale_y,
|
||||||
Ok(CompositionLayer::Quad(quad))
|
});
|
||||||
|
layers.push(CompositionLayer::Quad(quad))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(layers)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn after_input(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
pub(super) fn after_input(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ use wgui::gfx::{cmd::WGfxClearMode, pipeline::WPipelineCreateInfo};
|
|||||||
use crate::{
|
use crate::{
|
||||||
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
|
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
|
||||||
config_io,
|
config_io,
|
||||||
graphics::{ExtentExt, GpuFutures, dds::WlxCommandBufferDds},
|
graphics::{dds::WlxCommandBufferDds, ExtentExt, GpuFutures},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
swapchain::{create_swapchain, WlxSwapchain},
|
||||||
CompositionLayer, XrState,
|
CompositionLayer, XrState,
|
||||||
swapchain::{WlxSwapchain, create_swapchain},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) struct Skybox {
|
pub(super) struct Skybox {
|
||||||
@@ -94,7 +94,12 @@ impl Skybox {
|
|||||||
|
|
||||||
let extent = self.view.image().extent();
|
let extent = self.view.image().extent();
|
||||||
let mut swapchain = create_swapchain(xr, app.gfx.clone(), extent, opts)?;
|
let mut swapchain = create_swapchain(xr, app.gfx.clone(), extent, opts)?;
|
||||||
let tgt = swapchain.acquire_wait_image()?;
|
let tgt = swapchain
|
||||||
|
.acquire_wait_image()?
|
||||||
|
.views
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
let pipeline = app.gfx.create_pipeline(
|
let pipeline = app.gfx.create_pipeline(
|
||||||
app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic
|
app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic
|
||||||
app.gfx_extras.shaders.get("frag_srgb").unwrap(), // want panic
|
app.gfx_extras.shaders.get("frag_srgb").unwrap(), // want panic
|
||||||
@@ -119,7 +124,7 @@ impl Skybox {
|
|||||||
cmd_buffer.run_ref(&pass)?;
|
cmd_buffer.run_ref(&pass)?;
|
||||||
cmd_buffer.end_rendering()?;
|
cmd_buffer.end_rendering()?;
|
||||||
|
|
||||||
futures.execute((cmd_buffer.queue.clone(), cmd_buffer.build()?))?;
|
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
|
||||||
|
|
||||||
self.sky = Some(swapchain);
|
self.sky = Some(swapchain);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -148,7 +153,12 @@ impl Skybox {
|
|||||||
WPipelineCreateInfo::new(app.gfx.surface_format).use_blend(AttachmentBlend::alpha()),
|
WPipelineCreateInfo::new(app.gfx.surface_format).use_blend(AttachmentBlend::alpha()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let tgt = swapchain.acquire_wait_image()?;
|
let tgt = swapchain
|
||||||
|
.acquire_wait_image()?
|
||||||
|
.views
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
let pass = pipeline.create_pass(
|
let pass = pipeline.create_pass(
|
||||||
tgt.extent_f32(),
|
tgt.extent_f32(),
|
||||||
app.gfx_extras.quad_verts.clone(),
|
app.gfx_extras.quad_verts.clone(),
|
||||||
@@ -165,7 +175,7 @@ impl Skybox {
|
|||||||
cmd_buffer.run_ref(&pass)?;
|
cmd_buffer.run_ref(&pass)?;
|
||||||
cmd_buffer.end_rendering()?;
|
cmd_buffer.end_rendering()?;
|
||||||
|
|
||||||
futures.execute((cmd_buffer.queue.clone(), cmd_buffer.build()?))?;
|
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
|
||||||
|
|
||||||
self.grid = Some(swapchain);
|
self.grid = Some(swapchain);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -211,7 +221,7 @@ impl Skybox {
|
|||||||
.layer_flags(xr::CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
.layer_flags(xr::CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||||
.pose(pose)
|
.pose(pose)
|
||||||
.radius(10.0)
|
.radius(10.0)
|
||||||
.sub_image(self.sky.as_ref().unwrap().get_subimage())
|
.sub_image(self.sky.as_ref().unwrap().get_subimage(0))
|
||||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||||
.space(&xr.stage)
|
.space(&xr.stage)
|
||||||
.central_horizontal_angle(HORIZ_ANGLE)
|
.central_horizontal_angle(HORIZ_ANGLE)
|
||||||
@@ -226,7 +236,7 @@ impl Skybox {
|
|||||||
width: 10.0,
|
width: 10.0,
|
||||||
height: 10.0,
|
height: 10.0,
|
||||||
})
|
})
|
||||||
.sub_image(self.grid.as_ref().unwrap().get_subimage())
|
.sub_image(self.grid.as_ref().unwrap().get_subimage(0))
|
||||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||||
.space(&xr.stage);
|
.space(&xr.stage);
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ use openxr as xr;
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
|
image::{
|
||||||
|
sys::RawImage,
|
||||||
|
view::{ImageView, ImageViewCreateInfo},
|
||||||
|
ImageCreateInfo, ImageUsage,
|
||||||
|
},
|
||||||
Handle,
|
Handle,
|
||||||
image::{ImageCreateInfo, ImageUsage, sys::RawImage, view::ImageView},
|
|
||||||
};
|
};
|
||||||
use wgui::gfx::WGfx;
|
use wgui::gfx::WGfx;
|
||||||
|
|
||||||
@@ -47,7 +51,7 @@ pub(super) fn create_swapchain(
|
|||||||
width: extent[0],
|
width: extent[0],
|
||||||
height: extent[1],
|
height: extent[1],
|
||||||
face_count: 1,
|
face_count: 1,
|
||||||
array_size: 1,
|
array_size: extent[2],
|
||||||
mip_count: 1,
|
mip_count: 1,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -63,7 +67,8 @@ pub(super) fn create_swapchain(
|
|||||||
vk_image,
|
vk_image,
|
||||||
ImageCreateInfo {
|
ImageCreateInfo {
|
||||||
format: gfx.surface_format as _,
|
format: gfx.surface_format as _,
|
||||||
extent,
|
extent: [extent[0], extent[1], 1],
|
||||||
|
array_layers: extent[2],
|
||||||
usage: ImageUsage::COLOR_ATTACHMENT,
|
usage: ImageUsage::COLOR_ATTACHMENT,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -71,9 +76,15 @@ pub(super) fn create_swapchain(
|
|||||||
};
|
};
|
||||||
// SAFETY: OpenXR guarantees that the image is a swapchain image, thus has memory backing it.
|
// SAFETY: OpenXR guarantees that the image is a swapchain image, thus has memory backing it.
|
||||||
let image = Arc::new(unsafe { raw_image.assume_bound() });
|
let image = Arc::new(unsafe { raw_image.assume_bound() });
|
||||||
Ok(ImageView::new_default(image)?)
|
let mut wsi = WlxSwapchainImage::default();
|
||||||
|
for d in 0..extent[2] {
|
||||||
|
let mut create_info = ImageViewCreateInfo::from_image(&*image);
|
||||||
|
create_info.subresource_range.array_layers = d..d + 1;
|
||||||
|
wsi.views.push(ImageView::new(image.clone(), create_info)?);
|
||||||
|
}
|
||||||
|
Ok(wsi)
|
||||||
})
|
})
|
||||||
.collect::<anyhow::Result<SmallVec<[Arc<ImageView>; 4]>>>()?;
|
.collect::<anyhow::Result<SmallVec<[WlxSwapchainImage; 4]>>>()?;
|
||||||
|
|
||||||
Ok(WlxSwapchain {
|
Ok(WlxSwapchain {
|
||||||
acquired: false,
|
acquired: false,
|
||||||
@@ -84,16 +95,21 @@ pub(super) fn create_swapchain(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub(super) struct WlxSwapchainImage {
|
||||||
|
pub views: SmallVec<[Arc<ImageView>; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct WlxSwapchain {
|
pub(super) struct WlxSwapchain {
|
||||||
acquired: bool,
|
acquired: bool,
|
||||||
pub(super) ever_acquired: bool,
|
pub(super) ever_acquired: bool,
|
||||||
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
|
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
|
||||||
pub(super) extent: [u32; 3],
|
pub(super) extent: [u32; 3],
|
||||||
pub(super) images: SmallVec<[Arc<ImageView>; 4]>,
|
pub(super) images: SmallVec<[WlxSwapchainImage; 4]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlxSwapchain {
|
impl WlxSwapchain {
|
||||||
pub(super) fn acquire_wait_image(&mut self) -> anyhow::Result<Arc<ImageView>> {
|
pub(super) fn acquire_wait_image(&mut self) -> anyhow::Result<WlxSwapchainImage> {
|
||||||
let idx = self.swapchain.acquire_image()? as usize;
|
let idx = self.swapchain.acquire_image()? as usize;
|
||||||
self.swapchain.wait_image(xr::Duration::INFINITE)?;
|
self.swapchain.wait_image(xr::Duration::INFINITE)?;
|
||||||
self.ever_acquired = true;
|
self.ever_acquired = true;
|
||||||
@@ -109,7 +125,7 @@ impl WlxSwapchain {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_subimage(&self) -> xr::SwapchainSubImage<'_, xr::Vulkan> {
|
pub(super) fn get_subimage(&self, array_index: u32) -> xr::SwapchainSubImage<'_, xr::Vulkan> {
|
||||||
debug_assert!(self.ever_acquired, "swapchain was never acquired!");
|
debug_assert!(self.ever_acquired, "swapchain was never acquired!");
|
||||||
xr::SwapchainSubImage::new()
|
xr::SwapchainSubImage::new()
|
||||||
.swapchain(&self.swapchain)
|
.swapchain(&self.swapchain)
|
||||||
@@ -120,6 +136,6 @@ impl WlxSwapchain {
|
|||||||
height: self.extent[1] as _,
|
height: self.extent[1] as _,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.image_array_index(0)
|
.image_array_index(array_index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ use std::{
|
|||||||
sync::{Arc, OnceLock},
|
sync::{Arc, OnceLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use glam::{Vec2, vec2};
|
use glam::{vec2, Vec2};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
buffer::{BufferCreateInfo, BufferUsage},
|
buffer::{BufferCreateInfo, BufferUsage},
|
||||||
command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract},
|
command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract},
|
||||||
image::view::ImageView,
|
image::view::ImageView,
|
||||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||||
sync::GpuFuture,
|
sync::{now, GpuFuture},
|
||||||
};
|
};
|
||||||
use wgui::gfx::WGfx;
|
use wgui::gfx::WGfx;
|
||||||
|
|
||||||
@@ -26,11 +27,11 @@ use crate::shaders::{frag_color, frag_grid, frag_screen, frag_srgb, vert_quad};
|
|||||||
use {ash::vk, std::os::raw::c_void};
|
use {ash::vk, std::os::raw::c_void};
|
||||||
|
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
self, VulkanObject,
|
self,
|
||||||
buffer::{Buffer, BufferContents, IndexBuffer, Subbuffer},
|
buffer::{Buffer, BufferContents, IndexBuffer, Subbuffer},
|
||||||
device::{
|
device::{
|
||||||
DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags,
|
|
||||||
physical::{PhysicalDevice, PhysicalDeviceType},
|
physical::{PhysicalDevice, PhysicalDeviceType},
|
||||||
|
DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags,
|
||||||
},
|
},
|
||||||
format::Format,
|
format::Format,
|
||||||
instance::{Instance, InstanceCreateInfo, InstanceExtensions},
|
instance::{Instance, InstanceCreateInfo, InstanceExtensions},
|
||||||
@@ -39,6 +40,7 @@ use vulkano::{
|
|||||||
vertex_input::Vertex,
|
vertex_input::Vertex,
|
||||||
},
|
},
|
||||||
shader::ShaderModule,
|
shader::ShaderModule,
|
||||||
|
VulkanObject,
|
||||||
};
|
};
|
||||||
|
|
||||||
use dmabuf::get_drm_formats;
|
use dmabuf::get_drm_formats;
|
||||||
@@ -634,6 +636,11 @@ fn queue_families_priorities(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RenderResult {
|
||||||
|
pub queue: Arc<Queue>,
|
||||||
|
pub cmd_buf: Arc<PrimaryAutoCommandBuffer>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GpuFutures {
|
pub struct GpuFutures {
|
||||||
futures: Vec<Box<dyn GpuFuture>>,
|
futures: Vec<Box<dyn GpuFuture>>,
|
||||||
@@ -642,12 +649,27 @@ pub struct GpuFutures {
|
|||||||
impl GpuFutures {
|
impl GpuFutures {
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
cmd: (Arc<Queue>, Arc<PrimaryAutoCommandBuffer>),
|
queue: Arc<Queue>,
|
||||||
|
cmd_buf: Arc<PrimaryAutoCommandBuffer>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.futures.push(cmd.1.execute(cmd.0)?.boxed());
|
self.futures.push(cmd_buf.execute(queue)?.boxed());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn execute_results(&mut self, results: SmallVec<[RenderResult; 2]>) -> anyhow::Result<()> {
|
||||||
|
for (i, res) in results.into_iter().enumerate() {
|
||||||
|
if i == 0 {
|
||||||
|
let future = res.cmd_buf.execute(res.queue)?;
|
||||||
|
self.futures.push(Box::new(future));
|
||||||
|
} else {
|
||||||
|
let future = self.futures.pop().unwrap();
|
||||||
|
let future = future.then_execute(res.queue, res.cmd_buf)?;
|
||||||
|
self.futures.push(Box::new(future));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn wait(self) -> anyhow::Result<()> {
|
pub fn wait(self) -> anyhow::Result<()> {
|
||||||
let mut it = self.futures.into_iter();
|
let mut it = self.futures.into_iter();
|
||||||
let Some(mut all) = it.next() else {
|
let Some(mut all) = it.next() else {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use button::setup_custom_button;
|
use button::setup_custom_button;
|
||||||
use glam::{Affine2, Vec2, vec2};
|
use glam::{vec2, Affine2, Vec2};
|
||||||
use label::setup_custom_label;
|
use label::setup_custom_label;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
@@ -16,7 +16,7 @@ use wgui::{
|
|||||||
layout::{Layout, LayoutParams, WidgetID},
|
layout::{Layout, LayoutParams, WidgetID},
|
||||||
parser::{CustomAttribsInfoOwned, Fetchable, ParserState},
|
parser::{CustomAttribsInfoOwned, Fetchable, ParserState},
|
||||||
renderer_vk::context::Context as WguiContext,
|
renderer_vk::context::Context as WguiContext,
|
||||||
widget::{EventResult, label::WidgetLabel},
|
widget::{label::WidgetLabel, EventResult},
|
||||||
};
|
};
|
||||||
use wlx_common::timestep::Timestep;
|
use wlx_common::timestep::Timestep;
|
||||||
|
|
||||||
@@ -25,7 +25,8 @@ use crate::{
|
|||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
windowing::backend::{
|
windowing::backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, ui_transform,
|
ui_transform, BackendAttrib, BackendAttribValue, FrameMeta, OverlayBackend,
|
||||||
|
OverlayEventData, RenderResources, ShouldRender,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
|||||||
self.context.draw(
|
self.context.draw(
|
||||||
&globals.font_system,
|
&globals.font_system,
|
||||||
&mut app.wgui_shared,
|
&mut app.wgui_shared,
|
||||||
&mut rdr.cmd_buf,
|
&mut rdr.cmd_buf_single(),
|
||||||
&primitives,
|
&primitives,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -380,4 +381,10 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
||||||
self.interaction_transform
|
self.interaction_transform
|
||||||
}
|
}
|
||||||
|
fn get_attrib(&self, _attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, _app: &mut AppState, _value: BackendAttribValue) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::{
|
|||||||
|
|
||||||
use glam::vec2;
|
use glam::vec2;
|
||||||
use slotmap::Key;
|
use slotmap::Key;
|
||||||
|
use smallvec::smallvec;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
|
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
|
||||||
event::{CallbackDataCommon, EventAlterables, EventCallback},
|
event::{CallbackDataCommon, EventAlterables, EventCallback},
|
||||||
@@ -16,25 +17,35 @@ use wgui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
attrib_value,
|
||||||
backend::{
|
backend::{
|
||||||
input::HoverResult,
|
input::HoverResult,
|
||||||
task::{OverlayTask, TaskContainer, TaskType},
|
task::{OverlayTask, TaskContainer, TaskType},
|
||||||
},
|
},
|
||||||
gui::panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
|
gui::panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
|
||||||
overlays::edit::{
|
overlays::edit::{
|
||||||
lock::InteractLockHandler, pos::PositioningHandler, tab::ButtonPaneTabSwitcher,
|
lock::InteractLockHandler,
|
||||||
|
pos::{new_pos_tab_handler, PosTabState},
|
||||||
|
sprite_tab::SpriteTabHandler,
|
||||||
|
stereo::new_stereo_tab_handler,
|
||||||
|
tab::ButtonPaneTabSwitcher,
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlayID, OverlaySelector,
|
backend::{
|
||||||
backend::{DummyBackend, OverlayBackend, OverlayEventData, RenderResources, ShouldRender},
|
BackendAttrib, BackendAttribValue, DummyBackend, OverlayBackend, OverlayEventData,
|
||||||
|
RenderResources, ShouldRender, StereoMode,
|
||||||
|
},
|
||||||
window::OverlayWindowConfig,
|
window::OverlayWindowConfig,
|
||||||
|
OverlayID, OverlaySelector,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod lock;
|
mod lock;
|
||||||
mod pos;
|
mod pos;
|
||||||
|
mod sprite_tab;
|
||||||
|
mod stereo;
|
||||||
pub mod tab;
|
pub mod tab;
|
||||||
|
|
||||||
pub(super) struct LongPressButtonState {
|
pub(super) struct LongPressButtonState {
|
||||||
@@ -54,7 +65,8 @@ struct EditModeState {
|
|||||||
delete: LongPressButtonState,
|
delete: LongPressButtonState,
|
||||||
tabs: ButtonPaneTabSwitcher,
|
tabs: ButtonPaneTabSwitcher,
|
||||||
lock: InteractLockHandler,
|
lock: InteractLockHandler,
|
||||||
pos: PositioningHandler,
|
pos: SpriteTabHandler<PosTabState>,
|
||||||
|
stereo: SpriteTabHandler<StereoMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type EditModeWrapPanel = GuiPanel<EditModeState>;
|
type EditModeWrapPanel = GuiPanel<EditModeState>;
|
||||||
@@ -77,7 +89,6 @@ impl EditWrapperManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("EditMode wrap on {}", owc.name);
|
log::debug!("EditMode wrap on {}", owc.name);
|
||||||
let inner = mem::replace(&mut owc.backend, Box::new(DummyBackend {}));
|
|
||||||
let mut panel = self.panel_pool.pop();
|
let mut panel = self.panel_pool.pop();
|
||||||
if panel.is_none() {
|
if panel.is_none() {
|
||||||
panel = Some(make_edit_panel(app)?);
|
panel = Some(make_edit_panel(app)?);
|
||||||
@@ -85,6 +96,8 @@ impl EditWrapperManager {
|
|||||||
let mut panel = panel.unwrap();
|
let mut panel = panel.unwrap();
|
||||||
reset_panel(&mut panel, id, owc)?;
|
reset_panel(&mut panel, id, owc)?;
|
||||||
|
|
||||||
|
let inner = mem::replace(&mut owc.backend, Box::new(DummyBackend {}));
|
||||||
|
|
||||||
owc.backend = Box::new(EditModeBackendWrapper {
|
owc.backend = Box::new(EditModeBackendWrapper {
|
||||||
inner: ManuallyDrop::new(inner),
|
inner: ManuallyDrop::new(inner),
|
||||||
panel: ManuallyDrop::new(panel),
|
panel: ManuallyDrop::new(panel),
|
||||||
@@ -178,7 +191,16 @@ impl OverlayBackend for EditModeBackendWrapper {
|
|||||||
rdr: &mut RenderResources,
|
rdr: &mut RenderResources,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.inner.render(app, rdr)?;
|
self.inner.render(app, rdr)?;
|
||||||
self.panel.render(app, rdr)
|
|
||||||
|
self.panel.render(app, rdr)?;
|
||||||
|
// `GuiPanel` is not stereo-aware, so just render the same pass twice
|
||||||
|
if rdr.cmd_bufs.len() > 1 {
|
||||||
|
rdr.cmd_bufs.reverse();
|
||||||
|
self.panel.render(app, rdr)?;
|
||||||
|
rdr.cmd_bufs.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn frame_meta(&mut self) -> Option<crate::windowing::backend::FrameMeta> {
|
fn frame_meta(&mut self) -> Option<crate::windowing::backend::FrameMeta> {
|
||||||
self.inner.frame_meta()
|
self.inner.frame_meta()
|
||||||
@@ -218,6 +240,12 @@ impl OverlayBackend for EditModeBackendWrapper {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
||||||
self.inner.get_interaction_transform()
|
self.inner.get_interaction_transform()
|
||||||
}
|
}
|
||||||
|
fn get_attrib(&self, attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
self.inner.get_attrib(attrib)
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool {
|
||||||
|
self.inner.set_attrib(app, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||||
@@ -229,7 +257,8 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
|||||||
},
|
},
|
||||||
tabs: ButtonPaneTabSwitcher::default(),
|
tabs: ButtonPaneTabSwitcher::default(),
|
||||||
lock: InteractLockHandler::default(),
|
lock: InteractLockHandler::default(),
|
||||||
pos: PositioningHandler::default(),
|
pos: SpriteTabHandler::default(),
|
||||||
|
stereo: SpriteTabHandler::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
|
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
|
||||||
@@ -270,10 +299,20 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
"::EditModeSetPos" => {
|
"::EditModeSetPos" => {
|
||||||
let pos_key = args.next().unwrap().to_owned();
|
let key = args.next().unwrap().to_owned();
|
||||||
Box::new(move |common, _data, app, state| {
|
Box::new(move |common, _data, app, state| {
|
||||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||||
let task = state.pos.pos_button_clicked(common, &pos_key);
|
let task = state.pos.button_clicked(common, &key);
|
||||||
|
app.tasks
|
||||||
|
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
|
||||||
|
Ok(EventResult::Consumed)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"::EditModeSetStereo" => {
|
||||||
|
let key = args.next().unwrap().to_owned();
|
||||||
|
Box::new(move |common, _data, app, state| {
|
||||||
|
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||||
|
let task = state.stereo.button_clicked(common, &key);
|
||||||
app.tasks
|
app.tasks
|
||||||
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
|
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
|
||||||
Ok(EventResult::Consumed)
|
Ok(EventResult::Consumed)
|
||||||
@@ -315,9 +354,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
panel.state.pos = PositioningHandler::new(&mut panel)?;
|
panel.state.pos = new_pos_tab_handler(&mut panel)?;
|
||||||
|
panel.state.stereo = new_stereo_tab_handler(&mut panel)?;
|
||||||
panel.state.lock = InteractLockHandler::new(&mut panel)?;
|
panel.state.lock = InteractLockHandler::new(&mut panel)?;
|
||||||
panel.state.tabs = ButtonPaneTabSwitcher::new(&mut panel, &["none", "pos", "alpha", "curve"])?;
|
panel.state.tabs =
|
||||||
|
ButtonPaneTabSwitcher::new(&mut panel, &["none", "pos", "alpha", "curve", "stereo"])?;
|
||||||
|
|
||||||
set_up_checkbox(&mut panel, "additive_box", cb_assign_additive)?;
|
set_up_checkbox(&mut panel, "additive_box", cb_assign_additive)?;
|
||||||
set_up_slider(&mut panel, "lerp_slider", cb_assign_lerp)?;
|
set_up_slider(&mut panel, "lerp_slider", cb_assign_lerp)?;
|
||||||
@@ -366,10 +407,29 @@ fn reset_panel(
|
|||||||
.fetch_component_as::<ComponentCheckbox>("additive_box")?;
|
.fetch_component_as::<ComponentCheckbox>("additive_box")?;
|
||||||
c.set_checked(&mut common, state.additive);
|
c.set_checked(&mut common, state.additive);
|
||||||
|
|
||||||
panel.state.pos.reset(&mut common, state.positioning);
|
panel
|
||||||
|
.state
|
||||||
|
.pos
|
||||||
|
.reset(&mut common, &state.positioning.into());
|
||||||
panel.state.lock.reset(&mut common, state.interactable);
|
panel.state.lock.reset(&mut common, state.interactable);
|
||||||
panel.state.tabs.reset(&mut common);
|
panel.state.tabs.reset(&mut common);
|
||||||
|
|
||||||
|
if let Some(stereo) = attrib_value!(
|
||||||
|
owc.backend.get_attrib(BackendAttrib::Stereo),
|
||||||
|
BackendAttribValue::Stereo
|
||||||
|
) {
|
||||||
|
panel
|
||||||
|
.state
|
||||||
|
.tabs
|
||||||
|
.set_tab_visible(&mut common, "stereo", true);
|
||||||
|
panel.state.stereo.reset(&mut common, &stereo);
|
||||||
|
} else {
|
||||||
|
panel
|
||||||
|
.state
|
||||||
|
.tabs
|
||||||
|
.set_tab_visible(&mut common, "stereo", false);
|
||||||
|
}
|
||||||
|
|
||||||
panel.layout.process_alterables(alterables)?;
|
panel.layout.process_alterables(alterables)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,174 +1,116 @@
|
|||||||
use std::{collections::HashMap, rc::Rc};
|
use wgui::{event::StyleSetRequest, parser::Fetchable, taffy};
|
||||||
|
|
||||||
use wgui::{
|
|
||||||
components::button::ComponentButton,
|
|
||||||
event::{CallbackDataCommon, StyleSetRequest},
|
|
||||||
layout::WidgetID,
|
|
||||||
parser::Fetchable,
|
|
||||||
renderer_vk::text::custom_glyph::CustomGlyphData,
|
|
||||||
taffy,
|
|
||||||
widget::sprite::WidgetSprite,
|
|
||||||
};
|
|
||||||
use wlx_common::{common::LeftRight, windowing::Positioning};
|
use wlx_common::{common::LeftRight, windowing::Positioning};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::task::ModifyOverlayTask, overlays::edit::EditModeWrapPanel, windowing::window,
|
overlays::edit::{
|
||||||
|
sprite_tab::{SpriteTabHandler, SpriteTabKey},
|
||||||
|
EditModeWrapPanel,
|
||||||
|
},
|
||||||
|
windowing::window,
|
||||||
};
|
};
|
||||||
|
|
||||||
static POS_NAMES: [&str; 6] = ["static", "anchored", "floating", "hmd", "hand_l", "hand_r"];
|
static POS_NAMES: [&str; 6] = ["static", "anchored", "floating", "hmd", "hand_l", "hand_r"];
|
||||||
|
|
||||||
struct PosButtonState {
|
|
||||||
name: &'static str,
|
|
||||||
sprite: CustomGlyphData,
|
|
||||||
component: Rc<ComponentButton>,
|
|
||||||
positioning: Positioning,
|
|
||||||
has_interpolation: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(super) struct PositioningHandler {
|
pub struct PosTabState {
|
||||||
top_sprite_id: WidgetID,
|
pos: Positioning,
|
||||||
interpolation_id: WidgetID,
|
has_lerp: bool,
|
||||||
buttons: HashMap<&'static str, Rc<PosButtonState>>,
|
|
||||||
active_button: Option<Rc<PosButtonState>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PositioningHandler {
|
impl From<Positioning> for PosTabState {
|
||||||
pub fn new(panel: &mut EditModeWrapPanel) -> anyhow::Result<Self> {
|
fn from(value: Positioning) -> Self {
|
||||||
let mut buttons = HashMap::new();
|
Self {
|
||||||
|
pos: value,
|
||||||
|
has_lerp: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for name in &POS_NAMES {
|
pub fn new_pos_tab_handler(
|
||||||
let button_id = format!("pos_{name}");
|
panel: &mut EditModeWrapPanel,
|
||||||
let component = panel.parser_state.fetch_component_as(&button_id)?;
|
) -> anyhow::Result<SpriteTabHandler<PosTabState>> {
|
||||||
|
let interpolation_id = panel.parser_state.get_widget_id("pos_interpolation")?;
|
||||||
|
|
||||||
let sprite_id = format!("{button_id}_sprite");
|
SpriteTabHandler::new(
|
||||||
let id = panel.parser_state.get_widget_id(&sprite_id)?;
|
panel,
|
||||||
let sprite_w = panel
|
"pos",
|
||||||
.layout
|
&POS_NAMES,
|
||||||
.state
|
Box::new(|_common, state| {
|
||||||
.widgets
|
let positioning = state.pos;
|
||||||
.get_as::<WidgetSprite>(id)
|
Box::new(move |app, owc| {
|
||||||
.ok_or_else(|| {
|
let state = owc.active_state.as_mut().unwrap(); //want panic
|
||||||
anyhow::anyhow!("Element with id=\"{sprite_id}\" must be a <sprite>")
|
state.positioning = positioning;
|
||||||
})?;
|
window::save_transform(state, app);
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
Some(Box::new(move |common, state| {
|
||||||
|
let interpolation_disp = if state.has_lerp {
|
||||||
|
taffy::Display::Flex
|
||||||
|
} else {
|
||||||
|
taffy::Display::None
|
||||||
|
};
|
||||||
|
|
||||||
let sprite = sprite_w.params.glyph_data.clone().ok_or_else(|| {
|
common.alterables.set_style(
|
||||||
anyhow::anyhow!("Element with id=\"{sprite_id}\" must have a valid src!")
|
interpolation_id,
|
||||||
})?;
|
StyleSetRequest::Display(interpolation_disp),
|
||||||
|
|
||||||
let (positioning, has_interpolation) = key_to_pos(name);
|
|
||||||
|
|
||||||
buttons.insert(
|
|
||||||
*name,
|
|
||||||
Rc::new(PosButtonState {
|
|
||||||
name,
|
|
||||||
sprite,
|
|
||||||
component,
|
|
||||||
positioning,
|
|
||||||
has_interpolation,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
})),
|
||||||
|
)
|
||||||
let top_sprite_id = panel.parser_state.get_widget_id("top_pos_sprite")?;
|
|
||||||
let interpolation_id = panel.parser_state.get_widget_id("pos_interpolation")?;
|
|
||||||
Ok(Self {
|
|
||||||
buttons,
|
|
||||||
active_button: None,
|
|
||||||
top_sprite_id,
|
|
||||||
interpolation_id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn change_highlight(&mut self, common: &mut CallbackDataCommon, key: &str) {
|
|
||||||
if let Some(old) = self.active_button.take() {
|
|
||||||
old.component.set_sticky_state(common, false);
|
|
||||||
}
|
|
||||||
let new = self.buttons.get_mut(key).unwrap();
|
|
||||||
new.component.set_sticky_state(common, true);
|
|
||||||
self.active_button = Some(new.clone());
|
|
||||||
|
|
||||||
let interpolation_disp = if new.has_interpolation {
|
|
||||||
taffy::Display::Flex
|
|
||||||
} else {
|
|
||||||
taffy::Display::None
|
|
||||||
};
|
|
||||||
|
|
||||||
common.alterables.set_style(
|
|
||||||
self.interpolation_id,
|
|
||||||
StyleSetRequest::Display(interpolation_disp),
|
|
||||||
);
|
|
||||||
|
|
||||||
// change top sprite
|
|
||||||
if let Some(mut sprite) = common
|
|
||||||
.state
|
|
||||||
.widgets
|
|
||||||
.get_as::<WidgetSprite>(self.top_sprite_id)
|
|
||||||
{
|
|
||||||
sprite.params.glyph_data = Some(new.sprite.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pos_button_clicked(
|
|
||||||
&mut self,
|
|
||||||
common: &mut CallbackDataCommon,
|
|
||||||
key: &str,
|
|
||||||
) -> Box<ModifyOverlayTask> {
|
|
||||||
self.change_highlight(common, key);
|
|
||||||
|
|
||||||
let (pos, _) = key_to_pos(key);
|
|
||||||
Box::new(move |app, owc| {
|
|
||||||
let state = owc.active_state.as_mut().unwrap(); //want panic
|
|
||||||
state.positioning = pos;
|
|
||||||
window::save_transform(state, app);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self, common: &mut CallbackDataCommon, pos: Positioning) {
|
|
||||||
let key = pos_to_key(pos);
|
|
||||||
self.change_highlight(common, key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_to_pos(key: &str) -> (Positioning, bool) {
|
impl SpriteTabKey for PosTabState {
|
||||||
match key {
|
fn to_tab_key(&self) -> &'static str {
|
||||||
"static" => (Positioning::Static, false),
|
match self.pos {
|
||||||
"anchored" => (Positioning::Anchored, false),
|
Positioning::Static => "static",
|
||||||
"floating" => (Positioning::Floating, false),
|
Positioning::Anchored => "anchored",
|
||||||
"hmd" => (Positioning::FollowHead { lerp: 1.0 }, true),
|
Positioning::Floating => "floating",
|
||||||
"hand_l" => (
|
Positioning::FollowHead { .. } => "hmd",
|
||||||
Positioning::FollowHand {
|
Positioning::FollowHand {
|
||||||
hand: LeftRight::Left,
|
hand: LeftRight::Left,
|
||||||
lerp: 1.0,
|
..
|
||||||
},
|
} => "hand_l",
|
||||||
true,
|
|
||||||
),
|
|
||||||
"hand_r" => (
|
|
||||||
Positioning::FollowHand {
|
Positioning::FollowHand {
|
||||||
hand: LeftRight::Right,
|
hand: LeftRight::Right,
|
||||||
lerp: 1.0,
|
..
|
||||||
|
} => "hand_r",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_tab_key(key: &str) -> Self {
|
||||||
|
match key {
|
||||||
|
"static" => PosTabState {
|
||||||
|
pos: Positioning::Static,
|
||||||
|
has_lerp: false,
|
||||||
},
|
},
|
||||||
true,
|
"anchored" => PosTabState {
|
||||||
),
|
pos: Positioning::Anchored,
|
||||||
_ => {
|
has_lerp: false,
|
||||||
panic!("cannot translate to positioning: {key}")
|
},
|
||||||
|
"floating" => PosTabState {
|
||||||
|
pos: Positioning::Floating,
|
||||||
|
has_lerp: false,
|
||||||
|
},
|
||||||
|
"hmd" => PosTabState {
|
||||||
|
pos: Positioning::FollowHead { lerp: 1.0 },
|
||||||
|
has_lerp: true,
|
||||||
|
},
|
||||||
|
"hand_l" => PosTabState {
|
||||||
|
pos: Positioning::FollowHand {
|
||||||
|
hand: LeftRight::Left,
|
||||||
|
lerp: 1.0,
|
||||||
|
},
|
||||||
|
has_lerp: true,
|
||||||
|
},
|
||||||
|
"hand_r" => PosTabState {
|
||||||
|
pos: Positioning::FollowHand {
|
||||||
|
hand: LeftRight::Right,
|
||||||
|
lerp: 1.0,
|
||||||
|
},
|
||||||
|
has_lerp: true,
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("cannot translate to positioning: {key}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn pos_to_key(pos: Positioning) -> &'static str {
|
|
||||||
match pos {
|
|
||||||
Positioning::Static => "static",
|
|
||||||
Positioning::Anchored => "anchored",
|
|
||||||
Positioning::Floating => "floating",
|
|
||||||
Positioning::FollowHead { .. } => "hmd",
|
|
||||||
Positioning::FollowHand {
|
|
||||||
hand: LeftRight::Left,
|
|
||||||
..
|
|
||||||
} => "hand_l",
|
|
||||||
Positioning::FollowHand {
|
|
||||||
hand: LeftRight::Right,
|
|
||||||
..
|
|
||||||
} => "hand_r",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
129
wlx-overlay-s/src/overlays/edit/sprite_tab.rs
Normal file
129
wlx-overlay-s/src/overlays/edit/sprite_tab.rs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
use wgui::{
|
||||||
|
components::button::ComponentButton, event::CallbackDataCommon, layout::WidgetID,
|
||||||
|
parser::Fetchable, renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
|
widget::sprite::WidgetSprite,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{backend::task::ModifyOverlayTask, overlays::edit::EditModeWrapPanel};
|
||||||
|
|
||||||
|
pub trait SpriteTabKey {
|
||||||
|
fn to_tab_key(&self) -> &'static str;
|
||||||
|
fn from_tab_key(key: &str) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SpriteTabButtonState<S> {
|
||||||
|
name: &'static str,
|
||||||
|
sprite: CustomGlyphData,
|
||||||
|
component: Rc<ComponentButton>,
|
||||||
|
state: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SpriteTabHighlightChanged<S> = dyn Fn(&mut CallbackDataCommon, &S);
|
||||||
|
pub type SpriteTabButtonClicked<S> = dyn Fn(&mut CallbackDataCommon, &S) -> Box<ModifyOverlayTask>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct SpriteTabHandler<S> {
|
||||||
|
top_sprite_id: WidgetID,
|
||||||
|
buttons: HashMap<&'static str, Rc<SpriteTabButtonState<S>>>,
|
||||||
|
active_button: Option<Rc<SpriteTabButtonState<S>>>,
|
||||||
|
on_highlight_changed: Option<Box<SpriteTabHighlightChanged<S>>>,
|
||||||
|
on_button_clicked: Option<Box<SpriteTabButtonClicked<S>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> SpriteTabHandler<S>
|
||||||
|
where
|
||||||
|
S: SpriteTabKey,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
panel: &mut EditModeWrapPanel,
|
||||||
|
prefix: &str,
|
||||||
|
names: &[&'static str],
|
||||||
|
on_button_clicked: Box<SpriteTabButtonClicked<S>>,
|
||||||
|
on_highlight_changed: Option<Box<SpriteTabHighlightChanged<S>>>,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
|
let mut buttons = HashMap::new();
|
||||||
|
|
||||||
|
for name in names {
|
||||||
|
let button_id = format!("{prefix}_{name}");
|
||||||
|
let component = panel.parser_state.fetch_component_as(&button_id)?;
|
||||||
|
|
||||||
|
let sprite_id = format!("{button_id}_sprite");
|
||||||
|
let id = panel.parser_state.get_widget_id(&sprite_id)?;
|
||||||
|
let sprite_w = panel
|
||||||
|
.layout
|
||||||
|
.state
|
||||||
|
.widgets
|
||||||
|
.get_as::<WidgetSprite>(id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("Element with id=\"{sprite_id}\" must be a <sprite>")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let sprite = sprite_w.params.glyph_data.clone().ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("Element with id=\"{sprite_id}\" must have a valid src!")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let state = S::from_tab_key(name);
|
||||||
|
|
||||||
|
buttons.insert(
|
||||||
|
*name,
|
||||||
|
Rc::new(SpriteTabButtonState {
|
||||||
|
name,
|
||||||
|
sprite,
|
||||||
|
component,
|
||||||
|
state,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let top_sprite_id = panel
|
||||||
|
.parser_state
|
||||||
|
.get_widget_id(&format!("top_{prefix}_sprite"))?;
|
||||||
|
Ok(Self {
|
||||||
|
buttons,
|
||||||
|
active_button: None,
|
||||||
|
top_sprite_id,
|
||||||
|
on_highlight_changed,
|
||||||
|
on_button_clicked: Some(on_button_clicked),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_highlight(&mut self, common: &mut CallbackDataCommon, key: &str) {
|
||||||
|
if let Some(old) = self.active_button.take() {
|
||||||
|
old.component.set_sticky_state(common, false);
|
||||||
|
}
|
||||||
|
let new = self.buttons.get_mut(key).unwrap();
|
||||||
|
new.component.set_sticky_state(common, true);
|
||||||
|
self.active_button = Some(new.clone());
|
||||||
|
|
||||||
|
if let Some(highlight_changed) = self.on_highlight_changed.as_ref() {
|
||||||
|
highlight_changed(common, &new.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// change top sprite
|
||||||
|
if let Some(mut sprite) = common
|
||||||
|
.state
|
||||||
|
.widgets
|
||||||
|
.get_as::<WidgetSprite>(self.top_sprite_id)
|
||||||
|
{
|
||||||
|
sprite.params.glyph_data = Some(new.sprite.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button_clicked(
|
||||||
|
&mut self,
|
||||||
|
common: &mut CallbackDataCommon,
|
||||||
|
key: &str,
|
||||||
|
) -> Box<ModifyOverlayTask> {
|
||||||
|
self.change_highlight(common, key);
|
||||||
|
|
||||||
|
let state = S::from_tab_key(key);
|
||||||
|
self.on_button_clicked.as_ref().unwrap()(common, &state)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self, common: &mut CallbackDataCommon, state: &S) {
|
||||||
|
let key = state.to_tab_key();
|
||||||
|
self.change_highlight(common, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
wlx-overlay-s/src/overlays/edit/stereo.rs
Normal file
52
wlx-overlay-s/src/overlays/edit/stereo.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use crate::{
|
||||||
|
overlays::edit::{
|
||||||
|
sprite_tab::{SpriteTabHandler, SpriteTabKey},
|
||||||
|
EditModeWrapPanel,
|
||||||
|
},
|
||||||
|
windowing::backend::{BackendAttribValue, StereoMode},
|
||||||
|
};
|
||||||
|
|
||||||
|
static STEREO_NAMES: [&str; 5] = ["none", "leftright", "rightleft", "topbottom", "bottomtop"];
|
||||||
|
|
||||||
|
pub fn new_stereo_tab_handler(
|
||||||
|
panel: &mut EditModeWrapPanel,
|
||||||
|
) -> anyhow::Result<SpriteTabHandler<StereoMode>> {
|
||||||
|
SpriteTabHandler::new(
|
||||||
|
panel,
|
||||||
|
"stereo",
|
||||||
|
&STEREO_NAMES,
|
||||||
|
Box::new(|_common, state| {
|
||||||
|
let stereo = state.clone();
|
||||||
|
Box::new(move |app, owc| {
|
||||||
|
owc.backend
|
||||||
|
.set_attrib(app, BackendAttribValue::Stereo(stereo));
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteTabKey for StereoMode {
|
||||||
|
fn to_tab_key(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
StereoMode::None => "none",
|
||||||
|
StereoMode::LeftRight => "leftright",
|
||||||
|
StereoMode::RightLeft => "rightleft",
|
||||||
|
StereoMode::TopBottom => "topbottom",
|
||||||
|
StereoMode::BottomTop => "bottomtop",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_tab_key(key: &str) -> Self {
|
||||||
|
match key {
|
||||||
|
"none" => StereoMode::None,
|
||||||
|
"leftright" => StereoMode::LeftRight,
|
||||||
|
"rightleft" => StereoMode::RightLeft,
|
||||||
|
"topbottom" => StereoMode::TopBottom,
|
||||||
|
"bottomtop" => StereoMode::BottomTop,
|
||||||
|
_ => {
|
||||||
|
panic!("cannot translate to positioning: {key}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,22 @@ impl ButtonPaneTabSwitcher {
|
|||||||
self.active_tab = Some(data);
|
self.active_tab = Some(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_tab_visible(&mut self, common: &mut CallbackDataCommon, tab: &str, visible: bool) {
|
||||||
|
let Some(data) = self.tabs[tab].button.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let display = if visible {
|
||||||
|
taffy::Display::Flex
|
||||||
|
} else {
|
||||||
|
taffy::Display::None
|
||||||
|
};
|
||||||
|
|
||||||
|
common
|
||||||
|
.alterables
|
||||||
|
.set_style(data.get_rect(), StyleSetRequest::Display(display));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, common: &mut CallbackDataCommon) {
|
pub fn reset(&mut self, common: &mut CallbackDataCommon) {
|
||||||
if let Some(data) = self.active_tab.take() {
|
if let Some(data) = self.active_tab.take() {
|
||||||
set_tab_active(common, &data, false);
|
set_tab_active(common, &data, false);
|
||||||
|
|||||||
@@ -6,24 +6,27 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
KEYMAP_CHANGE,
|
|
||||||
backend::input::{HoverResult, PointerHit},
|
backend::input::{HoverResult, PointerHit},
|
||||||
gui::panel::GuiPanel,
|
gui::panel::GuiPanel,
|
||||||
overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier},
|
overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::{
|
subsystem::hid::{
|
||||||
ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey, WheelDelta, XkbKeymap,
|
get_keymap_wl, get_keymap_x11, KeyModifier, VirtualKey, WheelDelta, XkbKeymap, ALT, CTRL,
|
||||||
get_keymap_wl, get_keymap_x11,
|
META, SHIFT, SUPER,
|
||||||
},
|
},
|
||||||
windowing::{
|
windowing::{
|
||||||
backend::{FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender},
|
backend::{
|
||||||
|
BackendAttrib, BackendAttribValue, FrameMeta, OverlayBackend, OverlayEventData,
|
||||||
|
RenderResources, ShouldRender,
|
||||||
|
},
|
||||||
window::OverlayWindowConfig,
|
window::OverlayWindowConfig,
|
||||||
},
|
},
|
||||||
|
KEYMAP_CHANGE,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
use glam::{vec3, Affine3A, Quat, Vec3};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use slotmap::{SlotMap, new_key_type};
|
use slotmap::{new_key_type, SlotMap};
|
||||||
use wgui::{
|
use wgui::{
|
||||||
drawing,
|
drawing,
|
||||||
event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex},
|
event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex},
|
||||||
@@ -279,6 +282,12 @@ impl OverlayBackend for KeyboardBackend {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
||||||
self.panel().get_interaction_transform()
|
self.panel().get_interaction_transform()
|
||||||
}
|
}
|
||||||
|
fn get_attrib(&self, _attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, _app: &mut AppState, _value: BackendAttribValue) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyboardState {
|
struct KeyboardState {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::{
|
use std::{
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
},
|
},
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::{Future, FutureExt};
|
use futures::{Future, FutureExt};
|
||||||
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
|
use glam::{vec3, Affine2, Affine3A, Quat, Vec3};
|
||||||
use wlx_capture::pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen};
|
use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult};
|
||||||
use wlx_common::windowing::OverlayWindowState;
|
use wlx_common::windowing::OverlayWindowState;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -19,12 +19,12 @@ use crate::{
|
|||||||
state::{AppSession, AppState},
|
state::{AppSession, AppState},
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlaySelector,
|
|
||||||
backend::{
|
backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
ui_transform, BackendAttrib, BackendAttribValue, FrameMeta, OverlayBackend,
|
||||||
ui_transform,
|
OverlayEventData, RenderResources, ShouldRender,
|
||||||
},
|
},
|
||||||
window::{OverlayCategory, OverlayWindowConfig},
|
window::{OverlayCategory, OverlayWindowConfig},
|
||||||
|
OverlaySelector,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -152,6 +152,20 @@ impl OverlayBackend for MirrorBackend {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
||||||
self.interaction_transform
|
self.interaction_transform
|
||||||
}
|
}
|
||||||
|
fn get_attrib(&self, attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
if let Some(renderer) = self.renderer.as_ref() {
|
||||||
|
renderer.get_attrib(attrib)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool {
|
||||||
|
if let Some(renderer) = self.renderer.as_mut() {
|
||||||
|
renderer.set_attrib(app, value)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mirror_name() -> Arc<str> {
|
pub fn new_mirror_name() -> Arc<str> {
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, LazyLock, atomic::AtomicU64},
|
sync::{atomic::AtomicU64, Arc, LazyLock},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use glam::{Affine2, Vec2, vec2};
|
use glam::{vec2, Affine2, Vec2};
|
||||||
use wlx_capture::{WlxCapture, frame::Transform};
|
use wlx_capture::{frame::Transform, WlxCapture};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::input::{HoverResult, PointerHit, PointerMode},
|
backend::{
|
||||||
|
input::{HoverResult, PointerHit, PointerMode},
|
||||||
|
XrBackend,
|
||||||
|
},
|
||||||
graphics::ExtentExt,
|
graphics::ExtentExt,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, WheelDelta},
|
subsystem::hid::{WheelDelta, MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT},
|
||||||
windowing::backend::{
|
windowing::backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
BackendAttrib, BackendAttribValue, FrameMeta, OverlayBackend, OverlayEventData,
|
||||||
|
RenderResources, ShouldRender, StereoMode,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::capture::{ScreenPipeline, WlxCaptureIn, WlxCaptureOut, receive_callback};
|
use super::capture::{receive_callback, ScreenPipeline, WlxCaptureIn, WlxCaptureOut};
|
||||||
|
|
||||||
const CURSOR_SIZE: f32 = 16. / 1440.;
|
const CURSOR_SIZE: f32 = 16. / 1440.;
|
||||||
|
|
||||||
@@ -42,6 +46,7 @@ pub struct ScreenBackend {
|
|||||||
meta: Option<FrameMeta>,
|
meta: Option<FrameMeta>,
|
||||||
mouse_transform: Affine2,
|
mouse_transform: Affine2,
|
||||||
interaction_transform: Option<Affine2>,
|
interaction_transform: Option<Affine2>,
|
||||||
|
stereo: Option<StereoMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenBackend {
|
impl ScreenBackend {
|
||||||
@@ -57,6 +62,7 @@ impl ScreenBackend {
|
|||||||
meta: None,
|
meta: None,
|
||||||
mouse_transform: Affine2::ZERO,
|
mouse_transform: Affine2::ZERO,
|
||||||
interaction_transform: None,
|
interaction_transform: None,
|
||||||
|
stereo: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +109,12 @@ impl ScreenBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OverlayBackend for ScreenBackend {
|
impl OverlayBackend for ScreenBackend {
|
||||||
fn init(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||||
|
self.stereo = if matches!(app.xr_backend, XrBackend::OpenXR) {
|
||||||
|
Some(StereoMode::None)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
|
||||||
@@ -155,10 +166,14 @@ impl OverlayBackend for ScreenBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(frame) = self.capture.receive() {
|
if let Some(frame) = self.capture.receive() {
|
||||||
let meta = frame.get_frame_meta(&app.session.config);
|
let mut meta = frame.get_frame_meta(&app.session.config);
|
||||||
|
|
||||||
if let Some(pipeline) = self.pipeline.as_mut() {
|
if let Some(pipeline) = self.pipeline.as_mut() {
|
||||||
if self.meta.is_some_and(|old| old.extent != meta.extent) {
|
meta.extent[2] = pipeline.get_depth();
|
||||||
|
if self
|
||||||
|
.meta
|
||||||
|
.is_some_and(|old| old.extent[..2] != meta.extent[..2])
|
||||||
|
{
|
||||||
pipeline.set_extent(app, [meta.extent[0] as _, meta.extent[1] as _])?;
|
pipeline.set_extent(app, [meta.extent[0] as _, meta.extent[1] as _])?;
|
||||||
self.set_interaction_transform(
|
self.set_interaction_transform(
|
||||||
meta.extent.extent_vec2(),
|
meta.extent.extent_vec2(),
|
||||||
@@ -166,7 +181,10 @@ impl OverlayBackend for ScreenBackend {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.pipeline = Some(ScreenPipeline::new(&meta, app)?);
|
let pipeline =
|
||||||
|
ScreenPipeline::new(&meta, app, self.stereo.unwrap_or(StereoMode::None))?;
|
||||||
|
meta.extent[2] = pipeline.get_depth();
|
||||||
|
self.pipeline = Some(pipeline);
|
||||||
self.set_interaction_transform(meta.extent.extent_vec2(), frame.get_transform());
|
self.set_interaction_transform(meta.extent.extent_vec2(), frame.get_transform());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,4 +277,29 @@ impl OverlayBackend for ScreenBackend {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
||||||
self.interaction_transform
|
self.interaction_transform
|
||||||
}
|
}
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
fn get_attrib(&self, attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
match attrib {
|
||||||
|
BackendAttrib::Stereo => self.stereo.map(|s| BackendAttribValue::Stereo(s)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool {
|
||||||
|
match value {
|
||||||
|
BackendAttribValue::Stereo(new) => {
|
||||||
|
if let Some(stereo) = self.stereo.as_mut() {
|
||||||
|
log::debug!("{}: stereo: {stereo:?} → {new:?}", self.name);
|
||||||
|
*stereo = new;
|
||||||
|
if let Some(pipeline) = self.pipeline.as_mut() {
|
||||||
|
pipeline.set_stereo(app, new).unwrap(); // only panics if gfx is dead
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,58 @@
|
|||||||
use std::{f32::consts::PI, sync::Arc};
|
use std::{f32::consts::PI, sync::Arc};
|
||||||
|
|
||||||
use glam::{Affine3A, Vec3};
|
use glam::{Affine3A, Vec3};
|
||||||
use smallvec::smallvec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
buffer::{BufferUsage, Subbuffer},
|
buffer::{BufferUsage, Subbuffer},
|
||||||
command_buffer::CommandBufferUsage,
|
command_buffer::CommandBufferUsage,
|
||||||
device::Queue,
|
device::Queue,
|
||||||
format::Format,
|
format::Format,
|
||||||
image::{Image, sampler::Filter, view::ImageView},
|
image::{sampler::Filter, view::ImageView, Image},
|
||||||
pipeline::graphics::color_blend::AttachmentBlend,
|
pipeline::graphics::color_blend::AttachmentBlend,
|
||||||
};
|
};
|
||||||
use wgui::gfx::{
|
use wgui::gfx::{
|
||||||
WGfx,
|
|
||||||
cmd::WGfxClearMode,
|
cmd::WGfxClearMode,
|
||||||
pass::WGfxPass,
|
pass::WGfxPass,
|
||||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||||
|
WGfx,
|
||||||
};
|
};
|
||||||
use wlx_capture::{
|
use wlx_capture::{
|
||||||
WlxCapture,
|
|
||||||
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
|
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
|
||||||
|
WlxCapture,
|
||||||
};
|
};
|
||||||
use wlx_common::config::GeneralConfig;
|
use wlx_common::config::GeneralConfig;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics::{
|
graphics::{
|
||||||
Vert2Uv,
|
dmabuf::{fourcc_to_vk, WGfxDmabuf},
|
||||||
dmabuf::{WGfxDmabuf, fourcc_to_vk},
|
upload_quad_vertices, Vert2Uv,
|
||||||
upload_quad_vertices,
|
|
||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::backend::{FrameMeta, RenderResources},
|
windowing::backend::{FrameMeta, RenderResources, StereoMode},
|
||||||
};
|
};
|
||||||
|
|
||||||
const CURSOR_SIZE: f32 = 16. / 1440.;
|
const CURSOR_SIZE: f32 = 16. / 1440.;
|
||||||
|
|
||||||
struct MousePass {
|
struct BufPass {
|
||||||
pass: WGfxPass<Vert2Uv>,
|
pass: WGfxPass<Vert2Uv>,
|
||||||
buf_vert: Subbuffer<[Vert2Uv]>,
|
buf_vert: Subbuffer<[Vert2Uv]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct ScreenPipeline {
|
pub(super) struct ScreenPipeline {
|
||||||
mouse: MousePass,
|
mouse: BufPass,
|
||||||
|
pass: SmallVec<[BufPass; 2]>,
|
||||||
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||||
pass: WGfxPass<Vert2Uv>,
|
|
||||||
buf_alpha: Subbuffer<[f32]>,
|
buf_alpha: Subbuffer<[f32]>,
|
||||||
extentf: [f32; 2],
|
extentf: [f32; 2],
|
||||||
|
stereo: StereoMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenPipeline {
|
impl ScreenPipeline {
|
||||||
pub(super) fn new(meta: &FrameMeta, app: &mut AppState) -> anyhow::Result<Self> {
|
pub(super) fn new(
|
||||||
|
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(
|
||||||
@@ -63,17 +67,61 @@ impl ScreenPipeline {
|
|||||||
.gfx
|
.gfx
|
||||||
.empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 1)?;
|
.empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 1)?;
|
||||||
|
|
||||||
Ok(Self {
|
let mut me = Self {
|
||||||
pass: Self::create_pass(app, pipeline.clone(), extentf, buf_alpha.clone())?,
|
pass: smallvec![Self::create_pass(
|
||||||
|
app,
|
||||||
|
pipeline.clone(),
|
||||||
|
extentf,
|
||||||
|
buf_alpha.clone()
|
||||||
|
)?],
|
||||||
mouse: Self::create_mouse_pass(app, pipeline.clone(), extentf, buf_alpha.clone())?,
|
mouse: Self::create_mouse_pass(app, pipeline.clone(), extentf, buf_alpha.clone())?,
|
||||||
pipeline,
|
pipeline,
|
||||||
extentf,
|
extentf,
|
||||||
buf_alpha,
|
buf_alpha,
|
||||||
})
|
stereo,
|
||||||
|
};
|
||||||
|
me.set_stereo(app, stereo)?;
|
||||||
|
Ok(me)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_stereo(&mut self, app: &mut AppState, stereo: StereoMode) -> anyhow::Result<()> {
|
||||||
|
let depth = if matches!(stereo, StereoMode::None) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.pass.len() < depth {
|
||||||
|
self.pass.push(Self::create_pass(
|
||||||
|
app,
|
||||||
|
self.pipeline.clone(),
|
||||||
|
self.extentf,
|
||||||
|
self.buf_alpha.clone(),
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.pass.len() > depth {
|
||||||
|
self.pass.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (eye, current) in self.pass.iter_mut().enumerate() {
|
||||||
|
let verts = stereo_mode_to_verts(stereo, eye);
|
||||||
|
current.buf_vert.write()?.copy_from_slice(&verts);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_depth(&self) -> u32 {
|
||||||
|
self.pass.len() as _
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_extent(&mut self, app: &mut AppState, extentf: [f32; 2]) -> anyhow::Result<()> {
|
pub fn set_extent(&mut self, app: &mut AppState, extentf: [f32; 2]) -> anyhow::Result<()> {
|
||||||
self.pass = Self::create_pass(app, self.pipeline.clone(), extentf, self.buf_alpha.clone())?;
|
for (eye, pass) in self.pass.iter_mut().enumerate() {
|
||||||
|
*pass = Self::create_pass(app, self.pipeline.clone(), extentf, self.buf_alpha.clone())?;
|
||||||
|
let verts = stereo_mode_to_verts(self.stereo, eye);
|
||||||
|
pass.buf_vert.write()?.copy_from_slice(&verts);
|
||||||
|
}
|
||||||
|
|
||||||
self.mouse =
|
self.mouse =
|
||||||
Self::create_mouse_pass(app, self.pipeline.clone(), extentf, self.buf_alpha.clone())?;
|
Self::create_mouse_pass(app, self.pipeline.clone(), extentf, self.buf_alpha.clone())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -84,21 +132,27 @@ impl ScreenPipeline {
|
|||||||
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||||
extentf: [f32; 2],
|
extentf: [f32; 2],
|
||||||
buf_alpha: Subbuffer<[f32]>,
|
buf_alpha: Subbuffer<[f32]>,
|
||||||
) -> anyhow::Result<WGfxPass<Vert2Uv>> {
|
) -> anyhow::Result<BufPass> {
|
||||||
let set0 = pipeline.uniform_sampler(
|
let set0 = pipeline.uniform_sampler(
|
||||||
0,
|
0,
|
||||||
app.gfx_extras.fallback_image.clone(),
|
app.gfx_extras.fallback_image.clone(),
|
||||||
app.gfx.texture_filter,
|
app.gfx.texture_filter,
|
||||||
)?;
|
)?;
|
||||||
let set1 = pipeline.buffer(1, buf_alpha)?;
|
let set1 = pipeline.buffer(1, buf_alpha)?;
|
||||||
pipeline.create_pass(
|
let buf_vert = app
|
||||||
|
.gfx
|
||||||
|
.empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::VERTEX_BUFFER, 4)?;
|
||||||
|
|
||||||
|
let pass = pipeline.create_pass(
|
||||||
extentf,
|
extentf,
|
||||||
app.gfx_extras.quad_verts.clone(),
|
buf_vert.clone(),
|
||||||
0..4,
|
0..4,
|
||||||
0..1,
|
0..1,
|
||||||
vec![set0, set1],
|
vec![set0, set1],
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
Ok(BufPass { pass, buf_vert })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_mouse_pass(
|
fn create_mouse_pass(
|
||||||
@@ -106,7 +160,7 @@ impl ScreenPipeline {
|
|||||||
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||||
extentf: [f32; 2],
|
extentf: [f32; 2],
|
||||||
buf_alpha: Subbuffer<[f32]>,
|
buf_alpha: Subbuffer<[f32]>,
|
||||||
) -> anyhow::Result<MousePass> {
|
) -> anyhow::Result<BufPass> {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let mouse_bytes = [
|
let mouse_bytes = [
|
||||||
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
|
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
|
||||||
@@ -140,7 +194,7 @@ impl ScreenPipeline {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
cmd_xfer.build_and_execute_now()?;
|
cmd_xfer.build_and_execute_now()?;
|
||||||
Ok(MousePass { pass, buf_vert })
|
Ok(BufPass { pass, buf_vert })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn render(
|
pub(super) fn render(
|
||||||
@@ -150,33 +204,103 @@ impl ScreenPipeline {
|
|||||||
rdr: &mut RenderResources,
|
rdr: &mut RenderResources,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let view = ImageView::new_default(capture.image.clone())?;
|
let view = ImageView::new_default(capture.image.clone())?;
|
||||||
|
|
||||||
self.pass.update_sampler(0, view, app.gfx.texture_filter)?;
|
|
||||||
self.buf_alpha.write()?[0] = rdr.alpha;
|
self.buf_alpha.write()?[0] = rdr.alpha;
|
||||||
|
|
||||||
rdr.cmd_buf.run_ref(&self.pass)?;
|
for (eye, cmd_buf) in rdr.cmd_bufs.iter_mut().enumerate() {
|
||||||
|
let current = &mut self.pass[eye];
|
||||||
|
|
||||||
if let Some(mouse) = capture.mouse.as_ref() {
|
current
|
||||||
let size = CURSOR_SIZE * self.extentf[1];
|
.pass
|
||||||
let half_size = size * 0.5;
|
.update_sampler(0, view.clone(), app.gfx.texture_filter)?;
|
||||||
|
|
||||||
upload_quad_vertices(
|
cmd_buf.run_ref(¤t.pass)?;
|
||||||
&mut self.mouse.buf_vert,
|
|
||||||
self.extentf[0],
|
|
||||||
self.extentf[1],
|
|
||||||
mouse.x.mul_add(self.extentf[0], -half_size),
|
|
||||||
mouse.y.mul_add(self.extentf[1], -half_size),
|
|
||||||
size,
|
|
||||||
size,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
rdr.cmd_buf.run_ref(&self.mouse.pass)?;
|
if let Some(mouse) = capture.mouse.as_ref() {
|
||||||
|
let size = CURSOR_SIZE * self.extentf[1];
|
||||||
|
let half_size = size * 0.5;
|
||||||
|
|
||||||
|
upload_quad_vertices(
|
||||||
|
&mut self.mouse.buf_vert,
|
||||||
|
self.extentf[0],
|
||||||
|
self.extentf[1],
|
||||||
|
mouse.x.mul_add(self.extentf[0], -half_size),
|
||||||
|
mouse.y.mul_add(self.extentf[1], -half_size),
|
||||||
|
size,
|
||||||
|
size,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cmd_buf.run_ref(&self.mouse.pass)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stereo_mode_to_verts(stereo: StereoMode, array_index: usize) -> [Vert2Uv; 4] {
|
||||||
|
let eye = match stereo {
|
||||||
|
StereoMode::RightLeft | StereoMode::BottomTop => (1 - array_index) as f32,
|
||||||
|
_ => array_index as f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
match stereo {
|
||||||
|
StereoMode::None => [
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [0., 0.],
|
||||||
|
in_uv: [0., 0.],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [1., 0.],
|
||||||
|
in_uv: [1., 0.],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [0., 1.],
|
||||||
|
in_uv: [0., 1.],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [1., 1.],
|
||||||
|
in_uv: [1., 1.],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
StereoMode::LeftRight | StereoMode::RightLeft => [
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [0., 0.],
|
||||||
|
in_uv: [eye * 0.5, 0.],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [1., 0.],
|
||||||
|
in_uv: [0.5 + eye * 0.5, 0.],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [0., 1.],
|
||||||
|
in_uv: [eye * 0.5, 1.],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [1., 1.],
|
||||||
|
in_uv: [0.5 + eye * 0.5, 1.],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
StereoMode::TopBottom | StereoMode::BottomTop => [
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [0., 0.],
|
||||||
|
in_uv: [0., eye * 0.5],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [1., 0.],
|
||||||
|
in_uv: [1., eye * 0.5],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [0., 1.],
|
||||||
|
in_uv: [0., 0.5 + eye * 0.5],
|
||||||
|
},
|
||||||
|
Vert2Uv {
|
||||||
|
in_pos: [1., 1.],
|
||||||
|
in_uv: [1., 0.5 + eye * 0.5],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WlxCaptureIn {
|
pub struct WlxCaptureIn {
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
|
use glam::{vec3, Affine2, Affine3A, Quat, Vec3};
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
buffer::{BufferUsage, Subbuffer},
|
buffer::{BufferUsage, Subbuffer},
|
||||||
command_buffer::CommandBufferUsage,
|
command_buffer::CommandBufferUsage,
|
||||||
format::Format,
|
format::Format,
|
||||||
image::{Image, ImageTiling, SubresourceLayout, view::ImageView},
|
image::{view::ImageView, Image, ImageTiling, SubresourceLayout},
|
||||||
};
|
};
|
||||||
use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged};
|
use wayvr_ipc::packet_server::{self, PacketServer, WvrStateChanged};
|
||||||
use wgui::gfx::{
|
use wgui::gfx::{
|
||||||
WGfx,
|
|
||||||
pass::WGfxPass,
|
pass::WGfxPass,
|
||||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||||
|
WGfx,
|
||||||
};
|
};
|
||||||
use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
|
use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
|
||||||
use wlx_common::windowing::OverlayWindowState;
|
use wlx_common::windowing::OverlayWindowState;
|
||||||
@@ -22,22 +22,23 @@ use crate::{
|
|||||||
input::{self, HoverResult},
|
input::{self, HoverResult},
|
||||||
task::{OverlayTask, TaskType},
|
task::{OverlayTask, TaskType},
|
||||||
wayvr::{
|
wayvr::{
|
||||||
self, WayVR, WayVRAction, WayVRDisplayClickAction, display,
|
self, display,
|
||||||
server_ipc::{gen_args_vec, gen_env_vec},
|
server_ipc::{gen_args_vec, gen_env_vec},
|
||||||
|
WayVR, WayVRAction, WayVRDisplayClickAction,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config_wayvr,
|
config_wayvr,
|
||||||
graphics::{Vert2Uv, dmabuf::WGfxDmabuf},
|
graphics::{dmabuf::WGfxDmabuf, Vert2Uv},
|
||||||
state::{self, AppState},
|
state::{self, AppState},
|
||||||
subsystem::{hid::WheelDelta, input::KeyboardFocus},
|
subsystem::{hid::WheelDelta, input::KeyboardFocus},
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
|
|
||||||
backend::{
|
backend::{
|
||||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
ui_transform, BackendAttrib, BackendAttribValue, FrameMeta, OverlayBackend,
|
||||||
ui_transform,
|
OverlayEventData, RenderResources, ShouldRender,
|
||||||
},
|
},
|
||||||
manager::OverlayWindowManager,
|
manager::OverlayWindowManager,
|
||||||
window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData},
|
window::{OverlayCategory, OverlayWindowConfig, OverlayWindowData},
|
||||||
|
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -703,7 +704,7 @@ impl OverlayBackend for WayVRBackend {
|
|||||||
self.pass
|
self.pass
|
||||||
.update_sampler(0, image.vk_image_view.clone(), self.graphics.texture_filter)?;
|
.update_sampler(0, image.vk_image_view.clone(), self.graphics.texture_filter)?;
|
||||||
self.buf_alpha.write()?[0] = rdr.alpha;
|
self.buf_alpha.write()?[0] = rdr.alpha;
|
||||||
rdr.cmd_buf.run_ref(&self.pass)?;
|
rdr.cmd_buf_single().run_ref(&self.pass)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,6 +783,13 @@ impl OverlayBackend for WayVRBackend {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<Affine2> {
|
||||||
self.interaction_transform
|
self.interaction_transform
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_attrib(&self, _attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, _app: &mut AppState, _value: BackendAttribValue) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use glam::Affine3A;
|
use glam::Affine3A;
|
||||||
use idmap::IdMap;
|
use idmap::IdMap;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
||||||
@@ -22,7 +22,7 @@ use {
|
|||||||
use crate::subsystem::osc::OscSender;
|
use crate::subsystem::osc::OscSender;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{input::InputState, task::TaskContainer},
|
backend::{input::InputState, task::TaskContainer, XrBackend},
|
||||||
config::load_general_config,
|
config::load_general_config,
|
||||||
config_io::{self, get_config_file_path},
|
config_io::{self, get_config_file_path},
|
||||||
graphics::WGfxExtras,
|
graphics::WGfxExtras,
|
||||||
@@ -51,6 +51,8 @@ pub struct AppState {
|
|||||||
|
|
||||||
pub dbus: DbusConnector,
|
pub dbus: DbusConnector,
|
||||||
|
|
||||||
|
pub xr_backend: XrBackend,
|
||||||
|
|
||||||
#[cfg(feature = "osc")]
|
#[cfg(feature = "osc")]
|
||||||
pub osc_sender: Option<OscSender>,
|
pub osc_sender: Option<OscSender>,
|
||||||
|
|
||||||
@@ -60,7 +62,11 @@ pub struct AppState {
|
|||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn from_graphics(gfx: Arc<WGfx>, gfx_extras: WGfxExtras) -> anyhow::Result<Self> {
|
pub fn from_graphics(
|
||||||
|
gfx: Arc<WGfx>,
|
||||||
|
gfx_extras: WGfxExtras,
|
||||||
|
xr_backend: XrBackend,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
// insert shared resources
|
// insert shared resources
|
||||||
let mut tasks = TaskContainer::new();
|
let mut tasks = TaskContainer::new();
|
||||||
|
|
||||||
@@ -133,6 +139,7 @@ impl AppState {
|
|||||||
get_config_file_path(&theme),
|
get_config_file_path(&theme),
|
||||||
)?,
|
)?,
|
||||||
dbus,
|
dbus,
|
||||||
|
xr_backend,
|
||||||
|
|
||||||
#[cfg(feature = "osc")]
|
#[cfg(feature = "osc")]
|
||||||
osc_sender,
|
osc_sender,
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
use glam::{Affine2, Affine3A, Vec2};
|
use glam::{Affine2, Affine3A, Vec2};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
use vulkano::{
|
use vulkano::{command_buffer::CommandBufferUsage, format::Format, image::view::ImageView};
|
||||||
command_buffer::{CommandBufferUsage, PrimaryAutoCommandBuffer},
|
|
||||||
device::Queue,
|
|
||||||
format::Format,
|
|
||||||
image::view::ImageView,
|
|
||||||
};
|
|
||||||
use wgui::gfx::{
|
use wgui::gfx::{
|
||||||
WGfx,
|
|
||||||
cmd::{GfxCommandBuffer, WGfxClearMode},
|
cmd::{GfxCommandBuffer, WGfxClearMode},
|
||||||
|
WGfx,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::input::{HoverResult, PointerHit},
|
backend::input::{HoverResult, PointerHit},
|
||||||
graphics::ExtentExt,
|
graphics::{ExtentExt, RenderResult},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
subsystem::hid::WheelDelta,
|
subsystem::hid::WheelDelta,
|
||||||
windowing::{OverlayID, window::OverlayCategory},
|
windowing::{window::OverlayCategory, OverlayID},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
@@ -36,33 +32,90 @@ pub enum ShouldRender {
|
|||||||
Unable,
|
Unable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub enum StereoMode {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
LeftRight,
|
||||||
|
RightLeft,
|
||||||
|
TopBottom,
|
||||||
|
BottomTop,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenderTarget {
|
||||||
|
pub views: SmallVec<[Arc<ImageView>; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RenderResources {
|
pub struct RenderResources {
|
||||||
pub alpha: f32,
|
pub alpha: f32,
|
||||||
pub cmd_buf: GfxCommandBuffer,
|
pub cmd_bufs: SmallVec<[GfxCommandBuffer; 2]>,
|
||||||
pub extent: [u32; 2],
|
pub extent: [u32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderResources {
|
impl RenderResources {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
gfx: Arc<WGfx>,
|
gfx: Arc<WGfx>,
|
||||||
tgt: Arc<ImageView>,
|
target: RenderTarget,
|
||||||
meta: &FrameMeta,
|
meta: &FrameMeta,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> anyhow::Result<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
let mut cmd_buf = gfx.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
let mut cmd_bufs = SmallVec::new_const();
|
||||||
cmd_buf.begin_rendering(tgt, meta.clear)?;
|
|
||||||
|
for tgt in target.views {
|
||||||
|
let mut cmd_buf = gfx.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||||
|
|
||||||
|
cmd_buf.begin_rendering(tgt, meta.clear)?;
|
||||||
|
cmd_bufs.push(cmd_buf);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cmd_buf,
|
cmd_bufs,
|
||||||
alpha,
|
alpha,
|
||||||
extent: meta.extent.extent_u32arr(),
|
extent: meta.extent.extent_u32arr(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end(mut self) -> anyhow::Result<(Arc<Queue>, Arc<PrimaryAutoCommandBuffer>)> {
|
pub fn cmd_buf_single(&mut self) -> &mut GfxCommandBuffer {
|
||||||
self.cmd_buf.end_rendering()?;
|
self.cmd_bufs.first_mut().unwrap() // first must always be populated
|
||||||
Ok((self.cmd_buf.queue.clone(), self.cmd_buf.build()?))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_stereo(&self) -> bool {
|
||||||
|
self.cmd_bufs.len() > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(self) -> anyhow::Result<SmallVec<[RenderResult; 2]>> {
|
||||||
|
let mut ret_val = SmallVec::new_const();
|
||||||
|
|
||||||
|
for mut buf in self.cmd_bufs {
|
||||||
|
buf.end_rendering()?;
|
||||||
|
ret_val.push(RenderResult {
|
||||||
|
queue: buf.queue.clone(),
|
||||||
|
cmd_buf: buf.build()?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! attrib_value {
|
||||||
|
($opt:expr, $variant:path) => {
|
||||||
|
$opt.and_then(|e| match e {
|
||||||
|
$variant(inner) => Some(inner),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum BackendAttrib {
|
||||||
|
Stereo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum BackendAttribValue {
|
||||||
|
Stereo(StereoMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OverlayMeta {
|
pub struct OverlayMeta {
|
||||||
@@ -105,6 +158,8 @@ pub trait OverlayBackend: Any {
|
|||||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
|
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
|
||||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: WheelDelta);
|
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: WheelDelta);
|
||||||
fn get_interaction_transform(&mut self) -> Option<Affine2>;
|
fn get_interaction_transform(&mut self) -> Option<Affine2>;
|
||||||
|
fn get_attrib(&self, attrib: BackendAttrib) -> Option<BackendAttribValue>;
|
||||||
|
fn set_attrib(&mut self, app: &mut AppState, value: BackendAttribValue) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui_transform(extent: [u32; 2]) -> Affine2 {
|
pub fn ui_transform(extent: [u32; 2]) -> Affine2 {
|
||||||
@@ -156,4 +211,10 @@ impl OverlayBackend for DummyBackend {
|
|||||||
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
fn get_interaction_transform(&mut self) -> Option<glam::Affine2> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn get_attrib(&self, _attrib: BackendAttrib) -> Option<BackendAttribValue> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn set_attrib(&mut self, _: &mut AppState, _value: BackendAttribValue) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use wlx_common::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
FRAME_COUNTER,
|
|
||||||
backend::task::OverlayTask,
|
backend::task::OverlayTask,
|
||||||
overlays::{
|
overlays::{
|
||||||
anchor::create_anchor, edit::EditWrapperManager, keyboard::create_keyboard,
|
anchor::create_anchor, edit::EditWrapperManager, keyboard::create_keyboard,
|
||||||
@@ -20,12 +19,13 @@ use crate::{
|
|||||||
},
|
},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
windowing::{
|
windowing::{
|
||||||
OverlayID, OverlaySelector,
|
|
||||||
backend::{OverlayEventData, OverlayMeta},
|
backend::{OverlayEventData, OverlayMeta},
|
||||||
set::OverlayWindowSet,
|
set::OverlayWindowSet,
|
||||||
snap_upright,
|
snap_upright,
|
||||||
window::{OverlayCategory, OverlayWindowData},
|
window::{OverlayCategory, OverlayWindowData},
|
||||||
|
OverlayID, OverlaySelector,
|
||||||
},
|
},
|
||||||
|
FRAME_COUNTER,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MAX_OVERLAY_SETS: usize = 7;
|
pub const MAX_OVERLAY_SETS: usize = 7;
|
||||||
@@ -401,6 +401,7 @@ impl<T> OverlayWindowManager<T> {
|
|||||||
if enabled {
|
if enabled {
|
||||||
self.wrappers
|
self.wrappers
|
||||||
.wrap_edit_mode(id, &mut overlay.config, app)
|
.wrap_edit_mode(id, &mut overlay.config, app)
|
||||||
|
.inspect_err(|e| log::error!("{e:?}"))
|
||||||
.unwrap(); // FIXME: unwrap
|
.unwrap(); // FIXME: unwrap
|
||||||
} else {
|
} else {
|
||||||
self.wrappers.unwrap_edit_mode(&mut overlay.config);
|
self.wrappers.unwrap_edit_mode(&mut overlay.config);
|
||||||
|
|||||||
Reference in New Issue
Block a user