refactor overlay windowing

This commit is contained in:
galister
2025-10-05 18:49:59 +09:00
parent 40cc27f7b0
commit aa64310d96
46 changed files with 1329 additions and 1342 deletions

View File

@@ -1,30 +1,31 @@
use glam::Vec3A;
use glam::{Affine3A, Quat, Vec3};
use std::sync::{Arc, LazyLock};
use crate::backend::overlay::{OverlayData, OverlayState, Positioning, Z_ORDER_ANCHOR};
use crate::gui::panel::GuiPanel;
use crate::state::AppState;
use crate::windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning};
use crate::windowing::Z_ORDER_ANCHOR;
pub static ANCHOR_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("anchor"));
pub fn create_anchor<O>(app: &mut AppState) -> anyhow::Result<OverlayData<O>>
where
O: Default,
{
pub fn create_anchor(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
let panel = GuiPanel::new_from_template(app, "gui/anchor.xml", (), None)?;
Ok(OverlayData {
state: OverlayState {
name: ANCHOR_NAME.clone(),
want_visible: false,
Ok(OverlayWindowConfig {
name: ANCHOR_NAME.clone(),
z_order: Z_ORDER_ANCHOR,
default_state: OverlayWindowState {
interactable: false,
grabbable: false,
z_order: Z_ORDER_ANCHOR,
spawn_scale: 0.1,
spawn_point: Vec3A::NEG_Z * 0.5,
positioning: Positioning::Static,
..Default::default()
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * 0.1,
Quat::IDENTITY,
Vec3::NEG_Z * 0.5,
),
..OverlayWindowState::default()
},
..OverlayData::from_backend(Box::new(panel))
global: true,
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}

View File

@@ -1,7 +1,9 @@
use glam::{Affine3A, Vec3};
use crate::{
backend::overlay::{OverlayData, OverlayState},
gui::panel::GuiPanel,
state::AppState,
windowing::window::{OverlayWindowConfig, OverlayWindowState},
};
pub const BAR_NAME: &str = "bar";
@@ -11,7 +13,7 @@ struct BarState {}
#[allow(clippy::significant_drop_tightening)]
#[allow(clippy::for_kv_map)] // TODO: remove later
#[allow(clippy::match_same_arms)] // TODO: remove later
pub fn create_bar<O>(app: &mut AppState) -> anyhow::Result<OverlayData<O>>
pub fn create_bar<O>(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig>
where
O: Default,
{
@@ -34,14 +36,14 @@ where
panel.update_layout()?;
Ok(OverlayData {
state: OverlayState {
name: BAR_NAME.into(),
want_visible: true,
Ok(OverlayWindowConfig {
name: BAR_NAME.into(),
default_state: OverlayWindowState {
interactable: true,
spawn_scale: 0.15,
..Default::default()
transform: Affine3A::from_scale(Vec3::ONE * 0.15),
..OverlayWindowState::default()
},
..OverlayData::from_backend(Box::new(panel))
global: true,
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}

View File

@@ -1,11 +1,11 @@
use std::sync::Arc;
use glam::Vec3A;
use glam::{vec3, Affine3A, Quat, Vec3};
use crate::{
backend::overlay::{OverlayBackend, OverlayState},
gui::panel::GuiPanel,
state::AppState,
windowing::window::{OverlayWindowConfig, OverlayWindowState},
};
const SETTINGS_NAME: &str = "settings";
@@ -13,10 +13,7 @@ const SETTINGS_NAME: &str = "settings";
#[allow(unreachable_code)]
#[allow(unused_variables)]
#[allow(dead_code)]
pub fn create_custom(
app: &mut AppState,
name: Arc<str>,
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindowConfig> {
return None;
unreachable!();
@@ -24,17 +21,18 @@ pub fn create_custom(
let panel = GuiPanel::new_blank(app, ()).ok()?;
panel.update_layout().ok()?;
let state = OverlayState {
Some(OverlayWindowConfig {
name,
want_visible: true,
interactable: true,
grabbable: true,
spawn_scale: 0.1, //TODO: this
spawn_point: Vec3A::from_array([0., 0., -0.5]),
//interaction_transform: ui_transform(config.size),
..Default::default()
};
let backend = Box::new(panel);
Some((state, backend))
default_state: OverlayWindowState {
interactable: true,
grabbable: true,
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * 0.1, // TODO scale
Quat::IDENTITY,
vec3(0.0, 0.0, -0.5),
),
..OverlayWindowState::default()
},
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}

View File

@@ -1,6 +1,6 @@
use std::{collections::HashMap, rc::Rc};
use glam::{vec2, vec3a, Mat4, Vec2, Vec3};
use glam::{vec2, vec3, Affine3A, Mat4, Quat, Vec2, Vec3};
use wgui::{
animation::{Animation, AnimationEasing},
drawing::Color,
@@ -17,10 +17,10 @@ use wgui::{
};
use crate::{
backend::overlay::{OverlayData, OverlayState, Positioning},
gui::panel::GuiPanel,
state::AppState,
subsystem::hid::{XkbKeymap, ALT, CTRL, META, SHIFT, SUPER},
windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning},
};
use super::{
@@ -33,13 +33,10 @@ const BACKGROUND_PADDING: f32 = 4.;
const PIXELS_PER_UNIT: f32 = 80.;
#[allow(clippy::too_many_lines, clippy::significant_drop_tightening)]
pub fn create_keyboard<O>(
pub fn create_keyboard(
app: &mut AppState,
mut keymap: Option<XkbKeymap>,
) -> anyhow::Result<OverlayData<O>>
where
O: Default,
{
) -> anyhow::Result<OverlayWindowConfig> {
let layout = layout::Layout::load_from_disk();
let state = KeyboardState {
modifiers: 0,
@@ -278,18 +275,21 @@ where
let width = layout.row_size * 0.05 * app.session.config.keyboard_scale;
Ok(OverlayData {
state: OverlayState {
name: KEYBOARD_NAME.into(),
Ok(OverlayWindowConfig {
name: KEYBOARD_NAME.into(),
default_state: OverlayWindowState {
grabbable: true,
recenter: true,
positioning: Positioning::Anchored,
interactable: true,
spawn_scale: width,
spawn_point: vec3a(0., -0.5, 0.),
..Default::default()
curvature: Some(0.15),
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * width,
Quat::from_rotation_x(-10f32.to_radians()),
vec3(0.0, -0.5, -0.5),
),
..OverlayWindowState::default()
},
..OverlayData::from_backend(Box::new(KeyboardBackend { panel }))
..OverlayWindowConfig::from_backend(Box::new(KeyboardBackend { panel }))
})
}

View File

@@ -11,14 +11,12 @@ use wgui::{
};
use crate::{
backend::{
input::{Haptics, PointerHit},
overlay::{FrameMeta, OverlayBackend, ShouldRender},
},
backend::input::{Haptics, PointerHit},
graphics::CommandBuffers,
gui::panel::GuiPanel,
state::AppState,
subsystem::hid::{ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey},
windowing::backend::{FrameMeta, OverlayBackend, ShouldRender},
};
pub mod builder;

View File

@@ -4,19 +4,22 @@ use std::{
};
use futures::{Future, FutureExt};
use glam::Affine2;
use glam::{Affine2, Affine3A, Vec3};
use vulkano::image::view::ImageView;
use wlx_capture::pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen};
use wlx_capture::pipewire::{pipewire_select_screen, PipewireCapture, PipewireSelectScreenResult};
use crate::{
backend::{
common::OverlaySelector,
input::{Haptics, PointerHit},
overlay::{FrameMeta, OverlayBackend, OverlayState, ShouldRender, ui_transform},
task::TaskType,
},
graphics::CommandBuffers,
state::{AppSession, AppState},
windowing::{
backend::{ui_transform, FrameMeta, OverlayBackend, ShouldRender},
window::{OverlayWindowConfig, OverlayWindowState},
OverlaySelector,
},
};
use super::screen::backend::ScreenBackend;
@@ -70,9 +73,7 @@ impl OverlayBackend for MirrorBackend {
app.tasks.enqueue(TaskType::Overlay(
OverlaySelector::Name(self.name.clone()),
Box::new(|app, o| {
o.grabbable = true;
o.interactable = true;
o.reset(app, false);
o.activate(app);
}),
));
}
@@ -140,19 +141,15 @@ impl OverlayBackend for MirrorBackend {
}
}
pub fn new_mirror(
name: Arc<str>,
show_hide: bool,
session: &AppSession,
) -> (OverlayState, Box<dyn OverlayBackend>) {
let state = OverlayState {
pub fn new_mirror(name: Arc<str>, session: &AppSession) -> OverlayWindowConfig {
OverlayWindowConfig {
name: name.clone(),
show_hide,
want_visible: true,
spawn_scale: 0.5 * session.config.desktop_view_scale,
..Default::default()
};
let backend = Box::new(MirrorBackend::new(name));
(state, backend)
default_state: OverlayWindowState {
interactable: true,
grabbable: true,
transform: Affine3A::from_scale(Vec3::ONE * 0.5 * session.config.desktop_view_scale),
..OverlayWindowState::default()
},
..OverlayWindowConfig::from_backend(Box::new(MirrorBackend::new(name)))
}
}

View File

@@ -1,23 +1,21 @@
use std::{
sync::{atomic::AtomicU64, Arc, LazyLock},
sync::{Arc, LazyLock, atomic::AtomicU64},
time::Instant,
};
use glam::{vec2, Affine2, Vec2};
use glam::{Affine2, Vec2, vec2};
use vulkano::image::view::ImageView;
use wlx_capture::{frame::Transform, WlxCapture};
use wlx_capture::{WlxCapture, frame::Transform};
use crate::{
backend::{
input::{Haptics, PointerHit, PointerMode},
overlay::{FrameMeta, OverlayBackend, ShouldRender},
},
backend::input::{Haptics, PointerHit, PointerMode},
graphics::{CommandBuffers, ExtentExt},
state::AppState,
subsystem::hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT},
windowing::backend::{FrameMeta, OverlayBackend, ShouldRender},
};
use super::capture::{receive_callback, ScreenPipeline, WlxCaptureIn, WlxCaptureOut};
use super::capture::{ScreenPipeline, WlxCaptureIn, WlxCaptureOut, receive_callback};
const CURSOR_SIZE: f32 = 16. / 1440.;

View File

@@ -7,28 +7,29 @@ use vulkano::{
command_buffer::CommandBufferUsage,
device::Queue,
format::Format,
image::{sampler::Filter, view::ImageView, Image},
image::{Image, sampler::Filter, view::ImageView},
pipeline::graphics::color_blend::AttachmentBlend,
};
use wgui::gfx::{
WGfx,
cmd::WGfxClearMode,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx,
};
use wlx_capture::{
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
WlxCapture,
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
};
use crate::{
backend::overlay::FrameMeta,
config::GeneralConfig,
graphics::{
dmabuf::{fourcc_to_vk, WGfxDmabuf},
upload_quad_vertices, CommandBuffers, Vert2Uv,
CommandBuffers, Vert2Uv,
dmabuf::{WGfxDmabuf, fourcc_to_vk},
upload_quad_vertices,
},
state::AppState,
windowing::backend::FrameMeta,
};
const CURSOR_SIZE: f32 = 16. / 1440.;

View File

@@ -1,14 +1,15 @@
use std::{f32::consts::PI, sync::Arc};
use backend::ScreenBackend;
use glam::{vec3a, Quat, Vec3};
use wl::create_screens_wayland;
use glam::{vec3, Affine3A, Quat, Vec3};
use wlx_capture::frame::Transform;
use crate::{
backend::overlay::{OverlayState, Positioning},
state::{AppSession, AppState, ScreenMeta},
subsystem::{hid::XkbKeymap, input::KeyboardFocus},
windowing::{
backend::OverlayBackend,
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
},
};
pub mod backend;
@@ -20,7 +21,12 @@ pub mod wl;
#[cfg(feature = "x11")]
pub mod x11;
fn create_screen_state(name: Arc<str>, transform: Transform, session: &AppSession) -> OverlayState {
fn create_screen_from_backend(
name: Arc<str>,
transform: Transform,
session: &AppSession,
backend: Box<dyn OverlayBackend>,
) -> OverlayWindowConfig {
let angle = if session.config.upright_screen_fix {
match transform {
Transform::Rotated90 | Transform::Flipped90 => PI / 2.,
@@ -32,22 +38,27 @@ fn create_screen_state(name: Arc<str>, transform: Transform, session: &AppSessio
0.
};
OverlayState {
OverlayWindowConfig {
name,
default_state: OverlayWindowState {
grabbable: true,
positioning: Positioning::Anchored,
interactable: true,
curvature: Some(0.15),
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * 1.5 * session.config.desktop_view_scale,
Quat::from_rotation_z(angle),
vec3(0.0, 0.2, -0.5),
),
..OverlayWindowState::default()
},
keyboard_focus: Some(KeyboardFocus::PhysicalScreen),
grabbable: true,
recenter: true,
positioning: Positioning::Anchored,
interactable: true,
spawn_scale: 1.5 * session.config.desktop_view_scale,
spawn_point: vec3a(0., 0.5, 0.),
spawn_rotation: Quat::from_axis_angle(Vec3::Z, angle),
..Default::default()
..OverlayWindowConfig::from_backend(backend)
}
}
pub struct ScreenCreateData {
pub screens: Vec<(ScreenMeta, OverlayState, Box<ScreenBackend>)>,
pub screens: Vec<(ScreenMeta, OverlayWindowConfig)>,
}
pub fn create_screens(app: &mut AppState) -> anyhow::Result<(ScreenCreateData, Option<XkbKeymap>)> {
@@ -61,7 +72,7 @@ pub fn create_screens(app: &mut AppState) -> anyhow::Result<(ScreenCreateData, O
.map_err(|f| log::warn!("Could not load keyboard layout: {f}"))
.ok();
return Ok((create_screens_wayland(&mut wl, app), keymap));
return Ok((wl::create_screens_wayland(&mut wl, app), keymap));
}
log::info!("Wayland not detected, assuming X11.");
}

View File

@@ -1,22 +1,22 @@
use glam::vec2;
use wlx_capture::{
WlxCapture,
wayland::{WlxClient, WlxOutput},
wlr_dmabuf::WlrDmabufCapture,
wlr_screencopy::WlrScreencopyCapture,
WlxCapture,
};
use crate::{
config::{AStrMapExt, PwTokenMap},
overlays::screen::create_screen_state,
overlays::screen::create_screen_from_backend,
state::{AppState, ScreenMeta},
};
use super::{
backend::ScreenBackend,
capture::{new_wlx_capture, MainThreadWlxCapture},
pw::{load_pw_token_config, save_pw_token_config},
ScreenCreateData,
backend::ScreenBackend,
capture::{MainThreadWlxCapture, new_wlx_capture},
pw::{load_pw_token_config, save_pw_token_config},
};
impl ScreenBackend {
@@ -130,15 +130,19 @@ pub fn create_screens_wayland(wl: &mut WlxClient, app: &mut AppState) -> ScreenC
backend.set_mouse_transform(logical_pos, logical_size, transform);
let state = create_screen_state(output.name.clone(), transform, &app.session);
let window_config = create_screen_from_backend(
output.name.clone(),
transform,
&app.session,
Box::new(backend),
);
let meta = ScreenMeta {
name: wl.outputs[id].name.clone(),
native_handle: *id,
};
let backend = Box::new(backend);
screens.push((meta, state, backend));
screens.push((meta, window_config));
}
}

View File

@@ -2,20 +2,20 @@ use std::sync::Arc;
use glam::vec2;
use wlx_capture::{
WlxCapture,
frame::Transform,
xshm::{XshmCapture, XshmScreen},
WlxCapture,
};
use crate::{
overlays::screen::create_screen_state,
overlays::screen::create_screen_from_backend,
state::{AppState, ScreenMeta},
};
use super::{
backend::ScreenBackend,
capture::{new_wlx_capture, MainThreadWlxCapture},
ScreenCreateData,
backend::ScreenBackend,
capture::{MainThreadWlxCapture, new_wlx_capture},
};
#[cfg(feature = "pipewire")]
@@ -39,7 +39,7 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
use crate::{
config::{AStrMapExt, PwTokenMap},
overlays::screen::{
create_screen_state,
create_screen_from_backend,
pw::{load_pw_token_config, save_pw_token_config, select_pw_screen},
},
state::ScreenMeta,
@@ -108,15 +108,19 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
Transform::Normal,
);
let state = create_screen_state(m.name.clone(), Transform::Normal, &app.session);
let window_data = create_screen_from_backend(
m.name.clone(),
Transform::Normal,
&app.session,
Box::new(backend),
);
let meta = ScreenMeta {
name: m.name.clone(),
native_handle: 0,
};
let backend = Box::new(backend);
(meta, state, backend)
(meta, window_data)
})
.collect();
@@ -189,15 +193,19 @@ pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateDat
Transform::Normal,
);
let state = create_screen_state(s.name.clone(), Transform::Normal, &app.session);
let window_data = create_screen_from_backend(
s.name.clone(),
Transform::Normal,
&app.session,
Box::new(backend),
);
let meta = ScreenMeta {
name: s.name.clone(),
native_handle: 0,
};
let backend = Box::new(backend);
(meta, state, backend)
(meta, window_data)
})
.collect();

View File

@@ -5,7 +5,7 @@ use std::{
time::Instant,
};
use glam::{vec3a, Quat};
use glam::{vec3, Affine3A, Quat, Vec3};
use idmap_derive::IntegerId;
use serde::{Deserialize, Serialize};
use wgui::{
@@ -24,13 +24,13 @@ use wgui::{
};
use crate::{
backend::{
common::OverlaySelector,
overlay::{OverlayBackend, OverlayState, Positioning, Z_ORDER_TOAST},
task::TaskType,
},
backend::task::TaskType,
gui::panel::GuiPanel,
state::{AppState, LeftRight},
windowing::{
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
OverlaySelector, Z_ORDER_TOAST,
},
};
const FONT_SIZE: isize = 16;
@@ -112,16 +112,13 @@ impl Toast {
TaskType::CreateOverlay(
selector.clone(),
Box::new(move |app| {
let mut maybe_toast = new_toast(self, app);
if let Some((state, _)) = maybe_toast.as_mut() {
state.auto_movement(app);
app.tasks.enqueue_at(
// at timeout, drop the overlay by ID instead
// in order to avoid dropping any newer toasts
TaskType::DropOverlay(selector),
destroy_at,
);
}
let maybe_toast = new_toast(self, app);
app.tasks.enqueue_at(
// at timeout, drop the overlay by ID instead
// in order to avoid dropping any newer toasts
TaskType::DropOverlay(selector),
destroy_at,
);
maybe_toast
}),
),
@@ -131,7 +128,7 @@ impl Toast {
}
#[allow(clippy::too_many_lines)]
fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
let current_method = app
.session
.toast_topics
@@ -142,12 +139,13 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
let (spawn_point, spawn_rotation, positioning) = match current_method {
DisplayMethod::Hide => return None,
DisplayMethod::Center => (
vec3a(0., -0.2, -0.5),
vec3(0., -0.2, -0.5),
Quat::IDENTITY,
Positioning::FollowHead { lerp: 0.1 },
),
DisplayMethod::Watch => {
let mut watch_pos = app.session.config.watch_pos + vec3a(-0.005, -0.05, 0.02);
let mut watch_pos =
Vec3::from(app.session.config.watch_pos) + vec3(-0.005, -0.05, 0.02);
let mut watch_rot = app.session.config.watch_rot;
let relative_to = match app.session.config.watch_hand {
LeftRight::Left => Positioning::FollowHand { hand: 0, lerp: 1.0 },
@@ -239,19 +237,21 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<(OverlayState, Box<dyn
panel.update_layout().ok()?;
let state = OverlayState {
Some(OverlayWindowConfig {
name: TOAST_NAME.clone(),
want_visible: true,
spawn_scale: panel.layout.content_size.x * PIXELS_TO_METERS,
spawn_rotation,
spawn_point,
default_state: OverlayWindowState {
positioning,
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * panel.layout.content_size.x * PIXELS_TO_METERS,
spawn_rotation,
spawn_point,
),
..OverlayWindowState::default()
},
global: true,
z_order: Z_ORDER_TOAST,
positioning,
..Default::default()
};
let backend = Box::new(panel);
Some((state, backend))
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}
fn msg_err(app: &mut AppState, message: &str) {

View File

@@ -1,98 +0,0 @@
use glam::{Affine3A, Quat, Vec3A};
use wgui::{
i18n::Translation,
parser::parse_color_hex,
renderer_vk::text::TextStyle,
taffy::{
self,
prelude::{auto, length, percent},
},
widget::{
rectangle::{Rectangle, RectangleParams},
text::{TextLabel, TextParams},
util::WLength,
},
};
use crate::{
backend::overlay::{OverlayBackend, OverlayState, Z_ORDER_TOAST},
gui::panel::GuiPanel,
state::AppState,
};
const FONT_SIZE: isize = 16;
const PADDING: (f32, f32) = (25., 7.);
const PIXELS_TO_METERS: f32 = 1. / 2000.;
#[allow(clippy::too_many_lines)]
fn new_tooltip(
text: &str,
transform: Affine3A,
app: &mut AppState,
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
let mut panel = GuiPanel::new_blank(app, ()).ok()?;
let globals = panel.layout.state.globals.clone();
let mut i18n = globals.i18n();
let (rect, _) = panel
.layout
.add_child(
panel.layout.root_widget,
Rectangle::create(RectangleParams {
color: parse_color_hex("#1e2030").unwrap(),
border_color: parse_color_hex("#5e7090").unwrap(),
border: 1.0,
round: WLength::Units(4.0),
..Default::default()
})
.unwrap(),
taffy::Style {
align_items: Some(taffy::AlignItems::Center),
justify_content: Some(taffy::JustifyContent::Center),
flex_direction: taffy::FlexDirection::Column,
padding: length(4.0),
..Default::default()
},
)
.ok()?;
let _ = panel.layout.add_child(
rect.id,
TextLabel::create(
&mut i18n,
TextParams {
content: Translation::from_raw_text(text),
style: TextStyle {
color: parse_color_hex("#ffffff"),
..Default::default()
},
},
)
.unwrap(),
taffy::Style {
size: taffy::Size {
width: percent(1.0),
height: auto(),
},
padding: length(8.0),
..Default::default()
},
);
panel.update_layout().ok()?;
let state = OverlayState {
name: "tooltip".into(),
want_visible: true,
spawn_scale: panel.layout.content_size.x * PIXELS_TO_METERS,
spawn_rotation: Quat::IDENTITY,
spawn_point: Vec3A::ZERO,
z_order: Z_ORDER_TOAST,
positioning: crate::backend::overlay::Positioning::Static,
..Default::default()
};
let backend = Box::new(panel);
Some((state, backend))
}

View File

@@ -1,12 +1,14 @@
use std::{collections::HashMap, rc::Rc, sync::Arc, time::Duration};
use std::{collections::HashMap, rc::Rc, time::Duration};
use glam::Vec3A;
use smallvec::SmallVec;
use glam::{Affine3A, Vec3, Vec3A};
use crate::{
backend::overlay::{OverlayData, OverlayState, Positioning, Z_ORDER_WATCH},
gui::{panel::GuiPanel, timer::GuiTimer},
state::AppState,
windowing::{
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState, Positioning},
Z_ORDER_WATCH,
},
};
pub const WATCH_NAME: &str = "watch";
@@ -14,16 +16,7 @@ pub const WATCH_NAME: &str = "watch";
struct WatchState {}
#[allow(clippy::significant_drop_tightening)]
pub fn create_watch<O>(app: &mut AppState) -> anyhow::Result<OverlayData<O>>
where
O: Default,
{
let screens = app
.screens
.iter()
.map(|s| s.name.clone())
.collect::<SmallVec<[Arc<str>; 8]>>();
pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<OverlayWindowConfig> {
let state = WatchState {};
let mut panel = GuiPanel::new_from_template(
app,
@@ -35,10 +28,10 @@ where
return Ok(());
}
for (idx, handle) in screens.iter().enumerate() {
for idx in 0..num_sets {
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert("display".into(), (idx + 1).to_string().into());
params.insert("handle".into(), handle.as_ref().into());
params.insert("handle".into(), idx.to_string().into());
parser_state.instantiate_template(
doc_params, "Set", layout, listeners, widget, params,
)?;
@@ -59,47 +52,36 @@ where
panel.update_layout()?;
Ok(OverlayData {
state: OverlayState {
name: WATCH_NAME.into(),
want_visible: true,
Ok(OverlayWindowConfig {
name: WATCH_NAME.into(),
z_order: Z_ORDER_WATCH,
default_state: OverlayWindowState {
interactable: true,
z_order: Z_ORDER_WATCH,
spawn_scale: 0.115, //TODO:configurable
spawn_point: app.session.config.watch_pos,
spawn_rotation: app.session.config.watch_rot,
positioning,
..Default::default()
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * 0.115,
app.session.config.watch_rot,
app.session.config.watch_pos,
),
..OverlayWindowState::default()
},
..OverlayData::from_backend(Box::new(panel))
show_on_spawn: true,
global: true,
..OverlayWindowConfig::from_backend(Box::new(panel))
})
}
pub fn watch_fade<D>(app: &mut AppState, watch: &mut OverlayData<D>)
where
D: Default,
{
if watch.state.saved_transform.is_some() {
watch.state.want_visible = false;
pub fn watch_fade<D>(app: &mut AppState, watch: &mut OverlayWindowData<D>) {
let Some(state) = watch.config.active_state.as_mut() else {
return;
}
};
let to_hmd = (watch.state.transform.translation - app.input_state.hmd.translation).normalize();
let watch_normal = watch
.state
.transform
.transform_vector3a(Vec3A::NEG_Z)
.normalize();
let to_hmd = (state.transform.translation - app.input_state.hmd.translation).normalize();
let watch_normal = state.transform.transform_vector3a(Vec3A::NEG_Z).normalize();
let dot = to_hmd.dot(watch_normal);
if dot < app.session.config.watch_view_angle_min {
watch.state.want_visible = false;
} else {
watch.state.want_visible = true;
watch.state.alpha = (dot - app.session.config.watch_view_angle_min)
/ (app.session.config.watch_view_angle_max - app.session.config.watch_view_angle_min);
watch.state.alpha += 0.1;
watch.state.alpha = watch.state.alpha.clamp(0., 1.);
}
state.alpha = (dot - app.session.config.watch_view_angle_min)
/ (app.session.config.watch_view_angle_max - app.session.config.watch_view_angle_min);
state.alpha += 0.1;
state.alpha = state.alpha.clamp(0., 1.);
}

View File

@@ -1,4 +1,4 @@
use glam::{vec3a, Affine2, Vec3, Vec3A};
use glam::{vec3, Affine2, Affine3A, Quat, Vec3};
use smallvec::smallvec;
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
use vulkano::{
@@ -17,12 +17,7 @@ use wlx_capture::frame::{DmabufFrame, FourCC, FrameFormat, FramePlane};
use crate::{
backend::{
common::{OverlayContainer, OverlaySelector},
input::{self},
overlay::{
ui_transform, FrameMeta, OverlayBackend, OverlayData, OverlayID, OverlayState,
ShouldRender, Z_ORDER_DASHBOARD,
},
task::TaskType,
wayvr::{
self, display,
@@ -34,6 +29,12 @@ use crate::{
graphics::{dmabuf::WGfxDmabuf, CommandBuffers, Vert2Uv},
state::{self, AppState},
subsystem::input::KeyboardFocus,
windowing::{
backend::{ui_transform, FrameMeta, OverlayBackend, ShouldRender},
manager::OverlayWindowManager,
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState},
OverlayID, OverlaySelector, Z_ORDER_DASHBOARD,
},
};
use super::toast::error_toast;
@@ -222,7 +223,7 @@ pub fn executable_exists_in_path(command: &str) -> bool {
fn toggle_dashboard<O>(
app: &mut AppState,
overlays: &mut OverlayContainer<O>,
overlays: &mut OverlayWindowManager<O>,
wayvr: &mut WayVRData,
) -> anyhow::Result<()>
where
@@ -245,31 +246,39 @@ where
if newly_created {
log::info!("Creating dashboard overlay");
let mut overlay = create_overlay::<O>(
app,
DASHBOARD_DISPLAY_NAME,
OverlayToCreate {
disp_handle,
conf_display: config_wayvr::WayVRDisplay {
attach_to: None,
width: DASHBOARD_WIDTH,
height: DASHBOARD_HEIGHT,
scale: None,
rotation: None,
pos: None,
primary: None,
},
let mut overlay = OverlayWindowData::from_config(OverlayWindowConfig {
default_state: OverlayWindowState {
curvature: Some(0.15),
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * 2.0,
Quat::IDENTITY,
vec3(0.0, -0.35, -1.75),
),
..OverlayWindowState::default()
},
)?;
z_order: Z_ORDER_DASHBOARD,
show_on_spawn: true,
..create_overlay(
app,
DASHBOARD_DISPLAY_NAME,
OverlayToCreate {
disp_handle,
conf_display: config_wayvr::WayVRDisplay {
attach_to: None,
width: DASHBOARD_WIDTH,
height: DASHBOARD_HEIGHT,
scale: None,
rotation: None,
pos: None,
primary: None,
},
},
)?
});
overlay.state.curvature = Some(0.15);
overlay.state.want_visible = true;
overlay.state.spawn_scale = 2.0;
overlay.state.spawn_point = vec3a(0.0, -0.35, -1.75);
overlay.state.z_order = Z_ORDER_DASHBOARD;
overlay.state.reset(app, true);
overlay.config.reset(app, true);
let overlay_id = overlays.add(overlay);
let overlay_id = overlays.add(overlay, app);
wayvr.set_overlay_display_handle(overlay_id, disp_handle);
let args_vec = &conf_dash
@@ -319,29 +328,22 @@ where
app.tasks.enqueue(TaskType::Overlay(
OverlaySelector::Id(overlay_id),
Box::new(move |app, o| {
// Toggle visibility
o.want_visible = cur_visibility;
if cur_visibility {
o.reset(app, true);
}
o.toggle(app);
}),
));
Ok(())
}
fn create_overlay<O>(
fn create_overlay(
app: &mut AppState,
name: &str,
cell: OverlayToCreate,
) -> anyhow::Result<OverlayData<O>>
where
O: Default,
{
) -> anyhow::Result<OverlayWindowConfig> {
let conf_display = &cell.conf_display;
let disp_handle = cell.disp_handle;
let mut overlay = create_wayvr_display_overlay::<O>(
let mut overlay = create_wayvr_display_overlay(
app,
conf_display.width,
conf_display.height,
@@ -351,17 +353,22 @@ where
)?;
if let Some(attach_to) = &conf_display.attach_to {
overlay.state.positioning = attach_to.get_positioning();
overlay.default_state.positioning = attach_to.get_positioning();
}
if let Some(rot) = &conf_display.rotation {
overlay.state.spawn_rotation =
glam::Quat::from_axis_angle(Vec3::from_slice(&rot.axis), f32::to_radians(rot.angle));
}
let rot = if let Some(rot) = &conf_display.rotation {
glam::Quat::from_axis_angle(Vec3::from_slice(&rot.axis), f32::to_radians(rot.angle))
} else {
glam::Quat::IDENTITY
};
if let Some(pos) = &conf_display.pos {
overlay.state.spawn_point = Vec3A::from_slice(pos);
}
let pos = if let Some(pos) = &conf_display.pos {
Vec3::from_slice(pos)
} else {
Vec3::NEG_Z
};
overlay.default_state.transform = Affine3A::from_rotation_translation(rot, pos);
Ok(overlay)
}
@@ -369,7 +376,7 @@ where
fn create_queued_displays<O>(
app: &mut AppState,
data: &mut WayVRData,
overlays: &mut OverlayContainer<O>,
overlays: &mut OverlayWindowManager<O>,
) -> anyhow::Result<()>
where
O: Default,
@@ -384,8 +391,8 @@ where
let name = disp.name.clone();
let disp_handle = cell.disp_handle;
let overlay = create_overlay::<O>(app, name.as_str(), cell)?;
let overlay_id = overlays.add(overlay); // Insert freshly created WayVR overlay into wlx stack
let overlay = OverlayWindowData::from_config(create_overlay(app, name.as_str(), cell)?);
let overlay_id = overlays.add(overlay, app); // Insert freshly created WayVR overlay into wlx stack
data.set_overlay_display_handle(overlay_id, disp_handle);
}
@@ -393,7 +400,10 @@ where
}
#[allow(clippy::too_many_lines)]
pub fn tick_events<O>(app: &mut AppState, overlays: &mut OverlayContainer<O>) -> anyhow::Result<()>
pub fn tick_events<O>(
app: &mut AppState,
overlays: &mut OverlayWindowManager<O>,
) -> anyhow::Result<()>
where
O: Default,
{
@@ -414,8 +424,8 @@ where
.set_display_visible(display_handle, visible);
app.tasks.enqueue(TaskType::Overlay(
OverlaySelector::Id(overlay_id),
Box::new(move |_app, o| {
o.want_visible = visible;
Box::new(move |app, o| {
o.toggle(app);
}),
));
}
@@ -775,28 +785,14 @@ impl OverlayBackend for WayVRBackend {
}
#[allow(dead_code)]
pub fn create_wayvr_display_overlay<O>(
pub fn create_wayvr_display_overlay(
app: &mut state::AppState,
display_width: u16,
display_height: u16,
display_handle: wayvr::display::DisplayHandle,
display_scale: f32,
name: &str,
) -> anyhow::Result<OverlayData<O>>
where
O: Default,
{
let state = OverlayState {
name: format!("WayVR - {name}").into(),
keyboard_focus: Some(KeyboardFocus::WayVR),
want_visible: true,
interactable: true,
grabbable: true,
spawn_scale: display_scale,
spawn_point: vec3a(0.0, -0.1, -1.0),
..Default::default()
};
) -> anyhow::Result<OverlayWindowConfig> {
let wayvr = app.get_wayvr()?;
let backend = Box::new(WayVRBackend::new(
@@ -806,21 +802,36 @@ where
[display_width, display_height],
)?);
Ok(OverlayData {
state,
..OverlayData::from_backend(backend)
Ok(OverlayWindowConfig {
name: format!("WayVR - {name}").into(),
keyboard_focus: Some(KeyboardFocus::WayVR),
default_state: OverlayWindowState {
interactable: true,
grabbable: true,
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * display_scale,
Quat::IDENTITY,
vec3(0.0, -0.1, -1.0),
),
..OverlayWindowState::default()
},
..OverlayWindowConfig::from_backend(backend)
})
}
fn show_display<O>(wayvr: &mut WayVRData, overlays: &mut OverlayContainer<O>, display_name: &str)
where
fn show_display<O>(
wayvr: &mut WayVRData,
overlays: &mut OverlayWindowManager<O>,
app: &mut AppState,
display_name: &str,
) where
O: Default,
{
if let Some(display) = WayVR::get_display_by_name(&wayvr.data.state.displays, display_name) {
if let Some(overlay_id) = wayvr.display_handle_map.get(&display)
&& let Some(overlay) = overlays.mut_by_id(*overlay_id)
{
overlay.state.want_visible = true;
overlay.config.activate(app);
}
wayvr.data.state.set_display_visible(display, true);
@@ -829,7 +840,7 @@ where
fn action_app_click<O>(
app: &mut AppState,
overlays: &mut OverlayContainer<O>,
overlays: &mut OverlayWindowManager<O>,
catalog_name: &Arc<str>,
app_name: &Arc<str>,
) -> anyhow::Result<()>
@@ -884,7 +895,7 @@ where
HashMap::default(),
)?;
show_display::<O>(&mut wayvr, overlays, app_entry.target_display.as_str());
show_display::<O>(&mut wayvr, overlays, app, app_entry.target_display.as_str());
}
}
@@ -893,7 +904,7 @@ where
pub fn action_display_click<O>(
app: &mut AppState,
overlays: &mut OverlayContainer<O>,
overlays: &mut OverlayWindowManager<O>,
display_name: &Arc<str>,
action: &WayVRDisplayClickAction,
) -> anyhow::Result<()>
@@ -922,20 +933,22 @@ where
match action {
WayVRDisplayClickAction::ToggleVisibility => {
// Toggle visibility
overlay.state.want_visible = !overlay.state.want_visible;
overlay.config.toggle(app);
}
WayVRDisplayClickAction::Reset => {
// Show it at the front
overlay.state.want_visible = true;
overlay.state.reset(app, true);
overlay.config.reset(app, true);
}
}
Ok(())
}
pub fn wayvr_action<O>(app: &mut AppState, overlays: &mut OverlayContainer<O>, action: &WayVRAction)
where
pub fn wayvr_action<O>(
app: &mut AppState,
overlays: &mut OverlayWindowManager<O>,
action: &WayVRAction,
) where
O: Default,
{
match action {