mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Warp to cursor hint on constraint unlock
Not the greatest implementation performance-wise but an implementation nonetheless
This commit is contained in:
parent
55a0379697
commit
5679a295d8
2 changed files with 126 additions and 105 deletions
|
@ -166,117 +166,18 @@ impl XwmHandler for State {
|
|||
|
||||
fn unmapped_window(&mut self, _xwm: XwmId, surface: X11Surface) {
|
||||
trace!("XwmHandler::unmapped_window");
|
||||
for output in self.pinnacle.space.outputs() {
|
||||
output.with_state_mut(|state| {
|
||||
state
|
||||
.focus_stack
|
||||
.stack
|
||||
.retain(|win| win.x11_surface().is_some_and(|surf| surf != &surface));
|
||||
});
|
||||
}
|
||||
|
||||
let win = self
|
||||
.pinnacle
|
||||
.space
|
||||
.elements()
|
||||
.find(|win| win.x11_surface() == Some(&surface))
|
||||
.cloned();
|
||||
|
||||
if let Some(win) = win {
|
||||
self.pinnacle
|
||||
.windows
|
||||
.retain(|win| win.x11_surface().is_some_and(|surf| surf != &surface));
|
||||
self.pinnacle
|
||||
.z_index_stack
|
||||
.retain(|win| win.x11_surface().is_some_and(|surf| surf != &surface));
|
||||
|
||||
self.pinnacle.space.unmap_elem(&win);
|
||||
|
||||
if let Some(output) = win.output(&self.pinnacle) {
|
||||
self.pinnacle.request_layout(&output);
|
||||
|
||||
let focus = self
|
||||
.pinnacle
|
||||
.focused_window(&output)
|
||||
.map(KeyboardFocusTarget::Window);
|
||||
|
||||
if let Some(KeyboardFocusTarget::Window(win)) = &focus {
|
||||
self.pinnacle.raise_window(win.clone(), true);
|
||||
if let Some(toplevel) = win.toplevel() {
|
||||
toplevel.send_configure();
|
||||
}
|
||||
}
|
||||
|
||||
self.pinnacle
|
||||
.seat
|
||||
.get_keyboard()
|
||||
.expect("Seat had no keyboard")
|
||||
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
|
||||
|
||||
self.schedule_render(&output);
|
||||
}
|
||||
}
|
||||
|
||||
if !surface.is_override_redirect() {
|
||||
debug!("set mapped to false");
|
||||
surface.set_mapped(false).expect("failed to unmap x11 win");
|
||||
}
|
||||
|
||||
self.remove_xwayland_window(surface);
|
||||
}
|
||||
|
||||
fn destroyed_window(&mut self, _xwm: XwmId, surface: X11Surface) {
|
||||
trace!("XwmHandler::destroyed_window");
|
||||
for output in self.pinnacle.space.outputs() {
|
||||
output.with_state_mut(|state| {
|
||||
state
|
||||
.focus_stack
|
||||
.stack
|
||||
.retain(|win| win.x11_surface().is_some_and(|surf| surf != &surface));
|
||||
});
|
||||
}
|
||||
|
||||
let win = self
|
||||
.pinnacle
|
||||
.windows
|
||||
.iter()
|
||||
.find(|win| win.x11_surface() == Some(&surface))
|
||||
.cloned();
|
||||
|
||||
if let Some(win) = win {
|
||||
debug!("removing x11 window from windows");
|
||||
|
||||
self.pinnacle
|
||||
.windows
|
||||
.retain(|win| win.x11_surface().is_some_and(|surf| surf != &surface));
|
||||
|
||||
self.pinnacle
|
||||
.z_index_stack
|
||||
.retain(|win| win.x11_surface().is_some_and(|surf| surf != &surface));
|
||||
|
||||
if let Some(output) = win.output(&self.pinnacle) {
|
||||
self.pinnacle.request_layout(&output);
|
||||
|
||||
let focus = self
|
||||
.pinnacle
|
||||
.focused_window(&output)
|
||||
.map(KeyboardFocusTarget::Window);
|
||||
|
||||
if let Some(KeyboardFocusTarget::Window(win)) = &focus {
|
||||
self.pinnacle.raise_window(win.clone(), true);
|
||||
if let Some(toplevel) = win.toplevel() {
|
||||
toplevel.send_configure();
|
||||
}
|
||||
}
|
||||
|
||||
self.pinnacle
|
||||
.seat
|
||||
.get_keyboard()
|
||||
.expect("Seat had no keyboard")
|
||||
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
|
||||
|
||||
self.schedule_render(&output);
|
||||
}
|
||||
}
|
||||
debug!("destroyed x11 window");
|
||||
self.remove_xwayland_window(surface);
|
||||
}
|
||||
|
||||
fn configure_request(
|
||||
|
@ -533,6 +434,54 @@ impl XwmHandler for State {
|
|||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn remove_xwayland_window(&mut self, surface: X11Surface) {
|
||||
let win = self
|
||||
.pinnacle
|
||||
.windows
|
||||
.iter()
|
||||
.find(|elem| elem.x11_surface() == Some(&surface))
|
||||
.cloned();
|
||||
|
||||
if let Some(win) = win {
|
||||
debug!("removing x11 window from windows");
|
||||
for output in self.pinnacle.space.outputs() {
|
||||
output.with_state_mut(|state| {
|
||||
state.focus_stack.stack.retain(|w| w != &win);
|
||||
});
|
||||
}
|
||||
|
||||
self.pinnacle.windows.retain(|w| w != &win);
|
||||
|
||||
self.pinnacle.z_index_stack.retain(|w| w != &win);
|
||||
|
||||
if let Some(output) = win.output(&self.pinnacle) {
|
||||
self.pinnacle.request_layout(&output);
|
||||
|
||||
let focus = self
|
||||
.pinnacle
|
||||
.focused_window(&output)
|
||||
.map(KeyboardFocusTarget::Window);
|
||||
|
||||
if let Some(KeyboardFocusTarget::Window(win)) = &focus {
|
||||
self.pinnacle.raise_window(win.clone(), true);
|
||||
if let Some(toplevel) = win.toplevel() {
|
||||
toplevel.send_configure();
|
||||
}
|
||||
}
|
||||
|
||||
self.pinnacle
|
||||
.seat
|
||||
.get_keyboard()
|
||||
.expect("Seat had no keyboard")
|
||||
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
|
||||
|
||||
self.schedule_render(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pinnacle {
|
||||
pub fn fixup_xwayland_window_layering(&mut self) {
|
||||
let Some(xwm) = self.xwm.as_mut() else {
|
||||
|
|
78
src/input.rs
78
src/input.rs
|
@ -91,10 +91,9 @@ impl From<&ModifiersState> for ModifierMask {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct InputState {
|
||||
// TODO: move all of these to config
|
||||
pub reload_keybind: Option<(ModifierMask, Keysym)>,
|
||||
pub kill_keybind: Option<(ModifierMask, Keysym)>,
|
||||
/// All libinput devices that have been connected
|
||||
pub libinput_devices: Vec<input::Device>,
|
||||
|
||||
pub keybinds:
|
||||
HashMap<(ModifierMask, Keysym), UnboundedSender<Result<SetKeybindResponse, tonic::Status>>>,
|
||||
|
@ -102,13 +101,19 @@ pub struct InputState {
|
|||
(ModifierMask, u32, set_mousebind_request::MouseEdge),
|
||||
UnboundedSender<Result<SetMousebindResponse, tonic::Status>>,
|
||||
>,
|
||||
//--------------------------------------------------
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub libinput_settings: HashMap<Discriminant<Setting>, Box<dyn Fn(&mut input::Device) + Send>>,
|
||||
/// All libinput devices that have been connected
|
||||
pub libinput_devices: Vec<input::Device>,
|
||||
|
||||
/// A keyboard focus target stack that is used when there are exclusive keyboard layer
|
||||
/// surfaces. When used, the first item is the previous focus before there were any
|
||||
/// exclusive layer surfaces.
|
||||
// TODO: make that a type or something
|
||||
exclusive_layer_focus_stack: Vec<KeyboardFocusTarget>,
|
||||
|
||||
locked_pointer_position_hint: Option<Point<f64, Logical>>,
|
||||
}
|
||||
|
||||
impl InputState {
|
||||
|
@ -285,7 +290,46 @@ impl State {
|
|||
let location = pointer.current_location();
|
||||
let surface_under = self.pinnacle.pointer_focus_target_under(location);
|
||||
|
||||
if pointer.current_focus().as_ref() == surface_under.as_ref().map(|foc| &foc.0) {
|
||||
// PERF: I'm not really a fan of polling all the time looking for locked pointer
|
||||
// updates, but there doesn't seem to be a great way to get the final cursor
|
||||
// position hint before destruction. I experimented with a
|
||||
// `PointerConstraintsHandler::constraint_destroyed` method but doing so
|
||||
// required threading the state through a bunch of different functions.
|
||||
// Additionally, `PointerConstraintRef::deactivate` gets called in `WlSurface::leave`,
|
||||
// so that would require all compositors implement `PointerConstraintsHandler`
|
||||
// which seems very scuffed.
|
||||
if pointer.current_focus().as_ref() == surface_under.as_ref().map(|s| &s.0) {
|
||||
if let Some((surf, surf_loc)) =
|
||||
surface_under.and_then(|(foc, loc)| Some((foc.wl_surface()?, loc)))
|
||||
{
|
||||
let unlocked = with_pointer_constraint(&surf, &pointer, |constraint| {
|
||||
let Some(constraint) = constraint else {
|
||||
return true;
|
||||
};
|
||||
if !constraint.is_active() {
|
||||
return true;
|
||||
}
|
||||
match &*constraint {
|
||||
PointerConstraint::Confined(_) => true,
|
||||
PointerConstraint::Locked(locked) => {
|
||||
self.pinnacle.input_state.locked_pointer_position_hint =
|
||||
locked.cursor_position_hint();
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if unlocked {
|
||||
if let Some(hint) = self
|
||||
.pinnacle
|
||||
.input_state
|
||||
.locked_pointer_position_hint
|
||||
.take()
|
||||
{
|
||||
self.warp_cursor_to_global_loc(hint + surf_loc.to_f64());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -303,6 +347,34 @@ impl State {
|
|||
pointer.frame(self);
|
||||
}
|
||||
|
||||
/// Warp the cursor to the given `loc` in the global space.
|
||||
///
|
||||
/// This is not handled by [`State::pointer_motion`] because I haven't
|
||||
/// figured out how thread that through yet.
|
||||
pub fn warp_cursor_to_global_loc(&mut self, loc: impl Into<Point<f64, Logical>>) {
|
||||
let Some(pointer) = self.pinnacle.seat.get_pointer() else {
|
||||
return;
|
||||
};
|
||||
let loc: Point<f64, Logical> = loc.into();
|
||||
self.pinnacle.maybe_activate_pointer_constraint(loc);
|
||||
let new_under = self.pinnacle.pointer_focus_target_under(loc);
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
new_under,
|
||||
&MotionEvent {
|
||||
location: loc,
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: Duration::from(self.pinnacle.clock.now()).as_millis() as u32,
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: only on outputs that the ptr left and entered
|
||||
for output in self.pinnacle.space.outputs().cloned().collect::<Vec<_>>() {
|
||||
self.schedule_render(&output);
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboard<I: InputBackend>(&mut self, event: I::KeyboardKeyEvent) {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let time = event.time_msec();
|
||||
|
|
Loading…
Reference in a new issue