blob: 95e0bdc2e286cf778eab51d704db52b92f9f53a2 [file] [log] [blame]
Jonas Ådahl61831f42016-03-15 18:14:00 +08001/*
2 * Copyright © 2010 Intel Corporation
3 * Copyright © 2012 Collabora, Ltd.
4 * Copyright © 2012 Jonas Ådahl
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26#include "config.h"
27
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +080028#include <stdbool.h>
Jonas Ådahl61831f42016-03-15 18:14:00 +080029#include <stdint.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <cairo.h>
34#include <math.h>
35#include <assert.h>
36#include <sys/timerfd.h>
37#include <sys/epoll.h>
38#include <unistd.h>
39
40#include <linux/input.h>
41#include <wayland-client.h>
42
43#include "window.h"
44#include "shared/helpers.h"
45#include "shared/xalloc.h"
46
47struct confine {
48 struct display *display;
49 struct window *window;
50 struct widget *widget;
51
52 cairo_surface_t *buffer;
53
54 struct {
55 int32_t x, y;
56 int32_t old_x, old_y;
57 } line;
58
59 int reset;
60
61 struct input *cursor_timeout_input;
62 int cursor_timeout_fd;
63 struct task cursor_timeout_task;
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +080064
65 bool pointer_confined;
Jonas Ådahl61831f42016-03-15 18:14:00 +080066};
67
68static void
69draw_line(struct confine *confine, cairo_t *cr,
70 struct rectangle *allocation)
71{
72 cairo_t *bcr;
73 cairo_surface_t *tmp_buffer = NULL;
74
75 if (confine->reset) {
76 tmp_buffer = confine->buffer;
77 confine->buffer = NULL;
78 confine->line.x = -1;
79 confine->line.y = -1;
80 confine->line.old_x = -1;
81 confine->line.old_y = -1;
82 confine->reset = 0;
83 }
84
85 if (confine->buffer == NULL) {
86 confine->buffer =
87 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
88 allocation->width,
89 allocation->height);
90 bcr = cairo_create(confine->buffer);
91 cairo_set_source_rgba(bcr, 0, 0, 0, 0);
92 cairo_rectangle(bcr,
93 0, 0,
94 allocation->width, allocation->height);
95 cairo_fill(bcr);
96 }
97 else
98 bcr = cairo_create(confine->buffer);
99
100 if (tmp_buffer) {
101 cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
102 cairo_rectangle(bcr, 0, 0,
103 allocation->width, allocation->height);
104 cairo_clip(bcr);
105 cairo_paint(bcr);
106
107 cairo_surface_destroy(tmp_buffer);
108 }
109
110 if (confine->line.x != -1 && confine->line.y != -1) {
111 if (confine->line.old_x != -1 &&
112 confine->line.old_y != -1) {
113 cairo_set_line_width(bcr, 2.0);
114 cairo_set_source_rgb(bcr, 1, 1, 1);
115 cairo_translate(bcr,
116 -allocation->x, -allocation->y);
117
118 cairo_move_to(bcr,
119 confine->line.old_x,
120 confine->line.old_y);
121 cairo_line_to(bcr,
122 confine->line.x,
123 confine->line.y);
124
125 cairo_stroke(bcr);
126 }
127
128 confine->line.old_x = confine->line.x;
129 confine->line.old_y = confine->line.y;
130 }
131 cairo_destroy(bcr);
132
133 cairo_set_source_surface(cr, confine->buffer,
134 allocation->x, allocation->y);
135 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
136 cairo_rectangle(cr,
137 allocation->x, allocation->y,
138 allocation->width, allocation->height);
139 cairo_clip(cr);
140 cairo_paint(cr);
141}
142
143static void
144redraw_handler(struct widget *widget, void *data)
145{
146 struct confine *confine = data;
147 cairo_surface_t *surface;
148 cairo_t *cr;
149 struct rectangle allocation;
150
151 widget_get_allocation(confine->widget, &allocation);
152
153 surface = window_get_surface(confine->window);
154
155 cr = cairo_create(surface);
156 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
157 cairo_rectangle(cr,
158 allocation.x,
159 allocation.y,
160 allocation.width,
161 allocation.height);
162 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
163 cairo_fill(cr);
164
165 draw_line(confine, cr, &allocation);
166
167 cairo_destroy(cr);
168
169 cairo_surface_destroy(surface);
170}
171
172static void
173keyboard_focus_handler(struct window *window,
174 struct input *device, void *data)
175{
176 struct confine *confine = data;
177
178 window_schedule_redraw(confine->window);
179}
180
181static void
182key_handler(struct window *window, struct input *input, uint32_t time,
183 uint32_t key, uint32_t sym,
184 enum wl_keyboard_key_state state, void *data)
185{
186 struct confine *confine = data;
187
188 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
189 return;
190
191 switch (sym) {
192 case XKB_KEY_Escape:
193 display_exit(confine->display);
194 break;
195 }
196}
197
198static void
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800199toggle_pointer_confine(struct confine *confine, struct input *input)
200{
201 if (confine->pointer_confined) {
202 window_unconfine_pointer(confine->window);
203 } else {
204 window_confine_pointer_to_widget(confine->window,
205 confine->widget,
206 input);
207 }
208
209 confine->pointer_confined = !confine->pointer_confined;
210}
211
212static void
213button_handler(struct widget *widget,
214 struct input *input, uint32_t time,
215 uint32_t button,
216 enum wl_pointer_button_state state, void *data)
217{
218 struct confine *confine = data;
219 bool is_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
220
221 if (is_pressed && button == BTN_LEFT)
222 toggle_pointer_confine(confine, input);
223 widget_schedule_redraw(widget);
224}
225
226static void
Jonas Ådahl61831f42016-03-15 18:14:00 +0800227cursor_timeout_reset(struct confine *confine)
228{
229 const long cursor_timeout = 500;
230 struct itimerspec its;
231
232 its.it_interval.tv_sec = 0;
233 its.it_interval.tv_nsec = 0;
234 its.it_value.tv_sec = cursor_timeout / 1000;
235 its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000;
236 timerfd_settime(confine->cursor_timeout_fd, 0, &its, NULL);
237}
238
239static int
240motion_handler(struct widget *widget,
241 struct input *input, uint32_t time,
242 float x, float y, void *data)
243{
244 struct confine *confine = data;
245 confine->line.x = x;
246 confine->line.y = y;
247
248 window_schedule_redraw(confine->window);
249
250 cursor_timeout_reset(confine);
251 confine->cursor_timeout_input = input;
252
253 return CURSOR_BLANK;
254}
255
256static void
257resize_handler(struct widget *widget,
258 int32_t width, int32_t height,
259 void *data)
260{
261 struct confine *confine = data;
262
263 confine->reset = 1;
264}
265
266static void
267leave_handler(struct widget *widget,
268 struct input *input, void *data)
269{
270 struct confine *confine = data;
271
272 confine->reset = 1;
273}
274
275static void
276cursor_timeout_func(struct task *task, uint32_t events)
277{
278 struct confine *confine =
279 container_of(task, struct confine, cursor_timeout_task);
280 uint64_t exp;
281
282 if (read(confine->cursor_timeout_fd, &exp, sizeof (uint64_t)) !=
283 sizeof(uint64_t))
284 abort();
285
286 input_set_pointer_image(confine->cursor_timeout_input,
287 CURSOR_LEFT_PTR);
288}
289
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800290static void
291pointer_unconfined(struct window *window, struct input *input, void *data)
292{
293 struct confine *confine = data;
294
295 confine->pointer_confined = false;
296}
297
Jonas Ådahl61831f42016-03-15 18:14:00 +0800298static struct confine *
299confine_create(struct display *display)
300{
301 struct confine *confine;
302
303 confine = xzalloc(sizeof *confine);
304 confine->window = window_create(display);
305 confine->widget = window_frame_create(confine->window, confine);
306 window_set_title(confine->window, "Wayland Confine");
307 confine->display = display;
308 confine->buffer = NULL;
309
310 window_set_key_handler(confine->window, key_handler);
311 window_set_user_data(confine->window, confine);
312 window_set_keyboard_focus_handler(confine->window,
313 keyboard_focus_handler);
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800314 window_set_pointer_confined_handler(confine->window,
315 NULL,
316 pointer_unconfined);
Jonas Ådahl61831f42016-03-15 18:14:00 +0800317
318 widget_set_redraw_handler(confine->widget, redraw_handler);
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800319 widget_set_button_handler(confine->widget, button_handler);
Jonas Ådahl61831f42016-03-15 18:14:00 +0800320 widget_set_motion_handler(confine->widget, motion_handler);
321 widget_set_resize_handler(confine->widget, resize_handler);
322 widget_set_leave_handler(confine->widget, leave_handler);
323
324 widget_schedule_resize(confine->widget, 500, 400);
325 confine->line.x = -1;
326 confine->line.y = -1;
327 confine->line.old_x = -1;
328 confine->line.old_y = -1;
329 confine->reset = 0;
330
331 confine->cursor_timeout_fd =
332 timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
333 confine->cursor_timeout_task.run = cursor_timeout_func;
334 display_watch_fd(window_get_display(confine->window),
335 confine->cursor_timeout_fd,
336 EPOLLIN, &confine->cursor_timeout_task);
337
338 return confine;
339}
340
341static void
342confine_destroy(struct confine *confine)
343{
344 display_unwatch_fd(window_get_display(confine->window),
345 confine->cursor_timeout_fd);
346 close(confine->cursor_timeout_fd);
347 if (confine->buffer)
348 cairo_surface_destroy(confine->buffer);
349 widget_destroy(confine->widget);
350 window_destroy(confine->window);
351 free(confine);
352}
353
354int
355main(int argc, char *argv[])
356{
357 struct display *display;
358 struct confine *confine;
359
360 display = display_create(&argc, argv);
361 if (display == NULL) {
362 fprintf(stderr, "failed to create display: %m\n");
363 return -1;
364 }
365
366 confine = confine_create(display);
367
368 display_run(display);
369
370 confine_destroy(confine);
371 display_destroy(display);
372
373 return 0;
374}