blob: 17503b3528925f584df843b84694e3fb23a7e92d [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
28#include <stdint.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <cairo.h>
33#include <math.h>
34#include <assert.h>
35#include <sys/timerfd.h>
36#include <sys/epoll.h>
37#include <unistd.h>
38
39#include <linux/input.h>
40#include <wayland-client.h>
41
42#include "window.h"
43#include "shared/helpers.h"
44#include "shared/xalloc.h"
45
46struct confine {
47 struct display *display;
48 struct window *window;
49 struct widget *widget;
50
51 cairo_surface_t *buffer;
52
53 struct {
54 int32_t x, y;
55 int32_t old_x, old_y;
56 } line;
57
58 int reset;
59
60 struct input *cursor_timeout_input;
61 int cursor_timeout_fd;
62 struct task cursor_timeout_task;
63};
64
65static void
66draw_line(struct confine *confine, cairo_t *cr,
67 struct rectangle *allocation)
68{
69 cairo_t *bcr;
70 cairo_surface_t *tmp_buffer = NULL;
71
72 if (confine->reset) {
73 tmp_buffer = confine->buffer;
74 confine->buffer = NULL;
75 confine->line.x = -1;
76 confine->line.y = -1;
77 confine->line.old_x = -1;
78 confine->line.old_y = -1;
79 confine->reset = 0;
80 }
81
82 if (confine->buffer == NULL) {
83 confine->buffer =
84 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
85 allocation->width,
86 allocation->height);
87 bcr = cairo_create(confine->buffer);
88 cairo_set_source_rgba(bcr, 0, 0, 0, 0);
89 cairo_rectangle(bcr,
90 0, 0,
91 allocation->width, allocation->height);
92 cairo_fill(bcr);
93 }
94 else
95 bcr = cairo_create(confine->buffer);
96
97 if (tmp_buffer) {
98 cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
99 cairo_rectangle(bcr, 0, 0,
100 allocation->width, allocation->height);
101 cairo_clip(bcr);
102 cairo_paint(bcr);
103
104 cairo_surface_destroy(tmp_buffer);
105 }
106
107 if (confine->line.x != -1 && confine->line.y != -1) {
108 if (confine->line.old_x != -1 &&
109 confine->line.old_y != -1) {
110 cairo_set_line_width(bcr, 2.0);
111 cairo_set_source_rgb(bcr, 1, 1, 1);
112 cairo_translate(bcr,
113 -allocation->x, -allocation->y);
114
115 cairo_move_to(bcr,
116 confine->line.old_x,
117 confine->line.old_y);
118 cairo_line_to(bcr,
119 confine->line.x,
120 confine->line.y);
121
122 cairo_stroke(bcr);
123 }
124
125 confine->line.old_x = confine->line.x;
126 confine->line.old_y = confine->line.y;
127 }
128 cairo_destroy(bcr);
129
130 cairo_set_source_surface(cr, confine->buffer,
131 allocation->x, allocation->y);
132 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
133 cairo_rectangle(cr,
134 allocation->x, allocation->y,
135 allocation->width, allocation->height);
136 cairo_clip(cr);
137 cairo_paint(cr);
138}
139
140static void
141redraw_handler(struct widget *widget, void *data)
142{
143 struct confine *confine = data;
144 cairo_surface_t *surface;
145 cairo_t *cr;
146 struct rectangle allocation;
147
148 widget_get_allocation(confine->widget, &allocation);
149
150 surface = window_get_surface(confine->window);
151
152 cr = cairo_create(surface);
153 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
154 cairo_rectangle(cr,
155 allocation.x,
156 allocation.y,
157 allocation.width,
158 allocation.height);
159 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
160 cairo_fill(cr);
161
162 draw_line(confine, cr, &allocation);
163
164 cairo_destroy(cr);
165
166 cairo_surface_destroy(surface);
167}
168
169static void
170keyboard_focus_handler(struct window *window,
171 struct input *device, void *data)
172{
173 struct confine *confine = data;
174
175 window_schedule_redraw(confine->window);
176}
177
178static void
179key_handler(struct window *window, struct input *input, uint32_t time,
180 uint32_t key, uint32_t sym,
181 enum wl_keyboard_key_state state, void *data)
182{
183 struct confine *confine = data;
184
185 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
186 return;
187
188 switch (sym) {
189 case XKB_KEY_Escape:
190 display_exit(confine->display);
191 break;
192 }
193}
194
195static void
196cursor_timeout_reset(struct confine *confine)
197{
198 const long cursor_timeout = 500;
199 struct itimerspec its;
200
201 its.it_interval.tv_sec = 0;
202 its.it_interval.tv_nsec = 0;
203 its.it_value.tv_sec = cursor_timeout / 1000;
204 its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000;
205 timerfd_settime(confine->cursor_timeout_fd, 0, &its, NULL);
206}
207
208static int
209motion_handler(struct widget *widget,
210 struct input *input, uint32_t time,
211 float x, float y, void *data)
212{
213 struct confine *confine = data;
214 confine->line.x = x;
215 confine->line.y = y;
216
217 window_schedule_redraw(confine->window);
218
219 cursor_timeout_reset(confine);
220 confine->cursor_timeout_input = input;
221
222 return CURSOR_BLANK;
223}
224
225static void
226resize_handler(struct widget *widget,
227 int32_t width, int32_t height,
228 void *data)
229{
230 struct confine *confine = data;
231
232 confine->reset = 1;
233}
234
235static void
236leave_handler(struct widget *widget,
237 struct input *input, void *data)
238{
239 struct confine *confine = data;
240
241 confine->reset = 1;
242}
243
244static void
245cursor_timeout_func(struct task *task, uint32_t events)
246{
247 struct confine *confine =
248 container_of(task, struct confine, cursor_timeout_task);
249 uint64_t exp;
250
251 if (read(confine->cursor_timeout_fd, &exp, sizeof (uint64_t)) !=
252 sizeof(uint64_t))
253 abort();
254
255 input_set_pointer_image(confine->cursor_timeout_input,
256 CURSOR_LEFT_PTR);
257}
258
259static struct confine *
260confine_create(struct display *display)
261{
262 struct confine *confine;
263
264 confine = xzalloc(sizeof *confine);
265 confine->window = window_create(display);
266 confine->widget = window_frame_create(confine->window, confine);
267 window_set_title(confine->window, "Wayland Confine");
268 confine->display = display;
269 confine->buffer = NULL;
270
271 window_set_key_handler(confine->window, key_handler);
272 window_set_user_data(confine->window, confine);
273 window_set_keyboard_focus_handler(confine->window,
274 keyboard_focus_handler);
275
276 widget_set_redraw_handler(confine->widget, redraw_handler);
277 widget_set_motion_handler(confine->widget, motion_handler);
278 widget_set_resize_handler(confine->widget, resize_handler);
279 widget_set_leave_handler(confine->widget, leave_handler);
280
281 widget_schedule_resize(confine->widget, 500, 400);
282 confine->line.x = -1;
283 confine->line.y = -1;
284 confine->line.old_x = -1;
285 confine->line.old_y = -1;
286 confine->reset = 0;
287
288 confine->cursor_timeout_fd =
289 timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
290 confine->cursor_timeout_task.run = cursor_timeout_func;
291 display_watch_fd(window_get_display(confine->window),
292 confine->cursor_timeout_fd,
293 EPOLLIN, &confine->cursor_timeout_task);
294
295 return confine;
296}
297
298static void
299confine_destroy(struct confine *confine)
300{
301 display_unwatch_fd(window_get_display(confine->window),
302 confine->cursor_timeout_fd);
303 close(confine->cursor_timeout_fd);
304 if (confine->buffer)
305 cairo_surface_destroy(confine->buffer);
306 widget_destroy(confine->widget);
307 window_destroy(confine->window);
308 free(confine);
309}
310
311int
312main(int argc, char *argv[])
313{
314 struct display *display;
315 struct confine *confine;
316
317 display = display_create(&argc, argv);
318 if (display == NULL) {
319 fprintf(stderr, "failed to create display: %m\n");
320 return -1;
321 }
322
323 confine = confine_create(display);
324
325 display_run(display);
326
327 confine_destroy(confine);
328 display_destroy(display);
329
330 return 0;
331}