Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/pointer_binding.rs (5K)
1 // SPDX-FileCopyrightText: © 2026 The River Developers
2 // SPDX-License-Identifier: GPL-3.0-only
3
4 use crate::ffi;
5 use crate::seat::Seat;
6
7 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
8 pub enum PointerBindingStateChange {
9 None,
10 Pressed,
11 Released,
12 }
13
14 pub struct PointerBindingScheduled {
15 pub state_changes: Vec<PointerBindingStateChange>,
16 }
17
18 pub struct PointerBindingRequested {
19 pub enabled: bool,
20 }
21
22 pub struct PointerBinding {
23 pub seat: *mut Seat,
24 pub object: *mut ffi::wl_resource,
25 pub button: u32,
26 pub modifiers: u32,
27 pub wm_scheduled: PointerBindingScheduled,
28 pub wm_requested: PointerBindingRequested,
29 pub sent_pressed: bool,
30 pub link: ffi::wl_list,
31 }
32
33 impl PointerBinding {
34 pub unsafe fn create(
35 seat: *mut Seat,
36 client: *mut ffi::wl_client,
37 version: u32,
38 id: u32,
39 button: u32,
40 modifiers: u32,
41 ) -> Result<(), &'static str> {
42 let binding_ptr = Box::into_raw(Box::new(Self {
43 seat,
44 object: std::ptr::null_mut(),
45 button,
46 modifiers,
47 wm_scheduled: PointerBindingScheduled {
48 state_changes: Vec::new(),
49 },
50 wm_requested: PointerBindingRequested {
51 enabled: false,
52 },
53 sent_pressed: false,
54 link: std::mem::zeroed(),
55 }));
56
57 let resource = ffi::wl_resource_create(client, &ffi::zcce_pointer_binding_v1_interface, version as i32, id);
58 if resource.is_null() {
59 let _ = Box::from_raw(binding_ptr);
60 return Err("wl_resource_create failed");
61 }
62
63 (*binding_ptr).object = resource;
64 ffi::wl_resource_set_implementation(
65 resource,
66 &POINTER_BINDING_INTERFACE as *const _ as *const _,
67 binding_ptr as *mut _,
68 Some(handle_binding_resource_destroy),
69 );
70
71 let pointer_bindings_list = &mut (*seat).pointer_bindings as *mut ffi::wl_list as *mut crate::server::WlList;
72 crate::server::wl_list_insert((*pointer_bindings_list).prev, &mut (*binding_ptr).link as *mut ffi::wl_list as *mut crate::server::WlList);
73
74 log::debug!(
75 "new zcce_pointer_binding_v1: button: {} modifiers: {}",
76 button,
77 modifiers
78 );
79
80 Ok(())
81 }
82
83 pub unsafe fn destroy(binding: *mut Self) {
84 ffi::wl_resource_set_implementation(
85 (*binding).object,
86 std::ptr::null(),
87 std::ptr::null_mut(),
88 None,
89 );
90 handle_binding_resource_destroy((*binding).object);
91 }
92
93 pub unsafe fn pressed(&mut self) {
94 self.wm_scheduled.state_changes.push(PointerBindingStateChange::Pressed);
95 (*(*self.seat).server).wm.dirty_windowing();
96 }
97
98 pub unsafe fn released(&mut self) {
99 self.wm_scheduled.state_changes.push(PointerBindingStateChange::Released);
100 (*(*self.seat).server).wm.dirty_windowing();
101 }
102
103 pub fn match_binding(&self, button: u32, modifiers: u32) -> bool {
104 if !self.wm_requested.enabled {
105 return false;
106 }
107 button == self.button && modifiers == self.modifiers
108 }
109 }
110
111 unsafe extern "C" fn handle_binding_resource_destroy(resource: *mut ffi::wl_resource) {
112 let binding = ffi::wl_resource_get_user_data(resource) as *mut PointerBinding;
113 if !binding.is_null() {
114 // It is possible for the window manager to create duplicate pointer bindings.
115 let cursor = &mut (*(*binding).seat).cursor;
116 if let Some(val) = cursor.pressed.get_mut(&(*binding).button) {
117 if *val == Some(binding) {
118 *val = None;
119 }
120 }
121
122 crate::server::wl_list_remove(&mut (*binding).link as *mut ffi::wl_list as *mut crate::server::WlList);
123 let _ = Box::from_raw(binding);
124 }
125 }
126
127 static POINTER_BINDING_INTERFACE: ffi::zcce_pointer_binding_v1_interface = ffi::zcce_pointer_binding_v1_interface {
128 destroy: Some(pointer_binding_destroy),
129 enable: Some(pointer_binding_enable),
130 disable: Some(pointer_binding_disable),
131 };
132
133 unsafe extern "C" fn pointer_binding_destroy(
134 _client: *mut ffi::wl_client,
135 resource: *mut ffi::wl_resource,
136 ) {
137 ffi::wl_resource_destroy(resource);
138 }
139
140 unsafe extern "C" fn pointer_binding_enable(
141 _client: *mut ffi::wl_client,
142 resource: *mut ffi::wl_resource,
143 ) {
144 let binding = ffi::wl_resource_get_user_data(resource) as *mut PointerBinding;
145 if binding.is_null() {
146 return;
147 }
148 let server = (*(*binding).seat).server;
149 if !(*server).wm.ensure_windowing() {
150 return;
151 }
152 (*binding).wm_requested.enabled = true;
153 }
154
155 unsafe extern "C" fn pointer_binding_disable(
156 _client: *mut ffi::wl_client,
157 resource: *mut ffi::wl_resource,
158 ) {
159 let binding = ffi::wl_resource_get_user_data(resource) as *mut PointerBinding;
160 if binding.is_null() {
161 return;
162 }
163 let server = (*(*binding).seat).server;
164 if !(*server).wm.ensure_windowing() {
165 return;
166 }
167 (*binding).wm_requested.enabled = false;
168 }