1 /*
2 * Copyright (c) 2020 Andri Yngvason
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <stdint.h>
20 #include <assert.h>
21 #include <wayland-client.h>
22 #include <wayland-cursor.h>
23 #include <linux/input-event-codes.h>
24
25 #include "pointer.h"
26
27 #define STEP_SIZE 15.0
28
29 extern struct wl_shm* wl_shm;
30 extern struct wl_compositor* wl_compositor;
31
pointer_load_cursor_theme(void)32 static struct wl_cursor_theme* pointer_load_cursor_theme(void)
33 {
34 const char* xcursor_theme = getenv("XCURSOR_THEME");
35 const char* xcursor_size_str = getenv("XCURSOR_SIZE");
36
37 unsigned long xcursor_size = 24;
38 if (xcursor_size_str) {
39 char* end;
40 unsigned long size = strtoul(xcursor_size_str, &end, 10);
41 if (!*end && *xcursor_size_str)
42 xcursor_size = size;
43 }
44
45 return wl_cursor_theme_load(xcursor_theme, xcursor_size, wl_shm);
46 }
47
pointer_new(struct wl_pointer * wl_pointer,enum pointer_cursor_type cursor_type)48 struct pointer* pointer_new(struct wl_pointer* wl_pointer,
49 enum pointer_cursor_type cursor_type)
50 {
51 struct pointer* self = calloc(1, sizeof(*self));
52 if (!self)
53 return NULL;
54
55 self->wl_pointer = wl_pointer;
56 self->cursor_type = cursor_type;
57
58 if (cursor_type == POINTER_CURSOR_LEFT_PTR)
59 self->cursor_theme = pointer_load_cursor_theme();
60
61 self->cursor_surface = wl_compositor_create_surface(wl_compositor);
62
63 return self;
64 }
65
pointer_destroy(struct pointer * self)66 void pointer_destroy(struct pointer* self)
67 {
68 wl_pointer_destroy(self->wl_pointer);
69 if (self->cursor_theme)
70 wl_cursor_theme_destroy(self->cursor_theme);
71 wl_surface_destroy(self->cursor_surface);
72 free(self);
73 }
74
pointer_collection_new(enum pointer_cursor_type cursor_type)75 struct pointer_collection* pointer_collection_new(
76 enum pointer_cursor_type cursor_type)
77 {
78 struct pointer_collection* self = calloc(1, sizeof(*self));
79 if (!self)
80 return NULL;
81
82 wl_list_init(&self->pointers);
83 self->cursor_type = cursor_type;
84
85 return self;
86 }
87
pointer_collection_destroy(struct pointer_collection * self)88 void pointer_collection_destroy(struct pointer_collection* self)
89 {
90 struct pointer* pointer;
91 struct pointer* tmp;
92
93 wl_list_for_each_safe(pointer, tmp, &self->pointers, link) {
94 wl_list_remove(&pointer->link);
95 pointer_destroy(pointer);
96 }
97
98 free(self);
99 }
100
pointer_collection_find_wl_pointer(struct pointer_collection * self,struct wl_pointer * wl_pointer)101 struct pointer* pointer_collection_find_wl_pointer(
102 struct pointer_collection* self, struct wl_pointer* wl_pointer)
103 {
104 struct pointer* pointer;
105 wl_list_for_each(pointer, &self->pointers, link)
106 if (pointer->wl_pointer == wl_pointer)
107 return pointer;
108
109 return NULL;
110 }
111
pointer_update_cursor_none(struct pointer * self)112 static void pointer_update_cursor_none(struct pointer* self)
113 {
114 wl_surface_attach(self->cursor_surface, NULL, 0, 0);
115 wl_pointer_set_cursor(self->wl_pointer, self->serial,
116 self->cursor_surface, 0, 0);
117 wl_surface_commit(self->cursor_surface);
118 }
119
pointer_update_cursor_left_ptr(struct pointer * self)120 static void pointer_update_cursor_left_ptr(struct pointer* self)
121 {
122 struct wl_cursor* cursor = wl_cursor_theme_get_cursor(
123 self->cursor_theme, "left_ptr");
124 assert(cursor && cursor->image_count > 0);
125
126 struct wl_cursor_image* image = cursor->images[0];
127
128 // TODO Set buffer scale
129 wl_surface_attach(self->cursor_surface,
130 wl_cursor_image_get_buffer(image), 0, 0);
131 wl_pointer_set_cursor(self->wl_pointer, self->serial,
132 self->cursor_surface, image->hotspot_x,
133 image->hotspot_y);
134 wl_surface_damage_buffer(self->cursor_surface, 0, 0, image->width,
135 image->height);
136 wl_surface_commit(self->cursor_surface);
137 }
138
pointer_update_cursor(struct pointer * self)139 static void pointer_update_cursor(struct pointer* self)
140 {
141 switch (self->cursor_type) {
142 case POINTER_CURSOR_NONE:
143 pointer_update_cursor_none(self);
144 return;
145 case POINTER_CURSOR_LEFT_PTR:
146 pointer_update_cursor_left_ptr(self);
147 return;
148 }
149
150 abort();
151 }
152
pointer_enter(void * data,struct wl_pointer * wl_pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t x,wl_fixed_t y)153 static void pointer_enter(void* data, struct wl_pointer* wl_pointer,
154 uint32_t serial, struct wl_surface* surface, wl_fixed_t x,
155 wl_fixed_t y)
156 {
157 struct pointer_collection* self = data;
158 struct pointer* pointer =
159 pointer_collection_find_wl_pointer(self, wl_pointer);
160 assert(pointer);
161
162 pointer->serial = serial;
163
164 pointer_update_cursor(pointer);
165 }
166
pointer_leave(void * data,struct wl_pointer * wl_pointer,uint32_t serial,struct wl_surface * surface)167 static void pointer_leave(void* data, struct wl_pointer* wl_pointer,
168 uint32_t serial, struct wl_surface* surface)
169 {
170 struct pointer_collection* self = data;
171 struct pointer* pointer =
172 pointer_collection_find_wl_pointer(self, wl_pointer);
173 assert(pointer);
174
175 pointer->serial = serial;
176
177 // Do nothing?
178 }
179
pointer_motion(void * data,struct wl_pointer * wl_pointer,uint32_t t,wl_fixed_t x,wl_fixed_t y)180 static void pointer_motion(void* data, struct wl_pointer* wl_pointer,
181 uint32_t t, wl_fixed_t x, wl_fixed_t y)
182 {
183 struct pointer_collection* self = data;
184 struct pointer* pointer =
185 pointer_collection_find_wl_pointer(self, wl_pointer);
186 assert(pointer);
187
188 pointer->x = x;
189 pointer->y = y;
190 }
191
pointer_set_button_state(struct pointer * self,enum pointer_button_mask button,enum wl_pointer_button_state state)192 static void pointer_set_button_state(struct pointer* self,
193 enum pointer_button_mask button,
194 enum wl_pointer_button_state state)
195 {
196 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
197 self->pressed |= button;
198 } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
199 self->pressed &= ~button;
200 } else {
201 abort();
202 }
203 }
204
pointer_button(void * data,struct wl_pointer * wl_pointer,uint32_t serial,uint32_t t,uint32_t button,enum wl_pointer_button_state state)205 static void pointer_button(void* data, struct wl_pointer* wl_pointer,
206 uint32_t serial, uint32_t t, uint32_t button,
207 enum wl_pointer_button_state state)
208 {
209 struct pointer_collection* self = data;
210 struct pointer* pointer =
211 pointer_collection_find_wl_pointer(self, wl_pointer);
212 assert(pointer);
213
214 pointer->serial = serial;
215
216 switch (button) {
217 case BTN_LEFT:
218 pointer_set_button_state(pointer, POINTER_BUTTON_LEFT, state);
219 break;
220 case BTN_RIGHT:
221 pointer_set_button_state(pointer, POINTER_BUTTON_RIGHT, state);
222 break;
223 case BTN_MIDDLE:
224 pointer_set_button_state(pointer, POINTER_BUTTON_MIDDLE, state);
225 break;
226 // TODO: More buttons
227 }
228 }
229
pointer_axis(void * data,struct wl_pointer * wl_pointer,uint32_t t,enum wl_pointer_axis axis,wl_fixed_t value)230 static void pointer_axis(void* data, struct wl_pointer* wl_pointer, uint32_t t,
231 enum wl_pointer_axis axis, wl_fixed_t value)
232 {
233 struct pointer_collection* self = data;
234 struct pointer* pointer =
235 pointer_collection_find_wl_pointer(self, wl_pointer);
236
237 switch (axis) {
238 case WL_POINTER_AXIS_VERTICAL_SCROLL:
239 pointer->vertical_axis_value += wl_fixed_to_double(value);
240 break;
241 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
242 pointer->horizontal_axis_value += wl_fixed_to_double(value);
243 break;
244 default:;
245 }
246 }
247
pointer_axis_source(void * data,struct wl_pointer * wl_pointer,enum wl_pointer_axis_source source)248 static void pointer_axis_source(void* data, struct wl_pointer* wl_pointer,
249 enum wl_pointer_axis_source source)
250 {
251 // TODO
252 }
253
pointer_axis_stop(void * data,struct wl_pointer * wl_pointer,uint32_t t,enum wl_pointer_axis axis)254 static void pointer_axis_stop(void* data, struct wl_pointer* wl_pointer,
255 uint32_t t, enum wl_pointer_axis axis)
256 {
257 // TODO
258 }
259
pointer_axis_discrete(void * data,struct wl_pointer * wl_pointer,enum wl_pointer_axis axis,int steps)260 static void pointer_axis_discrete(void* data, struct wl_pointer* wl_pointer,
261 enum wl_pointer_axis axis, int steps)
262 {
263 struct pointer_collection* self = data;
264 struct pointer* pointer =
265 pointer_collection_find_wl_pointer(self, wl_pointer);
266
267 switch (axis) {
268 case WL_POINTER_AXIS_VERTICAL_SCROLL:
269 pointer->vertical_scroll_steps += steps;
270 break;
271 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
272 pointer->horizontal_scroll_steps += steps;
273 break;
274 default:;
275 }
276 }
277
pointer_frame(void * data,struct wl_pointer * wl_pointer)278 static void pointer_frame(void* data, struct wl_pointer* wl_pointer)
279 {
280 struct pointer_collection* self = data;
281 struct pointer* pointer =
282 pointer_collection_find_wl_pointer(self, wl_pointer);
283
284 double vertical_steps = trunc(pointer->vertical_axis_value / STEP_SIZE);
285 pointer->vertical_axis_value -= vertical_steps * STEP_SIZE;
286
287 double horizontal_steps = trunc(pointer->horizontal_axis_value / STEP_SIZE);
288 pointer->horizontal_axis_value -= horizontal_steps * STEP_SIZE;
289
290 pointer->vertical_scroll_steps += vertical_steps;
291 pointer->horizontal_scroll_steps += horizontal_steps;
292
293 self->on_frame(self, pointer);
294
295 pointer->vertical_scroll_steps = 0;
296 pointer->horizontal_scroll_steps = 0;
297 }
298
299 static struct wl_pointer_listener pointer_listener = {
300 .enter = pointer_enter,
301 .leave = pointer_leave,
302 .motion = pointer_motion,
303 .button = pointer_button,
304 .axis = pointer_axis,
305 .axis_source = pointer_axis_source,
306 .axis_stop = pointer_axis_stop,
307 .axis_discrete = pointer_axis_discrete,
308 .frame = pointer_frame,
309 };
310
pointer_collection_add_wl_pointer(struct pointer_collection * self,struct wl_pointer * wl_pointer)311 int pointer_collection_add_wl_pointer(struct pointer_collection* self,
312 struct wl_pointer* wl_pointer)
313 {
314 struct pointer* pointer = pointer_new(wl_pointer, self->cursor_type);
315 if (!pointer)
316 return -1;
317
318 wl_list_insert(&self->pointers, &pointer->link);
319 wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener, self);
320 return 0;
321 }
322
pointer_collection_remove_wl_pointer(struct pointer_collection * self,struct wl_pointer * wl_pointer)323 void pointer_collection_remove_wl_pointer(struct pointer_collection* self,
324 struct wl_pointer* wl_pointer)
325 {
326 struct pointer* pointer =
327 pointer_collection_find_wl_pointer(self, wl_pointer);
328 wl_list_remove(&pointer->link);
329 free(pointer);
330 }
331