git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/pointer_constraint.rs (9.8K)

  1 use crate::ffi;
  2 use crate::seat::{Seat, Focus};
  3 use crate::server::{WlListener, wl_listener_remove, wl_signal_add};
  4 
  5 #[derive(Clone, Copy)]
  6 pub enum PointerConstraintState {
  7     Inactive,
  8     Active {
  9         node: *mut ffi::wlr_scene_node,
 10         sx: f64,
 11         sy: f64,
 12     },
 13 }
 14 
 15 pub struct PointerConstraint {
 16     pub wlr_constraint: *mut ffi::wlr_pointer_constraint_v1,
 17     pub state: PointerConstraintState,
 18     pub destroy_listener: ffi::wl_listener,
 19     pub commit_listener: ffi::wl_listener,
 20     pub node_destroy_listener: ffi::wl_listener,
 21 }
 22 
 23 impl PointerConstraint {
 24     pub unsafe fn create(wlr_constraint: *mut ffi::wlr_pointer_constraint_v1) -> *mut Self {
 25         let constraint = Box::into_raw(Box::new(Self {
 26             wlr_constraint,
 27             state: PointerConstraintState::Inactive,
 28             destroy_listener: std::mem::zeroed(),
 29             commit_listener: std::mem::zeroed(),
 30             node_destroy_listener: std::mem::zeroed(),
 31         }));
 32 
 33         (*wlr_constraint).data = constraint as *mut _;
 34 
 35         let destroy_listener_ptr = &mut (*constraint).destroy_listener as *mut ffi::wl_listener as *mut WlListener;
 36         (*destroy_listener_ptr).notify = Some(handle_destroy);
 37         
 38         let commit_listener_ptr = &mut (*constraint).commit_listener as *mut ffi::wl_listener as *mut WlListener;
 39         (*commit_listener_ptr).notify = Some(handle_commit);
 40 
 41         let node_destroy_listener_ptr = &mut (*constraint).node_destroy_listener as *mut ffi::wl_listener as *mut WlListener;
 42         (*node_destroy_listener_ptr).notify = Some(handle_node_destroy);
 43 
 44         wl_signal_add(&mut (*wlr_constraint).events.destroy, &mut (*constraint).destroy_listener);
 45         let commit_signal = ffi::river_wlr_surface_get_commit_signal((*wlr_constraint).surface);
 46         wl_signal_add(commit_signal, &mut (*constraint).commit_listener);
 47 
 48         let seat = river_wlr_seat_get_data_safe((*wlr_constraint).seat);
 49         if !seat.is_null() {
 50             if let Focus::LayerSurface(surface) = (*seat).focused {
 51                 if surface == (*wlr_constraint).surface {
 52                     (*seat).cursor.constraint = constraint;
 53                     (*constraint).maybe_activate();
 54                 }
 55             }
 56         }
 57 
 58         constraint
 59     }
 60 
 61     pub unsafe fn maybe_activate(&mut self) {
 62         let seat = river_wlr_seat_get_data_safe((*self.wlr_constraint).seat);
 63         if seat.is_null() {
 64             return;
 65         }
 66 
 67         if (*seat).cursor.constraint != self {
 68             return;
 69         }
 70 
 71         if matches!(self.state, PointerConstraintState::Active { .. }) {
 72             return;
 73         }
 74 
 75         // Retrieve cursor coordinates
 76         let cx = (*seat).cursor.x();
 77         let cy = (*seat).cursor.y();
 78 
 79         let server = (*seat).server;
 80         if let Some(result) = (*server).scene.at(cx, cy) {
 81             if result.surface != (*self.wlr_constraint).surface {
 82                 return;
 83             }
 84 
 85             let sx = result.sx;
 86             let sy = result.sy;
 87 
 88             // Check if within the pixman region
 89             if ffi::pixman_region32_contains_point(&mut (*self.wlr_constraint).region, sx as i32, sy as i32, std::ptr::null_mut()) == 0 {
 90                 return;
 91             }
 92 
 93             self.state = PointerConstraintState::Active {
 94                 node: result.node,
 95                 sx,
 96                 sy,
 97             };
 98 
 99             let destroy_signal = ffi::river_scene_node_get_destroy_signal(result.node);
100             wl_signal_add(destroy_signal, &mut self.node_destroy_listener);
101 
102             log::info!("activating pointer constraint");
103             ffi::wlr_pointer_constraint_v1_send_activated(self.wlr_constraint);
104         }
105     }
106 
107     pub unsafe fn deactivate(&mut self) {
108         if !matches!(self.state, PointerConstraintState::Active { .. }) {
109             return;
110         }
111 
112         self.warp_to_hint_if_set();
113 
114         self.state = PointerConstraintState::Inactive;
115         wl_listener_remove(&mut self.node_destroy_listener);
116         ffi::wlr_pointer_constraint_v1_send_deactivated(self.wlr_constraint);
117     }
118 
119     pub unsafe fn update_state(&mut self) {
120         self.maybe_activate();
121 
122         if let PointerConstraintState::Active { node, sx, sy } = self.state {
123             let seat = river_wlr_seat_get_data_safe((*self.wlr_constraint).seat);
124             if seat.is_null() {
125                 return;
126             }
127 
128             let mut lx = 0;
129             let mut ly = 0;
130             // Get coordinates of scene node relative to layout
131             // In wlroots, scene node coords are retrieved
132             if !wlr_scene_node_coords_safe(node, &mut lx, &mut ly) {
133                 log::info!("deactivating pointer constraint, scene node disabled");
134                 self.deactivate();
135                 return;
136             }
137 
138             let warp_lx = lx as f64 + sx;
139             let warp_ly = ly as f64 + sy;
140 
141             if !ffi::wlr_cursor_warp((*seat).cursor.wlr_cursor, std::ptr::null_mut(), warp_lx, warp_ly) {
142                 log::info!("deactivating pointer constraint, could not warp cursor");
143                 self.deactivate();
144                 return;
145             }
146 
147             if ffi::pixman_region32_contains_point(&mut (*self.wlr_constraint).region, sx as i32, sy as i32, std::ptr::null_mut()) == 0 {
148                 log::info!("deactivating pointer constraint, cursor outside region despite warp");
149                 self.deactivate();
150             }
151         }
152     }
153 
154     pub unsafe fn confine(&mut self, dx: &mut f64, dy: &mut f64) {
155         if let PointerConstraintState::Active { sx, sy, node } = self.state {
156             let mut new_sx = 0.0;
157             let mut new_sy = 0.0;
158 
159             // Call wlroots / pixman confine helper
160             // river does: wlr.region.confine(region, sx, sy, sx + dx, sy + dy, &new_sx, &new_sy)
161             // If the region contains the line segment or we can confine it.
162             // Let's implement confinement using wlroots wlr_region_confine (or similar, or custom simple confinement).
163             // Actually, wlroots has: wlr_region_confine(struct pixman_region32 *region, double sx1, double sy1, double sx2, double sy2, double *ak_sx, double *ak_sy)
164             // Let's call it:
165             if ffi::wlr_region_confine(&mut (*self.wlr_constraint).region, sx, sy, sx + *dx, sy + *dy, &mut new_sx, &mut new_sy) {
166                 *dx = new_sx - sx;
167                 *dy = new_sy - sy;
168 
169                 self.state = PointerConstraintState::Active {
170                     node,
171                     sx: new_sx,
172                     sy: new_sy,
173                 };
174             }
175         }
176     }
177 
178     unsafe fn warp_to_hint_if_set(&mut self) {
179         let seat = river_wlr_seat_get_data_safe((*self.wlr_constraint).seat);
180         if seat.is_null() {
181             return;
182         }
183 
184         if (*self.wlr_constraint).current.cursor_hint.enabled {
185             if let PointerConstraintState::Active { node, .. } = self.state {
186                 let mut lx = 0;
187                 let mut ly = 0;
188                 wlr_scene_node_coords_safe(node, &mut lx, &mut ly);
189 
190                 let sx = (*self.wlr_constraint).current.cursor_hint.x;
191                 let sy = (*self.wlr_constraint).current.cursor_hint.y;
192                 ffi::wlr_cursor_warp((*seat).cursor.wlr_cursor, std::ptr::null_mut(), lx as f64 + sx, ly as f64 + sy);
193                 ffi::wlr_seat_pointer_warp((*seat).wlr_seat, sx, sy);
194             }
195         }
196     }
197 }
198 
199 unsafe fn river_wlr_seat_get_data_safe(wlr_seat: *mut ffi::wlr_seat) -> *mut Seat {
200     if wlr_seat.is_null() {
201         std::ptr::null_mut()
202     } else {
203         ffi::river_wlr_seat_get_data(wlr_seat) as *mut Seat
204     }
205 }
206 
207 unsafe fn wlr_scene_node_coords_safe(node: *mut ffi::wlr_scene_node, x: *mut i32, y: *mut i32) -> bool {
208     if node.is_null() {
209         false
210     } else {
211         ffi::wlr_scene_node_coords(node, x, y)
212     }
213 }
214 
215 unsafe extern "C" fn handle_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
216     let constraint_ptr = crate::container_of!(listener, PointerConstraint, destroy_listener) as *mut PointerConstraint;
217     let constraint = &mut *constraint_ptr;
218     let seat = river_wlr_seat_get_data_safe((*constraint.wlr_constraint).seat);
219 
220     if matches!(constraint.state, PointerConstraintState::Active { .. }) {
221         constraint.warp_to_hint_if_set();
222         wl_listener_remove(&mut constraint.node_destroy_listener);
223     }
224 
225     wl_listener_remove(&mut constraint.destroy_listener);
226     wl_listener_remove(&mut constraint.commit_listener);
227 
228     if !seat.is_null() && (*seat).cursor.constraint == constraint_ptr {
229         (*seat).cursor.constraint = std::ptr::null_mut();
230     }
231 
232     let _boxed = Box::from_raw(constraint_ptr);
233 }
234 
235 unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
236     let constraint_ptr = crate::container_of!(listener, PointerConstraint, commit_listener) as *mut PointerConstraint;
237     let constraint = &mut *constraint_ptr;
238     let seat = river_wlr_seat_get_data_safe((*constraint.wlr_constraint).seat);
239 
240     match constraint.state {
241         PointerConstraintState::Active { sx, sy, .. } => {
242             if ffi::pixman_region32_contains_point(&mut (*constraint.wlr_constraint).region, sx as i32, sy as i32, std::ptr::null_mut()) == 0 {
243                 log::info!("deactivating pointer constraint, input region change left pointer outside");
244                 constraint.deactivate();
245             }
246         }
247         PointerConstraintState::Inactive => {
248             if !seat.is_null() && (*seat).cursor.constraint == constraint_ptr {
249                 constraint.maybe_activate();
250             }
251         }
252     }
253 }
254 
255 unsafe extern "C" fn handle_node_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
256     let constraint_ptr = crate::container_of!(listener, PointerConstraint, node_destroy_listener) as *mut PointerConstraint;
257     log::info!("deactivating pointer constraint, scene node destroyed");
258     (*constraint_ptr).deactivate();
259 }