wvr_server: scale windows based on resolution

This commit is contained in:
galister 2026-07-05 18:14:33 +09:00
parent 2d820f1d1b
commit b2037feacf
5 changed files with 173 additions and 38 deletions

View File

@ -424,12 +424,12 @@ impl WayVRClient {
pub async fn fn_wlx_handsfree(
client: WayVRClientMutex,
params: HandsfreeParams
params: HandsfreeParams,
) -> anyhow::Result<()> {
send_only!(client, &PacketClient::WlxHandsfree(params));
Ok(())
}
pub async fn fn_wlx_modify_panel(
client: WayVRClientMutex,
params: packet_client::WlxModifyPanelParams,

View File

@ -168,7 +168,7 @@ impl Application {
self.redraw_requests.remove(surface_id)
}
fn output_logical_size(&self) -> Size<i32, Logical> {
pub fn output_logical_size(&self) -> Size<i32, Logical> {
self.output
.current_mode()
.map(|mode| Size::new(mode.size.w, mode.size.h))
@ -520,36 +520,60 @@ impl XdgShellHandler for Application {
surface.send_repositioned(token);
}
// If the app wants to be fullscreen, make it think that it's fullscreen.
// If the app requests fullscreen, make it fill the virtual output
fn fullscreen_request(
&mut self,
surface: ToplevelSurface,
_output: Option<wl_output::WlOutput>,
) {
let size = self.output_logical_size();
surface.with_pending_state(|state| {
state.bounds = Some(size);
state.size = Some(size);
state.states.set(xdg_toplevel::State::Fullscreen);
});
surface.send_configure();
}
fn unfullscreen_request(&mut self, surface: ToplevelSurface) {
surface.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
});
surface.send_configure();
}
// If the app wants to be maximized, make it think that it's maximized.
// If the app requests maximize, make it fill the virtual output
fn maximize_request(&mut self, surface: ToplevelSurface) {
let size = self.output_logical_size();
surface.with_pending_state(|state| {
state.bounds = Some(size);
state.size = Some(size);
state.states.set(xdg_toplevel::State::Maximized);
});
surface.send_configure();
}
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
fn unfullscreen_request(&mut self, surface: ToplevelSurface) {
let bounds = self.output_logical_size();
surface.with_pending_state(|state| {
state.bounds = Some(bounds);
state.size = None;
state.states.unset(xdg_toplevel::State::Fullscreen);
});
surface.send_configure();
}
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
let bounds = self.output_logical_size();
surface.with_pending_state(|state| {
state.bounds = Some(bounds);
state.size = None;
state.states.unset(xdg_toplevel::State::Maximized);
});
surface.send_configure();
}
// If the app requests minimize, hide its window
fn minimize_request(&mut self, surface: ToplevelSurface) {
if let Some(client) = surface.wl_surface().client() {

View File

@ -343,28 +343,35 @@ impl WvrServerState {
continue;
};
let output_bounds = wvr_server.manager.state.output_logical_size();
let (min_size, max_size) = with_states(toplevel.wl_surface(), |state| {
let mut guard = state.cached_state.get::<SurfaceCachedState>();
let (mut min_size, mut max_size) =
{ (guard.current().min_size, guard.current().max_size) };
let current = guard.current();
let mut min_size = current.min_size;
let mut max_size = current.max_size;
if min_size.is_empty() {
min_size = Size::new(1, 1);
}
if max_size.is_empty() {
max_size = Size::new(4096, 4096);
max_size = output_bounds;
} else {
max_size = max_size.clamp(Size::new(1, 1), output_bounds);
}
(min_size, max_size)
});
// Size, icon & fallback title comes from process
let (size, pos, fallback_title, icon, is_cage) =
let (fallback_size, pos, fallback_title, icon, is_cage) =
match wvr_server.processes.get(&process_handle) {
Some(Process::Managed(p)) => {
let size: Size<i32, Logical> =
Size::new(p.resolution[0] as _, p.resolution[1] as _);
(
size.clamp(min_size, max_size),
p.pos_mode,
@ -382,6 +389,21 @@ impl WvrServerState {
),
};
let window_handle = wvr_server.wm.create_window(
toplevel.clone(),
process_handle,
fallback_size.w as _,
fallback_size.h as _,
);
toplevel.with_pending_state(|state| {
state.bounds = Some(output_bounds);
// suggest an initial size but let the app request a different one
state.size = Some(fallback_size);
state.states.set(xdg_toplevel::State::Activated);
});
toplevel.send_configure();
let mut title: Arc<str> = fallback_title
.unwrap_or_else(|| format!("P{}", client.pid))
.into();
@ -392,19 +414,6 @@ impl WvrServerState {
SpawnPos::Parent(OverlaySelector::Id(oid))
});
let window_handle = wvr_server.wm.create_window(
toplevel.clone(),
process_handle,
size.w as _,
size.h as _,
);
toplevel.with_pending_state(|state| {
state.size = Some(size);
state.states.set(xdg_toplevel::State::Activated);
state.states.set(xdg_toplevel::State::Maximized);
});
toplevel.send_configure();
let mut icon = icon;
// Try to get title from xdg_toplevel, unless it's running in cage
@ -456,7 +465,7 @@ impl WvrServerState {
app,
window_handle,
icon,
[size.w as _, size.h as _],
[fallback_size.w as _, fallback_size.h as _],
pos,
)
.context("Could not create WvrWindow overlay")

View File

@ -1,5 +1,6 @@
use std::rc::Rc;
use smithay::utils::{Logical, Size};
use smithay::wayland::shell::xdg::ToplevelSurface;
use wayvr_ipc::packet_server;
@ -25,15 +26,33 @@ impl Window {
}
}
pub fn set_size(&mut self, size_x: u32, size_y: u32) {
pub fn configure_size(&mut self, size: Option<Size<i32, Logical>>, bounds: Size<i32, Logical>) {
self.toplevel.with_pending_state(|state| {
//state.bounds = Some((size_x as i32, size_y as i32).into());
state.size = Some((size_x as i32, size_y as i32).into());
state.bounds = Some(bounds);
state.size = size;
});
self.toplevel.send_configure();
if let Some(size) = size {
self.remember_committed_size(size);
}
}
pub fn request_size(&mut self, size: Size<i32, Logical>, bounds: Size<i32, Logical>) {
let size = size.clamp(Size::new(1, 1), bounds);
self.configure_size(Some(size), bounds);
}
pub fn remember_committed_size(&mut self, size: Size<i32, Logical>) -> bool {
let size_x = size.w.max(1) as u32;
let size_y = size.h.max(1) as u32;
let changed = self.size_x != size_x || self.size_y != size_y;
self.size_x = size_x;
self.size_y = size_y;
changed
}
}
@ -78,7 +97,7 @@ impl WindowManager {
size_y: u32,
) -> WindowHandle {
let mut window = Window::new(toplevel, process);
window.set_size(size_x, size_y);
window.remember_committed_size(Size::new(size_x as i32, size_y as i32));
self.windows.add(window)
}

View File

@ -1,8 +1,9 @@
use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3};
use slotmap::Key;
use smithay::{
desktop::PopupManager,
reexports::wayland_server::{Resource, backend::ObjectId, protocol::wl_surface::WlSurface},
utils::{Logical, Point},
utils::{Logical, Point, Size},
wayland::{
compositor::{
SUBSURFACE_ROLE, SubsurfaceCachedState, SurfaceAttributes, TraversalAction,
@ -37,6 +38,7 @@ use crate::{
backend::{
XrBackend,
input::{self, HoverResult},
task::{OverlayTask, TaskType},
wayvr::{
self, PointerFocusTarget, SurfaceBufWithImage, process::KillSignal,
window::WindowHandle,
@ -51,6 +53,7 @@ use crate::{
state::{self, AppState},
subsystem::{hid::WheelDelta, input::KeyboardFocus},
windowing::{
OverlayID, OverlaySelector,
backend::{
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
ui_transform,
@ -75,8 +78,7 @@ pub fn create_wl_window_overlay(
size: [u32; 2],
pos_mode: PositionMode,
) -> anyhow::Result<OverlayWindowConfig> {
let scale = size[0].max(size[1]) as f32 / 1920.0;
let curve_scale = size[0] as f32 / 1920.0;
let (scale, curve_scale) = overlay_scale_from_extent(size);
let z_dist = if matches!(pos_mode, PositionMode::Anchor) {
0.0
@ -106,7 +108,11 @@ pub fn create_wl_window_overlay(
category: OverlayCategory::WayVR,
show_on_spawn: true,
..OverlayWindowConfig::from_backend(Box::new(WvrWindowBackend::new(
name, app, window, icon,
name,
app,
window,
icon,
(scale, curve_scale),
)?))
})
}
@ -167,6 +173,8 @@ pub struct WvrWindowBackend {
uv_range: RangeInclusive<f32>,
panel_hovered: bool,
scrolling: bool,
overlay_id: OverlayID,
scale: (f32, f32),
}
impl WvrWindowBackend {
@ -175,6 +183,7 @@ impl WvrWindowBackend {
app: &mut AppState,
window: wayvr::window::WindowHandle,
icon: Arc<str>,
scale: (f32, f32),
) -> anyhow::Result<Self> {
let subsurface_pipeline = app.gfx.create_pipeline(
app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic
@ -274,10 +283,43 @@ impl WvrWindowBackend {
uv_range: 0.0..=1.0,
panel_hovered: false,
scrolling: false,
overlay_id: OverlayID::null(),
scale,
})
}
fn apply_extent(&mut self, app: &mut AppState, meta: &FrameMeta) -> anyhow::Result<()> {
let (old_scale, old_curve_scale) = self.scale;
let (new_scale, curve_scale) = overlay_scale_from_extent(meta.extent);
self.scale = (new_scale, curve_scale);
if new_scale.abs() > f32::EPSILON {
let scale_delta = new_scale / old_scale;
let curve_scale_delta = curve_scale / old_curve_scale;
if (scale_delta - 1.0).abs() > f32::EPSILON
|| (curve_scale_delta - 1.0).abs() > f32::EPSILON
{
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(self.overlay_id),
Box::new(move |_app, owc| {
if let Some(state) = owc.active_state.as_mut() {
if let Some(curvature) = state.curvature.as_mut() {
*curvature = (curve_scale_delta * *curvature).clamp(0.0, 0.5);
}
if let Some(saved) = state.saved_transform.as_mut() {
saved.matrix3 = saved.matrix3.mul_scalar(scale_delta);
}
state.transform.matrix3 =
state.transform.matrix3.mul_scalar(scale_delta);
}
}),
)));
}
}
self.interaction_transform = Some(ui_transform(meta.extent));
let mut scale = vec2(
@ -441,6 +483,35 @@ impl WvrWindowBackend {
Ok(())
}
fn sync_committed_toplevel_size(
&mut self,
app: &mut AppState,
inner_extent: [u32; 2],
) -> anyhow::Result<()> {
let Some(wvr_server) = app.wvr_server.as_mut() else {
return Ok(());
};
let bounds = wvr_server.manager.state.output_logical_size();
let requested = Size::new(inner_extent[0].max(1) as i32, inner_extent[1].max(1) as i32);
let clamped = requested.clamp(Size::new(1, 1), bounds);
let Some(window) = wvr_server.wm.windows.get_mut(&self.window) else {
return Ok(());
};
if requested == clamped {
// client picked a new size that fits
window.remember_committed_size(requested);
} else {
// client committed something outside the output bounds
if window.size_x != clamped.w as u32 || window.size_y != clamped.h as u32 {
window.request_size(clamped, bounds);
}
}
Ok(())
}
}
impl OverlayBackend for WvrWindowBackend {
@ -556,6 +627,8 @@ impl OverlayBackend for WvrWindowBackend {
}
let inner_extent = meta.extent;
self.sync_committed_toplevel_size(app, inner_extent)?;
meta.extent[0] += BORDER_SIZE * 2;
meta.extent[1] += BORDER_SIZE * 2 + BAR_SIZE;
@ -695,6 +768,7 @@ impl OverlayBackend for WvrWindowBackend {
) -> anyhow::Result<()> {
match event_data {
OverlayEventData::IdAssigned(oid) => {
self.overlay_id = oid;
let wvr_server = app.wvr_server.as_mut().unwrap(); //never None
wvr_server.overlay_added(oid, self.window);
}
@ -1093,3 +1167,12 @@ fn surface_tree_input_hit_test(
hit
}
fn overlay_scale_from_extent(size: [u32; 2]) -> (f32, f32) {
const RELATIVE_SIZE: f32 = 1440.0; // sqrt(1920 * 1080)
let w = size[0].max(1) as f32;
let h = size[1].max(1) as f32;
((w * h).sqrt() / RELATIVE_SIZE, w / 1920.0)
}