wvr_server: simplify wl-surface mouse interactions

This commit is contained in:
galister 2026-07-05 13:32:30 +09:00
parent 054d1fd9f4
commit 88493e3ada
7 changed files with 131 additions and 262 deletions

View File

@ -32,6 +32,15 @@ pub struct HoverResult {
pub consume: bool,
}
impl HoverResult {
pub fn consume() -> Self {
HoverResult {
consume: true,
..Default::default()
}
}
}
pub struct TrackedDevice {
pub soc: Option<f32>,
pub charging: bool,

View File

@ -14,7 +14,7 @@ use smithay::{
keyboard::KeyboardHandle,
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerHandle},
},
reexports::wayland_server::{self, Resource, protocol::wl_surface::WlSurface},
reexports::wayland_server::{self, protocol::wl_surface::WlSurface},
utils::{Logical, Point, SerialCounter},
};
use wgui::log::LogErr;
@ -247,12 +247,18 @@ impl WayVRCompositor {
.context("Failed to set keymap")
}
pub fn send_mouse_move_unfocused(&mut self, global_pos: Vec2) {
pub fn send_mouse_move(&mut self, focus: Option<(WlSurface, Vec2)>, global_pos: Vec2) {
let location: Point<f64, Logical> = (global_pos.x as f64, global_pos.y as f64).into();
let focus = focus.map(|(surface, origin)| {
let focus_location: Point<f64, Logical> = (origin.x as f64, origin.y as f64).into();
(surface, focus_location)
});
self.seat_pointer.motion(
&mut self.state,
None,
focus,
&MotionEvent {
location,
serial: self.serial_counter.next_serial(),
@ -263,33 +269,6 @@ impl WayVRCompositor {
self.seat_pointer.frame(&mut self.state);
}
pub fn send_mouse_move_to_surface(
&mut self,
surface: WlSurface,
global_pos: Vec2,
surface_origin: Vec2,
) {
let location: Point<f64, Logical> = (global_pos.x as f64, global_pos.y as f64).into();
let focus_location: Point<f64, Logical> =
(surface_origin.x as f64, surface_origin.y as f64).into();
let serial = self.serial_counter.next_serial();
let time = super::time::get_millis() as u32;
self.seat_pointer.motion(
&mut self.state,
Some((surface, focus_location)),
&MotionEvent {
location,
serial,
time,
},
);
self.seat_pointer.frame(&mut self.state);
}
pub fn send_pointer_button(&mut self, index: super::MouseIndex, pressed: bool) {
let state = if pressed {
ButtonState::Pressed
@ -305,11 +284,6 @@ impl WayVRCompositor {
super::MouseIndex::Right => 0x111,
};
log::trace!(
"pointer button: button={button:#x} pressed={pressed} focus={:?}",
self.seat_pointer.current_focus().map(|s| s.id())
);
self.seat_pointer.button(
&mut self.state,
&ButtonEvent {

View File

@ -63,7 +63,6 @@ use crate::{
wayvr::{
image_importer::ImageImporter,
process::{KillSignal, Process},
window::Window,
},
},
graphics::WGfxExtras,
@ -135,6 +134,12 @@ pub enum MouseIndex {
Right,
}
pub enum PointerFocusTarget {
Surface { surface: WlSurface, origin: Vec2 },
Toplevel,
None,
}
pub enum TickTask {
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
}
@ -644,19 +649,31 @@ impl WvrServerState {
self.manager.seat_pointer.is_grabbed()
}
pub fn send_mouse_move_to_surface(
pub fn send_mouse_move(
&mut self,
surface: WlSurface,
target: PointerFocusTarget,
global_pos: Vec2,
surface_origin: Vec2,
hover_window: window::WindowHandle,
) {
if self.mouse_freeze > Instant::now() {
return;
}
self.manager
.send_mouse_move_to_surface(surface, global_pos, surface_origin);
let focus = match target {
PointerFocusTarget::Surface { surface, origin } => Some((surface, origin)),
PointerFocusTarget::Toplevel => {
let surface = self
.wm
.windows
.get(&hover_window)
.map(|x| x.toplevel.wl_surface().clone());
surface.clone().map(|surface| (surface, Vec2::ZERO))
}
PointerFocusTarget::None => None,
};
self.manager.send_mouse_move(focus, global_pos);
self.mouse_freeze = Instant::now() + Duration::from_millis(1);
@ -667,40 +684,52 @@ impl WvrServerState {
});
}
pub fn send_mouse_button_to_surface(
pub fn send_mouse_button(
&mut self,
surface: WlSurface,
target: PointerFocusTarget,
global_pos: Vec2,
surface_origin: Vec2,
hover_window: window::WindowHandle,
index: MouseIndex,
pressed: bool,
) {
self.manager
.send_mouse_move_to_surface(surface, global_pos, surface_origin);
self.wm.mouse = Some(window::MouseState {
hover_window,
x: global_pos.x as u32,
y: global_pos.y as u32,
});
self.manager.send_pointer_button(index, pressed);
}
pub fn send_mouse_button_to_toplevel(
&mut self,
click_freeze: i32,
hover_window: window::WindowHandle,
global_pos: Vec2,
index: MouseIndex,
pressed: bool,
) {
if pressed {
self.mouse_freeze = Instant::now() + Duration::from_millis(click_freeze.max(0) as u64);
}
self.manager.send_mouse_move_unfocused(global_pos);
let (focus, focus_keyboard) = match target {
PointerFocusTarget::Surface { surface, origin } => (Some((surface, origin)), None),
PointerFocusTarget::Toplevel => {
let surface = self
.wm
.windows
.get(&hover_window)
.map(|x| x.toplevel.wl_surface().clone());
(
surface.clone().map(|surface| (surface, Vec2::ZERO)),
pressed.then_some(surface).flatten(),
)
}
PointerFocusTarget::None => {
let surface = self
.wm
.windows
.get(&hover_window)
.map(|x| x.toplevel.wl_surface().clone());
(None, pressed.then_some(surface).flatten())
}
};
if focus_keyboard.is_some() {
self.manager.seat_keyboard.set_focus(
&mut self.manager.state,
focus_keyboard,
self.manager.serial_counter.next_serial(),
);
}
self.manager.send_mouse_move(focus, global_pos);
self.wm.mouse = Some(window::MouseState {
hover_window,
@ -711,10 +740,10 @@ impl WvrServerState {
self.manager.send_pointer_button(index, pressed);
}
pub fn send_mouse_scroll_to_surface(
pub fn send_mouse_scroll(
&mut self,
global_pos: Vec2,
hover_window: window::WindowHandle,
global_pos: Vec2,
delta: WheelDelta,
) {
self.wm.mouse = Some(window::MouseState {
@ -726,55 +755,6 @@ impl WvrServerState {
self.manager.send_pointer_axis_wheel(delta);
}
pub fn send_mouse_scroll_to_toplevel(
&mut self,
handle: window::WindowHandle,
pos: Vec2,
delta: WheelDelta,
) {
self.wm.mouse = Some(window::MouseState {
hover_window: handle,
x: pos.x.max(0.0) as u32,
y: pos.y.max(0.0) as u32,
});
self.manager.send_pointer_axis_wheel(delta);
}
pub fn send_mouse_move(&mut self, handle: window::WindowHandle, x: u32, y: u32) {
if self.mouse_freeze > Instant::now() {
return;
}
if let Some(window) = self.wm.windows.get_mut(&handle) {
window.send_mouse_move(&mut self.manager, x, y);
} else {
return;
}
self.mouse_freeze = Instant::now() + Duration::from_millis(1); // prevent other pointer from moving the mouse on the same frame
self.wm.mouse = Some(window::MouseState {
hover_window: handle,
x,
y,
});
}
pub fn send_mouse_down(
&mut self,
click_freeze: i32,
handle: window::WindowHandle,
index: MouseIndex,
) {
self.mouse_freeze = Instant::now() + Duration::from_millis(click_freeze as _);
if let Some(window) = self.wm.windows.get_mut(&handle) {
window.send_mouse_down(&mut self.manager, index);
}
}
pub fn send_mouse_up(&mut self, index: MouseIndex) {
Window::send_mouse_up(&mut self.manager, index);
}
pub fn send_key(&mut self, virtual_key: u32, down: bool) {
self.manager.send_key(virtual_key, down);
}

View File

@ -1,16 +1,9 @@
use std::rc::Rc;
use smithay::{
input,
utils::{Logical, Point},
wayland::shell::xdg::ToplevelSurface,
};
use smithay::wayland::shell::xdg::ToplevelSurface;
use wayvr_ipc::packet_server;
use crate::{
backend::wayvr::{client::WayVRCompositor, process},
gen_id,
};
use crate::{backend::wayvr::process, gen_id};
#[derive(Debug)]
pub struct Window {
@ -42,72 +35,6 @@ impl Window {
self.size_x = size_x;
self.size_y = size_y;
}
pub(super) fn send_mouse_move(&self, manager: &mut WayVRCompositor, x: u32, y: u32) {
let surf = self.toplevel.wl_surface().clone();
let point = Point::<f64, Logical>::from((f64::from(x as i32), f64::from(y as i32)));
manager.seat_pointer.motion(
&mut manager.state,
Some((surf, Point::from((0.0, 0.0)))),
&input::pointer::MotionEvent {
serial: manager.serial_counter.next_serial(),
time: 0,
location: point,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
const fn get_mouse_index_number(index: super::MouseIndex) -> u32 {
match index {
super::MouseIndex::Left => 0x110, /* BTN_LEFT */
super::MouseIndex::Center => 0x112, /* BTN_MIDDLE */
super::MouseIndex::Right => 0x111, /* BTN_RIGHT */
}
}
pub(super) fn send_mouse_down(
&mut self,
manager: &mut WayVRCompositor,
index: super::MouseIndex,
) {
let surf = self.toplevel.wl_surface().clone();
// Change keyboard focus to pressed window
manager.seat_keyboard.set_focus(
&mut manager.state,
Some(surf),
manager.serial_counter.next_serial(),
);
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
button: Self::get_mouse_index_number(index),
serial: manager.serial_counter.next_serial(),
time: 0,
state: smithay::backend::input::ButtonState::Pressed,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
pub(super) fn send_mouse_up(manager: &mut WayVRCompositor, index: super::MouseIndex) {
manager.seat_pointer.button(
&mut manager.state,
&input::pointer::ButtonEvent {
button: Self::get_mouse_index_number(index),
serial: manager.serial_counter.next_serial(),
time: 0,
state: smithay::backend::input::ButtonState::Released,
},
);
manager.seat_pointer.frame(&mut manager.state);
}
}
#[derive(Debug)]

View File

@ -313,10 +313,7 @@ impl OverlayBackend for ScreenBackend {
app.hid_provider.inner.mouse_move(pos);
set_next_move(app.session.config.mouse_move_interval_ms as _);
}
HoverResult {
consume: true,
..HoverResult::default()
}
HoverResult::consume()
}
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
let mut btn = match hit.mode {

View File

@ -162,10 +162,7 @@ impl OverlayBackend for MirrorBackend {
}
fn on_hover(&mut self, _: &mut AppState, _: &PointerHit) -> HoverResult {
HoverResult {
consume: true,
..HoverResult::default()
}
HoverResult::consume()
}
fn on_left(&mut self, _: &mut AppState, _: usize) {}
fn on_pointer(&mut self, _: &mut AppState, _: &PointerHit, _: bool) {}

View File

@ -37,7 +37,10 @@ use crate::{
backend::{
XrBackend,
input::{self, HoverResult},
wayvr::{self, SurfaceBufWithImage, process::KillSignal, window::WindowHandle},
wayvr::{
self, PointerFocusTarget, SurfaceBufWithImage, process::KillSignal,
window::WindowHandle,
},
},
graphics::{ExtentExt, Vert2Uv, upload_quad_vertices},
gui::panel::{
@ -131,12 +134,12 @@ enum WvrHitTarget {
Surface {
surface: WlSurface,
global_pos: Vec2,
surface_origin: Vec2,
origin: Vec2,
},
Popup {
surface: WlSurface,
global_pos: Vec2,
surface_origin: Vec2,
origin: Vec2,
},
}
@ -358,7 +361,7 @@ impl WvrWindowBackend {
return Some(WvrHitTarget::Popup {
surface,
global_pos: client_pos,
surface_origin,
origin: surface_origin,
});
}
@ -376,7 +379,7 @@ impl WvrWindowBackend {
return Some(WvrHitTarget::Surface {
surface: surface.surface.clone(),
global_pos: client_pos,
surface_origin: surface.pos,
origin: surface.pos,
});
}
@ -491,8 +494,6 @@ impl OverlayBackend for WvrWindowBackend {
continue;
}
// Same logic you already use. `point` is the popup tree position;
// `geometry().loc` accounts for xdg_surface window geometry offset.
let popup_origin = point - popup.geometry().loc;
popup_roots.push(PopupRoot {
@ -716,10 +717,7 @@ impl OverlayBackend for WvrWindowBackend {
fn on_hover(&mut self, app: &mut state::AppState, hit: &input::PointerHit) -> HoverResult {
if std::mem::take(&mut self.scrolling) {
// we scrolled on previous frame so don't send mouse move events in case the user wants to scroll on this frame as well
return HoverResult {
haptics: None,
consume: true,
};
return HoverResult::consume();
}
match self.hit_target(hit) {
@ -730,12 +728,12 @@ impl OverlayBackend for WvrWindowBackend {
Some(WvrHitTarget::Popup {
surface,
global_pos,
surface_origin,
origin,
})
| Some(WvrHitTarget::Surface {
surface,
global_pos,
surface_origin,
origin,
}) => {
if self.panel_hovered {
self.panel.on_left(app, hit.pointer);
@ -743,18 +741,13 @@ impl OverlayBackend for WvrWindowBackend {
}
let wvr_server = app.wvr_server.as_mut().unwrap();
wvr_server.send_mouse_move_to_surface(
surface,
wvr_server.send_mouse_move(
PointerFocusTarget::Surface { surface, origin },
global_pos,
surface_origin,
self.window,
);
HoverResult {
haptics: None,
consume: true,
}
HoverResult::consume()
}
Some(WvrHitTarget::Toplevel { pos }) => {
if self.panel_hovered {
@ -763,15 +756,11 @@ impl OverlayBackend for WvrWindowBackend {
}
let wvr_server = app.wvr_server.as_mut().unwrap();
wvr_server.send_mouse_move(PointerFocusTarget::Toplevel, pos, self.window);
wvr_server.send_mouse_move(self.window, pos.x as u32, pos.y as u32);
HoverResult {
haptics: None,
consume: true,
}
HoverResult::consume()
}
None => HoverResult::default(),
None => HoverResult::default(), // pass
}
}
@ -789,22 +778,20 @@ impl OverlayBackend for WvrWindowBackend {
let target = self.hit_target(hit);
let outside_pos = self.unclamped_client_pos_from_hit(hit);
let click_freeze = app.session.config.click_freeze_time_ms;
// if the press was consumed to dismiss a popup, consume the matching release too.
if !pressed && self.popup_outside_button == Some(index) {
self.popup_outside_button = None;
let click_freeze = app.session.config.click_freeze_time_ms;
app.wvr_server
.as_mut()
.unwrap()
.send_mouse_button_to_toplevel(
click_freeze,
self.window,
outside_pos,
index,
false,
);
app.wvr_server.as_mut().unwrap().send_mouse_button(
PointerFocusTarget::None,
outside_pos,
self.window,
index,
false,
click_freeze,
);
return;
}
@ -823,16 +810,14 @@ impl OverlayBackend for WvrWindowBackend {
}
let click_freeze = app.session.config.click_freeze_time_ms;
app.wvr_server
.as_mut()
.unwrap()
.send_mouse_button_to_toplevel(
click_freeze,
self.window,
outside_pos,
index,
pressed,
);
app.wvr_server.as_mut().unwrap().send_mouse_button(
PointerFocusTarget::None,
outside_pos,
self.window,
index,
pressed,
click_freeze,
);
return;
}
@ -845,22 +830,22 @@ impl OverlayBackend for WvrWindowBackend {
Some(WvrHitTarget::Popup {
surface,
global_pos,
surface_origin,
origin,
})
| Some(WvrHitTarget::Surface {
surface,
global_pos,
surface_origin,
origin,
}) => {
let wvr_server = app.wvr_server.as_mut().unwrap();
wvr_server.send_mouse_button_to_surface(
surface,
wvr_server.send_mouse_button(
PointerFocusTarget::Surface { surface, origin },
global_pos,
surface_origin,
self.window,
index,
pressed,
click_freeze,
);
}
@ -868,14 +853,14 @@ impl OverlayBackend for WvrWindowBackend {
let click_freeze = app.session.config.click_freeze_time_ms;
let wvr_server = app.wvr_server.as_mut().unwrap();
// normal toplevel click path, only when no popup grab is active.
wvr_server.send_mouse_move(self.window, pos.x as u32, pos.y as u32);
if pressed {
wvr_server.send_mouse_down(click_freeze, self.window, index);
} else {
wvr_server.send_mouse_up(index);
}
wvr_server.send_mouse_button(
PointerFocusTarget::Toplevel,
pos,
self.window,
index,
pressed,
click_freeze,
);
}
None => {}
@ -895,11 +880,11 @@ impl OverlayBackend for WvrWindowBackend {
Some(WvrHitTarget::Popup { global_pos, .. })
| Some(WvrHitTarget::Surface { global_pos, .. }) => {
let wvr_server = app.wvr_server.as_mut().unwrap();
wvr_server.send_mouse_scroll_to_surface(global_pos, self.window, delta);
wvr_server.send_mouse_scroll(self.window, global_pos, delta);
}
Some(WvrHitTarget::Toplevel { pos }) => {
let wvr_server = app.wvr_server.as_mut().unwrap();
wvr_server.send_mouse_scroll_to_toplevel(self.window, pos, delta);
wvr_server.send_mouse_scroll(self.window, pos, delta);
}
None => {}
}