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