1 /*
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 <stdbool.h>
29 #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 <unistd.h>
37 #include <errno.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
46 #define NUM_COMPLEX_REGION_RECTS 9
47
48 static bool option_complex_confine_region;
49 static bool option_help;
50
51 struct confine {
52 struct display *display;
53 struct window *window;
54 struct widget *widget;
55
56 cairo_surface_t *buffer;
57
58 struct {
59 int32_t x, y;
60 int32_t old_x, old_y;
61 } line;
62
63 int reset;
64
65 struct input *cursor_timeout_input;
66 struct toytimer cursor_timeout;
67
68 bool pointer_confined;
69
70 bool complex_confine_region_enabled;
71 bool complex_confine_region_dirty;
72 struct rectangle complex_confine_region[NUM_COMPLEX_REGION_RECTS];
73 };
74
75 static void
draw_line(struct confine * confine,cairo_t * cr,struct rectangle * allocation)76 draw_line(struct confine *confine, cairo_t *cr,
77 struct rectangle *allocation)
78 {
79 cairo_t *bcr;
80 cairo_surface_t *tmp_buffer = NULL;
81
82 if (confine->reset) {
83 tmp_buffer = confine->buffer;
84 confine->buffer = NULL;
85 confine->line.x = -1;
86 confine->line.y = -1;
87 confine->line.old_x = -1;
88 confine->line.old_y = -1;
89 confine->reset = 0;
90 }
91
92 if (confine->buffer == NULL) {
93 confine->buffer =
94 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
95 allocation->width,
96 allocation->height);
97 bcr = cairo_create(confine->buffer);
98 cairo_set_source_rgba(bcr, 0, 0, 0, 0);
99 cairo_rectangle(bcr,
100 0, 0,
101 allocation->width, allocation->height);
102 cairo_fill(bcr);
103 }
104 else
105 bcr = cairo_create(confine->buffer);
106
107 if (tmp_buffer) {
108 cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
109 cairo_rectangle(bcr, 0, 0,
110 allocation->width, allocation->height);
111 cairo_clip(bcr);
112 cairo_paint(bcr);
113
114 cairo_surface_destroy(tmp_buffer);
115 }
116
117 if (confine->line.x != -1 && confine->line.y != -1) {
118 if (confine->line.old_x != -1 &&
119 confine->line.old_y != -1) {
120 cairo_set_line_width(bcr, 2.0);
121 cairo_set_source_rgb(bcr, 1, 1, 1);
122 cairo_translate(bcr,
123 -allocation->x, -allocation->y);
124
125 cairo_move_to(bcr,
126 confine->line.old_x,
127 confine->line.old_y);
128 cairo_line_to(bcr,
129 confine->line.x,
130 confine->line.y);
131
132 cairo_stroke(bcr);
133 }
134
135 confine->line.old_x = confine->line.x;
136 confine->line.old_y = confine->line.y;
137 }
138 cairo_destroy(bcr);
139
140 cairo_set_source_surface(cr, confine->buffer,
141 allocation->x, allocation->y);
142 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
143 cairo_rectangle(cr,
144 allocation->x, allocation->y,
145 allocation->width, allocation->height);
146 cairo_clip(cr);
147 cairo_paint(cr);
148 }
149
150 static void
calculate_complex_confine_region(struct confine * confine)151 calculate_complex_confine_region(struct confine *confine)
152 {
153 struct rectangle allocation;
154 int32_t x, y, w, h;
155 struct rectangle *rs = confine->complex_confine_region;
156
157 if (!confine->complex_confine_region_dirty)
158 return;
159
160 widget_get_allocation(confine->widget, &allocation);
161 x = allocation.x;
162 y = allocation.y;
163 w = allocation.width;
164 h = allocation.height;
165
166 /*
167 * The code below constructs a region made up of rectangles that
168 * is then used to set up both an illustrative shaded region in the
169 * widget and a confine region used when confining the pointer.
170 */
171
172 rs[0].x = x + (int)round(w * 0.05);
173 rs[0].y = y + (int)round(h * 0.15);
174 rs[0].width = (int)round(w * 0.35);
175 rs[0].height = (int)round(h * 0.7);
176
177 rs[1].x = rs[0].x + rs[0].width;
178 rs[1].y = y + (int)round(h * 0.45);
179 rs[1].width = (int)round(w * 0.09);
180 rs[1].height = (int)round(h * 0.1);
181
182 rs[2].x = rs[1].x + rs[1].width;
183 rs[2].y = y + (int)round(h * 0.48);
184 rs[2].width = (int)round(w * 0.02);
185 rs[2].height = (int)round(h * 0.04);
186
187 rs[3].x = rs[2].x + rs[2].width;
188 rs[3].y = y + (int)round(h * 0.45);
189 rs[3].width = (int)round(w * 0.09);
190 rs[3].height = (int)round(h * 0.1);
191
192 rs[4].x = rs[3].x + rs[3].width;
193 rs[4].y = y + (int)round(h * 0.15);
194 rs[4].width = (int)round(w * 0.35);
195 rs[4].height = (int)round(h * 0.7);
196
197 rs[5].x = x + (int)round(w * 0.05);
198 rs[5].y = y + (int)round(h * 0.05);
199 rs[5].width = rs[0].width + rs[1].width + rs[2].width +
200 rs[3].width + rs[4].width;
201 rs[5].height = (int)round(h * 0.10);
202
203 rs[6].x = x + (int)round(w * 0.1);
204 rs[6].y = rs[4].y + rs[4].height + (int)round(h * 0.02);
205 rs[6].width = (int)round(w * 0.8);
206 rs[6].height = (int)round(h * 0.03);
207
208 rs[7].x = x + (int)round(w * 0.05);
209 rs[7].y = rs[6].y + rs[6].height;
210 rs[7].width = (int)round(w * 0.9);
211 rs[7].height = (int)round(h * 0.03);
212
213 rs[8].x = x + (int)round(w * 0.1);
214 rs[8].y = rs[7].y + rs[7].height;
215 rs[8].width = (int)round(w * 0.8);
216 rs[8].height = (int)round(h * 0.03);
217
218 confine->complex_confine_region_dirty = false;
219 }
220
221 static void
draw_complex_confine_region_mask(struct confine * confine,cairo_t * cr)222 draw_complex_confine_region_mask(struct confine *confine, cairo_t *cr)
223 {
224 int i;
225
226 calculate_complex_confine_region(confine);
227
228 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
229
230 for (i = 0; i < NUM_COMPLEX_REGION_RECTS; i++) {
231 cairo_rectangle(cr,
232 confine->complex_confine_region[i].x,
233 confine->complex_confine_region[i].y,
234 confine->complex_confine_region[i].width,
235 confine->complex_confine_region[i].height);
236 cairo_set_source_rgba(cr, 0.14, 0.14, 0.14, 0.9);
237 cairo_fill(cr);
238 }
239 }
240
241 static void
redraw_handler(struct widget * widget,void * data)242 redraw_handler(struct widget *widget, void *data)
243 {
244 struct confine *confine = data;
245 cairo_surface_t *surface;
246 cairo_t *cr;
247 struct rectangle allocation;
248
249 widget_get_allocation(confine->widget, &allocation);
250
251 surface = window_get_surface(confine->window);
252
253 cr = cairo_create(surface);
254 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
255 cairo_rectangle(cr,
256 allocation.x,
257 allocation.y,
258 allocation.width,
259 allocation.height);
260 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
261 cairo_fill(cr);
262
263 if (confine->complex_confine_region_enabled) {
264 draw_complex_confine_region_mask(confine, cr);
265 }
266
267 draw_line(confine, cr, &allocation);
268
269 cairo_destroy(cr);
270
271 cairo_surface_destroy(surface);
272 }
273
274 static void
keyboard_focus_handler(struct window * window,struct input * device,void * data)275 keyboard_focus_handler(struct window *window,
276 struct input *device, void *data)
277 {
278 struct confine *confine = data;
279
280 window_schedule_redraw(confine->window);
281 }
282
283 static void
key_handler(struct window * window,struct input * input,uint32_t time,uint32_t key,uint32_t sym,enum wl_keyboard_key_state state,void * data)284 key_handler(struct window *window, struct input *input, uint32_t time,
285 uint32_t key, uint32_t sym,
286 enum wl_keyboard_key_state state, void *data)
287 {
288 struct confine *confine = data;
289
290 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
291 return;
292
293 switch (sym) {
294 case XKB_KEY_Escape:
295 display_exit(confine->display);
296 break;
297 case XKB_KEY_BackSpace:
298 cairo_surface_destroy(confine->buffer);
299 confine->buffer = NULL;
300 window_schedule_redraw(confine->window);
301 break;
302 case XKB_KEY_m:
303 window_set_maximized(confine->window,
304 !window_is_maximized(window));
305 break;
306 }
307 }
308
309 static void
toggle_pointer_confine(struct confine * confine,struct input * input)310 toggle_pointer_confine(struct confine *confine, struct input *input)
311 {
312 if (confine->pointer_confined) {
313 window_unconfine_pointer(confine->window);
314 } else if (confine->complex_confine_region_enabled) {
315 calculate_complex_confine_region(confine);
316 window_confine_pointer_to_rectangles(
317 confine->window,
318 input,
319 confine->complex_confine_region,
320 NUM_COMPLEX_REGION_RECTS);
321
322 } else {
323 window_confine_pointer_to_widget(confine->window,
324 confine->widget,
325 input);
326 }
327
328 confine->pointer_confined = !confine->pointer_confined;
329 }
330
331 static void
button_handler(struct widget * widget,struct input * input,uint32_t time,uint32_t button,enum wl_pointer_button_state state,void * data)332 button_handler(struct widget *widget,
333 struct input *input, uint32_t time,
334 uint32_t button,
335 enum wl_pointer_button_state state, void *data)
336 {
337 struct confine *confine = data;
338 bool is_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
339
340 if (is_pressed && button == BTN_LEFT)
341 toggle_pointer_confine(confine, input);
342 widget_schedule_redraw(widget);
343 }
344
345 static void
cursor_timeout_reset(struct confine * confine)346 cursor_timeout_reset(struct confine *confine)
347 {
348 toytimer_arm_once_usec(&confine->cursor_timeout, 500 * 1000);
349 }
350
351 static int
motion_handler(struct widget * widget,struct input * input,uint32_t time,float x,float y,void * data)352 motion_handler(struct widget *widget,
353 struct input *input, uint32_t time,
354 float x, float y, void *data)
355 {
356 struct confine *confine = data;
357 confine->line.x = x;
358 confine->line.y = y;
359
360 window_schedule_redraw(confine->window);
361
362 cursor_timeout_reset(confine);
363 confine->cursor_timeout_input = input;
364
365 return CURSOR_BLANK;
366 }
367
368 static void
resize_handler(struct widget * widget,int32_t width,int32_t height,void * data)369 resize_handler(struct widget *widget,
370 int32_t width, int32_t height,
371 void *data)
372 {
373 struct confine *confine = data;
374
375 confine->reset = 1;
376
377 if (confine->complex_confine_region_enabled) {
378 confine->complex_confine_region_dirty = true;
379
380 if (confine->pointer_confined) {
381 calculate_complex_confine_region(confine);
382 window_update_confine_rectangles(
383 confine->window,
384 confine->complex_confine_region,
385 NUM_COMPLEX_REGION_RECTS);
386 }
387 }
388 }
389
390 static void
leave_handler(struct widget * widget,struct input * input,void * data)391 leave_handler(struct widget *widget,
392 struct input *input, void *data)
393 {
394 struct confine *confine = data;
395
396 confine->reset = 1;
397 }
398
399 static void
cursor_timeout_func(struct toytimer * tt)400 cursor_timeout_func(struct toytimer *tt)
401 {
402 struct confine *confine =
403 container_of(tt, struct confine, cursor_timeout);
404
405 input_set_pointer_image(confine->cursor_timeout_input,
406 CURSOR_LEFT_PTR);
407 }
408
409 static void
pointer_unconfined(struct window * window,struct input * input,void * data)410 pointer_unconfined(struct window *window, struct input *input, void *data)
411 {
412 struct confine *confine = data;
413
414 confine->pointer_confined = false;
415 }
416
417 static struct confine *
confine_create(struct display * display)418 confine_create(struct display *display)
419 {
420 struct confine *confine;
421
422 confine = xzalloc(sizeof *confine);
423 confine->window = window_create(display);
424 confine->widget = window_frame_create(confine->window, confine);
425 window_set_title(confine->window, "Wayland Confine");
426 confine->display = display;
427 confine->buffer = NULL;
428
429 window_set_key_handler(confine->window, key_handler);
430 window_set_user_data(confine->window, confine);
431 window_set_keyboard_focus_handler(confine->window,
432 keyboard_focus_handler);
433 window_set_pointer_confined_handler(confine->window,
434 NULL,
435 pointer_unconfined);
436
437 widget_set_redraw_handler(confine->widget, redraw_handler);
438 widget_set_button_handler(confine->widget, button_handler);
439 widget_set_motion_handler(confine->widget, motion_handler);
440 widget_set_resize_handler(confine->widget, resize_handler);
441 widget_set_leave_handler(confine->widget, leave_handler);
442
443 widget_schedule_resize(confine->widget, 500, 400);
444 confine->line.x = -1;
445 confine->line.y = -1;
446 confine->line.old_x = -1;
447 confine->line.old_y = -1;
448 confine->reset = 0;
449
450 toytimer_init(&confine->cursor_timeout, CLOCK_MONOTONIC,
451 display, cursor_timeout_func);
452
453 return confine;
454 }
455
456 static void
confine_destroy(struct confine * confine)457 confine_destroy(struct confine *confine)
458 {
459 toytimer_fini(&confine->cursor_timeout);
460 if (confine->buffer)
461 cairo_surface_destroy(confine->buffer);
462 widget_destroy(confine->widget);
463 window_destroy(confine->window);
464 free(confine);
465 }
466
467 static const struct weston_option confine_options[] = {
468 { WESTON_OPTION_BOOLEAN, "complex-confine-region", 0, &option_complex_confine_region },
469 { WESTON_OPTION_BOOLEAN, "help", 0, &option_help },
470 };
471
472 static void
print_help(const char * argv0)473 print_help(const char *argv0)
474 {
475 printf("Usage: %s [--complex-confine-region]\n", argv0);
476 }
477
478 int
main(int argc,char * argv[])479 main(int argc, char *argv[])
480 {
481 struct display *display;
482 struct confine *confine;
483
484 if (parse_options(confine_options,
485 ARRAY_LENGTH(confine_options),
486 &argc, argv) > 1 ||
487 option_help) {
488 print_help(argv[0]);
489 return 0;
490 }
491
492 display = display_create(&argc, argv);
493 if (display == NULL) {
494 fprintf(stderr, "failed to create display: %s\n",
495 strerror(errno));
496 return -1;
497 }
498
499 confine = confine_create(display);
500
501 if (option_complex_confine_region) {
502 confine->complex_confine_region_dirty = true;
503 confine->complex_confine_region_enabled = true;
504 }
505
506 display_run(display);
507
508 confine_destroy(confine);
509 display_destroy(display);
510
511 return 0;
512 }
513