blob: 87786af0092d99b5a170af8ce642c0066552e5e8 [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;
Jonas Ådahlcadea762015-05-08 13:54:44 +0800195 case XKB_KEY_BackSpace:
196 cairo_surface_destroy(confine->buffer);
197 confine->buffer = NULL;
198 window_schedule_redraw(confine->window);
199 break;
Jonas Ådahl61831f42016-03-15 18:14:00 +0800200 }
201}
202
203static void
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800204toggle_pointer_confine(struct confine *confine, struct input *input)
205{
206 if (confine->pointer_confined) {
207 window_unconfine_pointer(confine->window);
208 } else {
209 window_confine_pointer_to_widget(confine->window,
210 confine->widget,
211 input);
212 }
213
214 confine->pointer_confined = !confine->pointer_confined;
215}
216
217static void
218button_handler(struct widget *widget,
219 struct input *input, uint32_t time,
220 uint32_t button,
221 enum wl_pointer_button_state state, void *data)
222{
223 struct confine *confine = data;
224 bool is_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
225
226 if (is_pressed && button == BTN_LEFT)
227 toggle_pointer_confine(confine, input);
228 widget_schedule_redraw(widget);
229}
230
231static void
Jonas Ådahl61831f42016-03-15 18:14:00 +0800232cursor_timeout_reset(struct confine *confine)
233{
234 const long cursor_timeout = 500;
235 struct itimerspec its;
236
237 its.it_interval.tv_sec = 0;
238 its.it_interval.tv_nsec = 0;
239 its.it_value.tv_sec = cursor_timeout / 1000;
240 its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000;
241 timerfd_settime(confine->cursor_timeout_fd, 0, &its, NULL);
242}
243
244static int
245motion_handler(struct widget *widget,
246 struct input *input, uint32_t time,
247 float x, float y, void *data)
248{
249 struct confine *confine = data;
250 confine->line.x = x;
251 confine->line.y = y;
252
253 window_schedule_redraw(confine->window);
254
255 cursor_timeout_reset(confine);
256 confine->cursor_timeout_input = input;
257
258 return CURSOR_BLANK;
259}
260
261static void
262resize_handler(struct widget *widget,
263 int32_t width, int32_t height,
264 void *data)
265{
266 struct confine *confine = data;
267
268 confine->reset = 1;
269}
270
271static void
272leave_handler(struct widget *widget,
273 struct input *input, void *data)
274{
275 struct confine *confine = data;
276
277 confine->reset = 1;
278}
279
280static void
281cursor_timeout_func(struct task *task, uint32_t events)
282{
283 struct confine *confine =
284 container_of(task, struct confine, cursor_timeout_task);
285 uint64_t exp;
286
287 if (read(confine->cursor_timeout_fd, &exp, sizeof (uint64_t)) !=
288 sizeof(uint64_t))
289 abort();
290
291 input_set_pointer_image(confine->cursor_timeout_input,
292 CURSOR_LEFT_PTR);
293}
294
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800295static void
296pointer_unconfined(struct window *window, struct input *input, void *data)
297{
298 struct confine *confine = data;
299
300 confine->pointer_confined = false;
301}
302
Jonas Ådahl61831f42016-03-15 18:14:00 +0800303static struct confine *
304confine_create(struct display *display)
305{
306 struct confine *confine;
307
308 confine = xzalloc(sizeof *confine);
309 confine->window = window_create(display);
310 confine->widget = window_frame_create(confine->window, confine);
311 window_set_title(confine->window, "Wayland Confine");
312 confine->display = display;
313 confine->buffer = NULL;
314
315 window_set_key_handler(confine->window, key_handler);
316 window_set_user_data(confine->window, confine);
317 window_set_keyboard_focus_handler(confine->window,
318 keyboard_focus_handler);
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800319 window_set_pointer_confined_handler(confine->window,
320 NULL,
321 pointer_unconfined);
Jonas Ådahl61831f42016-03-15 18:14:00 +0800322
323 widget_set_redraw_handler(confine->widget, redraw_handler);
Jonas Ådahlfdeb2bb2014-11-26 17:47:29 +0800324 widget_set_button_handler(confine->widget, button_handler);
Jonas Ådahl61831f42016-03-15 18:14:00 +0800325 widget_set_motion_handler(confine->widget, motion_handler);
326 widget_set_resize_handler(confine->widget, resize_handler);
327 widget_set_leave_handler(confine->widget, leave_handler);
328
329 widget_schedule_resize(confine->widget, 500, 400);
330 confine->line.x = -1;
331 confine->line.y = -1;
332 confine->line.old_x = -1;
333 confine->line.old_y = -1;
334 confine->reset = 0;
335
336 confine->cursor_timeout_fd =
337 timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
338 confine->cursor_timeout_task.run = cursor_timeout_func;
339 display_watch_fd(window_get_display(confine->window),
340 confine->cursor_timeout_fd,
341 EPOLLIN, &confine->cursor_timeout_task);
342
343 return confine;
344}
345
346static void
347confine_destroy(struct confine *confine)
348{
349 display_unwatch_fd(window_get_display(confine->window),
350 confine->cursor_timeout_fd);
351 close(confine->cursor_timeout_fd);
352 if (confine->buffer)
353 cairo_surface_destroy(confine->buffer);
354 widget_destroy(confine->widget);
355 window_destroy(confine->window);
356 free(confine);
357}
358
359int
360main(int argc, char *argv[])
361{
362 struct display *display;
363 struct confine *confine;
364
365 display = display_create(&argc, argv);
366 if (display == NULL) {
367 fprintf(stderr, "failed to create display: %m\n");
368 return -1;
369 }
370
371 confine = confine_create(display);
372
373 display_run(display);
374
375 confine_destroy(confine);
376 display_destroy(display);
377
378 return 0;
379}