• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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