mirror of https://github.com/wayvr-org/wayvr.git
wvr_server: context menu mouse fixes
This commit is contained in:
parent
f0f3402650
commit
9b3f315ff1
|
|
@ -3,10 +3,13 @@ use std::{io::Read, os::unix::net::UnixStream, path::PathBuf, sync::Arc};
|
|||
use anyhow::Context;
|
||||
use glam::Vec2;
|
||||
use smithay::{
|
||||
backend::input::Keycode,
|
||||
input::{keyboard::KeyboardHandle, pointer::PointerHandle},
|
||||
reexports::wayland_server::{self, protocol::wl_surface::WlSurface},
|
||||
utils::SerialCounter,
|
||||
backend::input::{ButtonState, Keycode},
|
||||
input::{
|
||||
keyboard::KeyboardHandle,
|
||||
pointer::{ButtonEvent, MotionEvent, PointerHandle},
|
||||
},
|
||||
reexports::wayland_server::{self, Resource, protocol::wl_surface::WlSurface},
|
||||
utils::{Logical, Point, SerialCounter},
|
||||
};
|
||||
use xkbcommon::xkb;
|
||||
|
||||
|
|
@ -211,29 +214,80 @@ impl WayVRCompositor {
|
|||
.context("Failed to set keymap")
|
||||
}
|
||||
|
||||
pub fn send_mouse_move_unfocused(&mut self, global_pos: Vec2) {
|
||||
let location: Point<f64, Logical> = (global_pos.x as f64, global_pos.y as f64).into();
|
||||
|
||||
self.seat_pointer.motion(
|
||||
&mut self.state,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location,
|
||||
serial: self.serial_counter.next_serial(),
|
||||
time: super::time::get_millis() as u32,
|
||||
},
|
||||
);
|
||||
|
||||
self.seat_pointer.frame(&mut self.state);
|
||||
}
|
||||
|
||||
pub fn send_mouse_move_to_surface(
|
||||
&mut self,
|
||||
surface: WlSurface,
|
||||
global_pos: Vec2,
|
||||
surface_origin: Vec2,
|
||||
) {
|
||||
use smithay::input::pointer::MotionEvent;
|
||||
use smithay::utils::{Logical, Point};
|
||||
|
||||
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: self.serial_counter.next_serial(),
|
||||
time: 0,
|
||||
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
|
||||
} else {
|
||||
ButtonState::Released
|
||||
};
|
||||
|
||||
let serial = self.serial_counter.next_serial();
|
||||
let time = super::time::get_millis() as u32;
|
||||
let button = match index {
|
||||
super::MouseIndex::Left => 0x110,
|
||||
super::MouseIndex::Center => 0x112,
|
||||
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 {
|
||||
serial,
|
||||
time,
|
||||
button,
|
||||
state,
|
||||
},
|
||||
);
|
||||
|
||||
self.seat_pointer.frame(&mut self.state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use anyhow::Context;
|
||||
use smithay::backend::allocator::dmabuf::Dmabuf;
|
||||
use smithay::backend::renderer::{BufferType, buffer_type};
|
||||
use smithay::desktop::{PopupKind, PopupManager};
|
||||
use smithay::desktop::{
|
||||
PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy,
|
||||
find_popup_root_surface,
|
||||
};
|
||||
use smithay::input::pointer::Focus;
|
||||
use smithay::input::{Seat, SeatHandler, SeatState};
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::rustix::fs::{OFlags, fcntl_setfl};
|
||||
|
|
@ -400,8 +404,59 @@ impl XdgShellHandler for Application {
|
|||
self.popup_manager.cleanup();
|
||||
}
|
||||
|
||||
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
|
||||
// Handle popup grab here
|
||||
fn grab(&mut self, surface: PopupSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
||||
log::info!(
|
||||
"xdg_popup.grab: surface={:?} serial={:?}",
|
||||
surface.wl_surface().id(),
|
||||
serial
|
||||
);
|
||||
|
||||
let popup = PopupKind::Xdg(surface.clone());
|
||||
|
||||
let Ok(root_surface) = find_popup_root_surface(&popup) else {
|
||||
log::warn!("xdg_popup.grab: could not find popup root surface");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(seat) = Seat::<Application>::from_resource(&seat) else {
|
||||
log::warn!("xdg_popup.grab: unknown seat");
|
||||
return;
|
||||
};
|
||||
|
||||
let root_focus = root_surface;
|
||||
|
||||
let Ok(mut grab) = self
|
||||
.popup_manager
|
||||
.grab_popup::<Application>(root_focus, popup, &seat, serial)
|
||||
else {
|
||||
log::debug!("xdg_popup.grab denied");
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(keyboard) = seat.get_keyboard() {
|
||||
if keyboard.is_grabbed()
|
||||
&& !(keyboard.has_grab(serial)
|
||||
|| keyboard.has_grab(grab.previous_serial().unwrap_or(serial)))
|
||||
{
|
||||
grab.ungrab(PopupUngrabStrategy::All);
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard.set_focus(self, grab.current_grab(), serial);
|
||||
keyboard.set_grab(self, PopupKeyboardGrab::new(&grab), serial);
|
||||
}
|
||||
|
||||
if let Some(pointer) = seat.get_pointer() {
|
||||
if pointer.is_grabbed()
|
||||
&& !(pointer.has_grab(serial)
|
||||
|| pointer.has_grab(grab.previous_serial().unwrap_or_else(|| grab.serial())))
|
||||
{
|
||||
grab.ungrab(PopupUngrabStrategy::All);
|
||||
return;
|
||||
}
|
||||
|
||||
pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, Focus::Keep);
|
||||
}
|
||||
}
|
||||
|
||||
fn reposition_request(
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ pub struct WvrServerState {
|
|||
overlay_to_window: SecondaryMap<OverlayID, window::WindowHandle>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum MouseIndex {
|
||||
Left,
|
||||
Center,
|
||||
|
|
@ -581,6 +582,10 @@ impl WvrServerState {
|
|||
self.window_to_overlay.get(&window).copied()
|
||||
}
|
||||
|
||||
pub fn pointer_is_grabbed(&self) -> bool {
|
||||
self.manager.seat_pointer.is_grabbed()
|
||||
}
|
||||
|
||||
pub fn send_mouse_move_to_surface(
|
||||
&mut self,
|
||||
surface: WlSurface,
|
||||
|
|
@ -604,6 +609,50 @@ impl WvrServerState {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn send_mouse_button_to_surface(
|
||||
&mut self,
|
||||
surface: WlSurface,
|
||||
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_main_surface(
|
||||
&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);
|
||||
|
||||
self.wm.mouse = Some(window::MouseState {
|
||||
hover_window,
|
||||
x: global_pos.x.max(0.0) as u32,
|
||||
y: global_pos.y.max(0.0) as u32,
|
||||
});
|
||||
|
||||
self.manager.send_pointer_button(index, pressed);
|
||||
}
|
||||
|
||||
pub fn send_mouse_move(&mut self, handle: window::WindowHandle, x: u32, y: u32) {
|
||||
if self.mouse_freeze > Instant::now() {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ pub struct WvrWindowBackend {
|
|||
icon: Arc<str>,
|
||||
pipeline: Option<ScreenPipeline>,
|
||||
popups_pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||
popup_outside_button: Option<wayvr::MouseIndex>,
|
||||
interaction_transform: Option<Affine2>,
|
||||
window: WindowHandle,
|
||||
popups: Vec<RenderedPopup>,
|
||||
|
|
@ -230,6 +231,7 @@ impl WvrWindowBackend {
|
|||
window,
|
||||
popups: vec![],
|
||||
popups_pipeline,
|
||||
popup_outside_button: None,
|
||||
interaction_transform: None,
|
||||
just_resumed: false,
|
||||
meta: None,
|
||||
|
|
@ -317,6 +319,11 @@ impl WvrWindowBackend {
|
|||
)
|
||||
}
|
||||
|
||||
fn unclamped_client_pos_from_hit(&self, hit: &input::PointerHit) -> Vec2 {
|
||||
let transformed = self.transformed_uv_from_hit(hit);
|
||||
self.client_pos_from_transformed_uv(transformed)
|
||||
}
|
||||
|
||||
fn is_inside_client_area(&self, transformed: Vec2) -> bool {
|
||||
self.uv_range.contains(&transformed.x) && self.uv_range.contains(&transformed.y)
|
||||
}
|
||||
|
|
@ -724,20 +731,89 @@ impl OverlayBackend for WvrWindowBackend {
|
|||
}
|
||||
|
||||
fn on_pointer(&mut self, app: &mut state::AppState, hit: &input::PointerHit, pressed: bool) {
|
||||
match self.hit_target(hit) {
|
||||
let Some(index) = Self::mouse_index_from_mode(hit.mode) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let target = self.hit_target(hit);
|
||||
let outside_pos = self.unclamped_client_pos_from_hit(hit);
|
||||
|
||||
// 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_main_surface(
|
||||
click_freeze,
|
||||
self.window,
|
||||
outside_pos,
|
||||
index,
|
||||
false,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let popup_grab_active = !self.popups.is_empty()
|
||||
&& app
|
||||
.wvr_server
|
||||
.as_ref()
|
||||
.is_some_and(|server| server.pointer_is_grabbed());
|
||||
|
||||
let outside_grabbed_popup =
|
||||
popup_grab_active && !matches!(&target, Some(WvrHitTarget::Popup { .. }));
|
||||
|
||||
if outside_grabbed_popup {
|
||||
if pressed {
|
||||
self.popup_outside_button = Some(index);
|
||||
}
|
||||
|
||||
let click_freeze = app.session.config.click_freeze_time_ms;
|
||||
app.wvr_server
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_mouse_button_main_surface(
|
||||
click_freeze,
|
||||
self.window,
|
||||
outside_pos,
|
||||
index,
|
||||
pressed,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match target {
|
||||
Some(WvrHitTarget::Panel(hit2)) => {
|
||||
self.panel_hovered = true;
|
||||
self.panel.on_pointer(app, &hit2, pressed);
|
||||
}
|
||||
|
||||
Some(WvrHitTarget::Popup { .. }) | Some(WvrHitTarget::Toplevel { .. }) => {
|
||||
let Some(index) = Self::mouse_index_from_mode(hit.mode) else {
|
||||
return;
|
||||
};
|
||||
Some(WvrHitTarget::Popup {
|
||||
surface,
|
||||
global_pos,
|
||||
surface_origin,
|
||||
}) => {
|
||||
let wvr_server = app.wvr_server.as_mut().unwrap();
|
||||
|
||||
wvr_server.send_mouse_button_to_surface(
|
||||
surface,
|
||||
global_pos,
|
||||
surface_origin,
|
||||
self.window,
|
||||
index,
|
||||
pressed,
|
||||
);
|
||||
}
|
||||
|
||||
Some(WvrHitTarget::Toplevel { pos }) => {
|
||||
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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue