1 /*
2  * Copyright © 2010-2012 Intel Corporation
3  * Copyright © 2011-2012 Collabora, Ltd.
4  * Copyright © 2013 Raspberry Pi Foundation
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 <linux/input.h>
30 
31 #include "shell.h"
32 #include "shared/helpers.h"
33 
34 struct exposay_surface {
35 	struct desktop_shell *shell;
36 	struct exposay_output *eoutput;
37 	struct weston_surface *surface;
38 	struct weston_view *view;
39 	struct wl_listener view_destroy_listener;
40 	struct wl_list link;
41 
42 	int x;
43 	int y;
44 	int width;
45 	int height;
46 	double scale;
47 
48 	int row;
49 	int column;
50 
51 	/* The animations only apply a transformation for their own lifetime,
52 	 * and don't have an option to indefinitely maintain the
53 	 * transformation in a steady state - so, we apply our own once the
54 	 * animation has finished. */
55 	struct weston_transform transform;
56 };
57 
58 static void exposay_set_state(struct desktop_shell *shell,
59                               enum exposay_target_state state,
60 			      struct weston_seat *seat);
61 static void exposay_check_state(struct desktop_shell *shell);
62 
63 static void
exposay_surface_destroy(struct exposay_surface * esurface)64 exposay_surface_destroy(struct exposay_surface *esurface)
65 {
66 	wl_list_remove(&esurface->link);
67 	wl_list_remove(&esurface->view_destroy_listener.link);
68 
69 	if (esurface->shell->exposay.focus_current == esurface->view)
70 		esurface->shell->exposay.focus_current = NULL;
71 	if (esurface->shell->exposay.focus_prev == esurface->view)
72 		esurface->shell->exposay.focus_prev = NULL;
73 
74 	free(esurface);
75 }
76 
77 static void
exposay_in_flight_inc(struct desktop_shell * shell)78 exposay_in_flight_inc(struct desktop_shell *shell)
79 {
80 	shell->exposay.in_flight++;
81 }
82 
83 static void
exposay_in_flight_dec(struct desktop_shell * shell)84 exposay_in_flight_dec(struct desktop_shell *shell)
85 {
86 	if (--shell->exposay.in_flight > 0)
87 		return;
88 
89 	exposay_check_state(shell);
90 }
91 
92 static void
exposay_animate_in_done(struct weston_view_animation * animation,void * data)93 exposay_animate_in_done(struct weston_view_animation *animation, void *data)
94 {
95 	struct exposay_surface *esurface = data;
96 
97 	wl_list_insert(&esurface->view->geometry.transformation_list,
98 	               &esurface->transform.link);
99 	weston_matrix_init(&esurface->transform.matrix);
100 	weston_matrix_scale(&esurface->transform.matrix,
101 	                    esurface->scale, esurface->scale, 1.0f);
102 	weston_matrix_translate(&esurface->transform.matrix,
103 	                        esurface->x - esurface->view->geometry.x,
104 				esurface->y - esurface->view->geometry.y,
105 				0);
106 
107 	weston_view_geometry_dirty(esurface->view);
108 	weston_compositor_schedule_repaint(esurface->view->surface->compositor);
109 
110 	exposay_in_flight_dec(esurface->shell);
111 }
112 
113 static void
exposay_animate_in(struct exposay_surface * esurface)114 exposay_animate_in(struct exposay_surface *esurface)
115 {
116 	exposay_in_flight_inc(esurface->shell);
117 
118 	weston_move_scale_run(esurface->view,
119 	                      esurface->x - esurface->view->geometry.x,
120 	                      esurface->y - esurface->view->geometry.y,
121 			      1.0, esurface->scale, 0,
122 	                      exposay_animate_in_done, esurface);
123 }
124 
125 static void
exposay_animate_out_done(struct weston_view_animation * animation,void * data)126 exposay_animate_out_done(struct weston_view_animation *animation, void *data)
127 {
128 	struct exposay_surface *esurface = data;
129 	struct desktop_shell *shell = esurface->shell;
130 
131 	exposay_surface_destroy(esurface);
132 
133 	exposay_in_flight_dec(shell);
134 }
135 
136 static void
exposay_animate_out(struct exposay_surface * esurface)137 exposay_animate_out(struct exposay_surface *esurface)
138 {
139 	exposay_in_flight_inc(esurface->shell);
140 
141 	/* Remove the static transformation set up by
142 	 * exposay_transform_in_done(). */
143 	wl_list_remove(&esurface->transform.link);
144 	weston_view_geometry_dirty(esurface->view);
145 
146 	weston_move_scale_run(esurface->view,
147 	                      esurface->x - esurface->view->geometry.x,
148 	                      esurface->y - esurface->view->geometry.y,
149 			      1.0, esurface->scale, 1,
150 			      exposay_animate_out_done, esurface);
151 }
152 
153 static void
exposay_highlight_surface(struct desktop_shell * shell,struct exposay_surface * esurface)154 exposay_highlight_surface(struct desktop_shell *shell,
155                           struct exposay_surface *esurface)
156 {
157 	struct weston_view *view = esurface->view;
158 
159 	if (shell->exposay.focus_current == view)
160 		return;
161 
162 	shell->exposay.row_current = esurface->row;
163 	shell->exposay.column_current = esurface->column;
164 	shell->exposay.cur_output = esurface->eoutput;
165 
166 	activate(shell, view, shell->exposay.seat,
167 		 WESTON_ACTIVATE_FLAG_NONE);
168 	shell->exposay.focus_current = view;
169 }
170 
171 static int
exposay_is_animating(struct desktop_shell * shell)172 exposay_is_animating(struct desktop_shell *shell)
173 {
174 	if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
175 	    shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
176 		return 0;
177 
178 	return (shell->exposay.in_flight > 0);
179 }
180 
181 static void
exposay_pick(struct desktop_shell * shell,int x,int y)182 exposay_pick(struct desktop_shell *shell, int x, int y)
183 {
184 	struct exposay_surface *esurface;
185 
186         if (exposay_is_animating(shell))
187             return;
188 
189 	wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
190 		if (x < esurface->x || x > esurface->x + esurface->width)
191 			continue;
192 		if (y < esurface->y || y > esurface->y + esurface->height)
193 			continue;
194 
195 		exposay_highlight_surface(shell, esurface);
196 		return;
197 	}
198 }
199 
200 static void
handle_view_destroy(struct wl_listener * listener,void * data)201 handle_view_destroy(struct wl_listener *listener, void *data)
202 {
203 	struct exposay_surface *esurface = container_of(listener,
204 						 struct exposay_surface,
205 						 view_destroy_listener);
206 
207 	exposay_surface_destroy(esurface);
208 }
209 
210 /* Pretty lame layout for now; just tries to make a square.  Should take
211  * aspect ratio into account really.  Also needs to be notified of surface
212  * addition and removal and adjust layout/animate accordingly. */
213 static enum exposay_layout_state
exposay_layout(struct desktop_shell * shell,struct shell_output * shell_output)214 exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output)
215 {
216 	struct workspace *workspace = shell->exposay.workspace;
217 	struct weston_output *output = shell_output->output;
218 	struct exposay_output *eoutput = &shell_output->eoutput;
219 	struct weston_view *view;
220 	struct exposay_surface *esurface, *highlight = NULL;
221 	int w, h;
222 	int i;
223 	int last_row_removed = 0;
224 
225 	eoutput->num_surfaces = 0;
226 	wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
227 		if (!get_shell_surface(view->surface))
228 			continue;
229 		if (view->output != output)
230 			continue;
231 		eoutput->num_surfaces++;
232 	}
233 
234 	if (eoutput->num_surfaces == 0) {
235 		eoutput->grid_size = 0;
236 		eoutput->hpadding_outer = 0;
237 		eoutput->vpadding_outer = 0;
238 		eoutput->padding_inner = 0;
239 		eoutput->surface_size = 0;
240 		return EXPOSAY_LAYOUT_OVERVIEW;
241 	}
242 
243 	/* Lay the grid out as square as possible, losing surfaces from the
244 	 * bottom row if required.  Start with fixed padding of a 10% margin
245 	 * around the outside and 80px internal padding between surfaces, and
246 	 * maximise the area made available to surfaces after this, but only
247 	 * to a maximum of 1/3rd the total output size.
248 	 *
249 	 * If we can't make a square grid, add one extra row at the bottom
250 	 * which will have a smaller number of columns.
251 	 *
252 	 * XXX: Surely there has to be a better way to express this maths,
253 	 *      right?!
254 	 */
255 	eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces));
256 	if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces)
257 		eoutput->grid_size++;
258 	last_row_removed = pow(eoutput->grid_size, 2) - eoutput->num_surfaces;
259 
260 	eoutput->hpadding_outer = (output->width / 10);
261 	eoutput->vpadding_outer = (output->height / 10);
262 	eoutput->padding_inner = 80;
263 
264 	w = output->width - (eoutput->hpadding_outer * 2);
265 	w -= eoutput->padding_inner * (eoutput->grid_size - 1);
266 	w /= eoutput->grid_size;
267 
268 	h = output->height - (eoutput->vpadding_outer * 2);
269 	h -= eoutput->padding_inner * (eoutput->grid_size - 1);
270 	h /= eoutput->grid_size;
271 
272 	eoutput->surface_size = (w < h) ? w : h;
273 	if (eoutput->surface_size > (output->width / 2))
274 		eoutput->surface_size = output->width / 2;
275 	if (eoutput->surface_size > (output->height / 2))
276 		eoutput->surface_size = output->height / 2;
277 
278 	i = 0;
279 	wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
280 		int pad;
281 
282 		pad = eoutput->surface_size + eoutput->padding_inner;
283 
284 		if (!get_shell_surface(view->surface))
285 			continue;
286 		if (view->output != output)
287 			continue;
288 
289 		esurface = malloc(sizeof(*esurface));
290 		if (!esurface) {
291 			exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
292 			                  shell->exposay.seat);
293 			break;
294 		}
295 
296 		wl_list_insert(&shell->exposay.surface_list, &esurface->link);
297 		esurface->shell = shell;
298 		esurface->eoutput = eoutput;
299 		esurface->view = view;
300 
301 		esurface->row = i / eoutput->grid_size;
302 		esurface->column = i % eoutput->grid_size;
303 
304 		esurface->x = output->x + eoutput->hpadding_outer;
305 		esurface->x += pad * esurface->column;
306 		esurface->y = output->y + eoutput->vpadding_outer;
307 		esurface->y += pad * esurface->row;
308 
309 		if (esurface->row == eoutput->grid_size - 1)
310 			esurface->x += (eoutput->surface_size + eoutput->padding_inner) * last_row_removed / 2;
311 
312 		if (view->surface->width > view->surface->height)
313 			esurface->scale = eoutput->surface_size / (float) view->surface->width;
314 		else
315 			esurface->scale = eoutput->surface_size / (float) view->surface->height;
316 		esurface->width = view->surface->width * esurface->scale;
317 		esurface->height = view->surface->height * esurface->scale;
318 
319 		if (shell->exposay.focus_current == esurface->view)
320 			highlight = esurface;
321 
322 		exposay_animate_in(esurface);
323 
324 		/* We want our destroy handler to be after the animation
325 		 * destroy handler in the list, this way when the view is
326 		 * destroyed, the animation can safely call the animation
327 		 * completion callback before we free the esurface in our
328 		 * destroy handler.
329 		 */
330 		esurface->view_destroy_listener.notify = handle_view_destroy;
331 		wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener);
332 
333 		i++;
334 	}
335 
336 	if (highlight) {
337 		shell->exposay.focus_current = NULL;
338 		exposay_highlight_surface(shell, highlight);
339 	}
340 
341 	weston_compositor_schedule_repaint(shell->compositor);
342 
343 	return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
344 }
345 
346 static void
exposay_focus(struct weston_pointer_grab * grab)347 exposay_focus(struct weston_pointer_grab *grab)
348 {
349 }
350 
351 static void
exposay_motion(struct weston_pointer_grab * grab,const struct timespec * time,struct weston_pointer_motion_event * event)352 exposay_motion(struct weston_pointer_grab *grab,
353 	       const struct timespec *time,
354 	       struct weston_pointer_motion_event *event)
355 {
356 	struct desktop_shell *shell =
357 		container_of(grab, struct desktop_shell, exposay.grab_ptr);
358 
359 	weston_pointer_move(grab->pointer, event);
360 
361 	exposay_pick(shell,
362 	             wl_fixed_to_int(grab->pointer->x),
363 	             wl_fixed_to_int(grab->pointer->y));
364 }
365 
366 static void
exposay_button(struct weston_pointer_grab * grab,const struct timespec * time,uint32_t button,uint32_t state_w)367 exposay_button(struct weston_pointer_grab *grab, const struct timespec *time,
368 	       uint32_t button, uint32_t state_w)
369 {
370 	struct desktop_shell *shell =
371 		container_of(grab, struct desktop_shell, exposay.grab_ptr);
372 	struct weston_seat *seat = grab->pointer->seat;
373 	enum wl_pointer_button_state state = state_w;
374 
375 	if (button != BTN_LEFT)
376 		return;
377 
378 	/* Store the surface we clicked on, and don't do anything if we end up
379 	 * releasing on a different surface. */
380 	if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
381 		shell->exposay.clicked = shell->exposay.focus_current;
382 		return;
383 	}
384 
385 	if (shell->exposay.focus_current == shell->exposay.clicked)
386 		exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
387 	else
388 		shell->exposay.clicked = NULL;
389 }
390 
391 static void
exposay_axis(struct weston_pointer_grab * grab,const struct timespec * time,struct weston_pointer_axis_event * event)392 exposay_axis(struct weston_pointer_grab *grab,
393 	     const struct timespec *time,
394 	     struct weston_pointer_axis_event *event)
395 {
396 }
397 
398 static void
exposay_axis_source(struct weston_pointer_grab * grab,uint32_t source)399 exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source)
400 {
401 }
402 
403 static void
exposay_frame(struct weston_pointer_grab * grab)404 exposay_frame(struct weston_pointer_grab *grab)
405 {
406 }
407 
408 static void
exposay_pointer_grab_cancel(struct weston_pointer_grab * grab)409 exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
410 {
411 	struct desktop_shell *shell =
412 		container_of(grab, struct desktop_shell, exposay.grab_ptr);
413 
414 	exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
415 }
416 
417 static const struct weston_pointer_grab_interface exposay_ptr_grab = {
418 	exposay_focus,
419 	exposay_motion,
420 	exposay_button,
421 	exposay_axis,
422 	exposay_axis_source,
423 	exposay_frame,
424 	exposay_pointer_grab_cancel,
425 };
426 
427 static int
exposay_maybe_move(struct desktop_shell * shell,int row,int column)428 exposay_maybe_move(struct desktop_shell *shell, int row, int column)
429 {
430 	struct exposay_surface *esurface;
431 
432 	wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
433 		if (esurface->eoutput != shell->exposay.cur_output ||
434 		    esurface->row != row || esurface->column != column)
435 			continue;
436 
437 		exposay_highlight_surface(shell, esurface);
438 		return 1;
439 	}
440 
441 	return 0;
442 }
443 
444 static void
exposay_key(struct weston_keyboard_grab * grab,const struct timespec * time,uint32_t key,uint32_t state_w)445 exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time,
446 	    uint32_t key, uint32_t state_w)
447 {
448 	struct weston_seat *seat = grab->keyboard->seat;
449 	struct desktop_shell *shell =
450 		container_of(grab, struct desktop_shell, exposay.grab_kbd);
451 	enum wl_keyboard_key_state state = state_w;
452 
453 	if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
454 		return;
455 
456 	switch (key) {
457 	case KEY_ESC:
458 		exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
459 		break;
460 	case KEY_ENTER:
461 		exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
462 		break;
463 	case KEY_UP:
464 		exposay_maybe_move(shell, shell->exposay.row_current - 1,
465 		                   shell->exposay.column_current);
466 		break;
467 	case KEY_DOWN:
468 		/* Special case for trying to move to the bottom row when it
469 		 * has fewer items than all the others. */
470 		if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
471 		                        shell->exposay.column_current) &&
472 		    shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) {
473 			exposay_maybe_move(shell, shell->exposay.row_current + 1,
474 					   (shell->exposay.cur_output->num_surfaces %
475 					    shell->exposay.cur_output->grid_size) - 1);
476 		}
477 		break;
478 	case KEY_LEFT:
479 		exposay_maybe_move(shell, shell->exposay.row_current,
480 		                   shell->exposay.column_current - 1);
481 		break;
482 	case KEY_RIGHT:
483 		exposay_maybe_move(shell, shell->exposay.row_current,
484 		                   shell->exposay.column_current + 1);
485 		break;
486 	case KEY_TAB:
487 		/* Try to move right, then down (and to the leftmost column),
488 		 * then if all else fails, to the top left. */
489 		if (!exposay_maybe_move(shell, shell->exposay.row_current,
490 					shell->exposay.column_current + 1) &&
491 		    !exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
492 			exposay_maybe_move(shell, 0, 0);
493 		break;
494 	default:
495 		break;
496 	}
497 }
498 
499 static void
exposay_modifier(struct weston_keyboard_grab * grab,uint32_t serial,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)500 exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
501                  uint32_t mods_depressed, uint32_t mods_latched,
502                  uint32_t mods_locked, uint32_t group)
503 {
504 	struct desktop_shell *shell =
505 		container_of(grab, struct desktop_shell, exposay.grab_kbd);
506 	struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
507 
508 	/* We want to know when mod has been pressed and released.
509 	 * FIXME: There is a problem here: if mod is pressed, then a key
510 	 * is pressed and released, then mod is released, we will treat that
511 	 * as if only mod had been pressed and released. */
512 	if (seat->modifier_state) {
513 		if (seat->modifier_state == shell->binding_modifier) {
514 			shell->exposay.mod_pressed = true;
515 		} else {
516 			shell->exposay.mod_invalid = true;
517 		}
518 	} else {
519 		if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
520 			exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
521 
522 		shell->exposay.mod_invalid = false;
523 		shell->exposay.mod_pressed = false;
524 	}
525 
526 	return;
527 }
528 
529 static void
exposay_cancel(struct weston_keyboard_grab * grab)530 exposay_cancel(struct weston_keyboard_grab *grab)
531 {
532 	struct desktop_shell *shell =
533 		container_of(grab, struct desktop_shell, exposay.grab_kbd);
534 
535 	exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
536 }
537 
538 static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
539 	exposay_key,
540 	exposay_modifier,
541 	exposay_cancel,
542 };
543 
544 /**
545  * Called when the transition from overview -> inactive has completed.
546  */
547 static enum exposay_layout_state
exposay_set_inactive(struct desktop_shell * shell)548 exposay_set_inactive(struct desktop_shell *shell)
549 {
550 	struct weston_seat *seat = shell->exposay.seat;
551 	struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
552 	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
553 
554 	if (pointer)
555 		weston_pointer_end_grab(pointer);
556 
557 	if (keyboard) {
558 		weston_keyboard_end_grab(keyboard);
559 		if (keyboard->input_method_resource)
560 			keyboard->grab = &keyboard->input_method_grab;
561 	}
562 
563 	return EXPOSAY_LAYOUT_INACTIVE;
564 }
565 
566 /**
567  * Begins the transition from overview to inactive. */
568 static enum exposay_layout_state
exposay_transition_inactive(struct desktop_shell * shell,int switch_focus)569 exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
570 {
571 	struct exposay_surface *esurface;
572 
573 	/* Call activate() before we start the animations to avoid
574 	 * animating back the old state and then immediately transitioning
575 	 * to the new. */
576 	if (switch_focus && shell->exposay.focus_current)
577 		activate(shell, shell->exposay.focus_current,
578 		         shell->exposay.seat,
579 			 WESTON_ACTIVATE_FLAG_CONFIGURE);
580 	else if (shell->exposay.focus_prev)
581 		activate(shell, shell->exposay.focus_prev,
582 		         shell->exposay.seat,
583 			 WESTON_ACTIVATE_FLAG_CONFIGURE);
584 
585 	wl_list_for_each(esurface, &shell->exposay.surface_list, link)
586 		exposay_animate_out(esurface);
587 	weston_compositor_schedule_repaint(shell->compositor);
588 
589 	return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
590 }
591 
592 static enum exposay_layout_state
exposay_transition_active(struct desktop_shell * shell)593 exposay_transition_active(struct desktop_shell *shell)
594 {
595 	struct weston_seat *seat = shell->exposay.seat;
596 	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
597 	struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
598 	struct shell_output *shell_output;
599 	bool animate = false;
600 
601 	shell->exposay.workspace = get_current_workspace(shell);
602 	shell->exposay.focus_prev = get_default_view(keyboard->focus);
603 	shell->exposay.focus_current = get_default_view(keyboard->focus);
604 	shell->exposay.clicked = NULL;
605 	wl_list_init(&shell->exposay.surface_list);
606 
607 	lower_fullscreen_layer(shell, NULL);
608 	shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
609 	weston_keyboard_start_grab(keyboard,
610 	                           &shell->exposay.grab_kbd);
611 	weston_keyboard_set_focus(keyboard, NULL);
612 
613 	shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
614 	if (pointer) {
615 		weston_pointer_start_grab(pointer,
616 		                          &shell->exposay.grab_ptr);
617 		weston_pointer_clear_focus(pointer);
618 	}
619 	wl_list_for_each(shell_output, &shell->output_list, link) {
620 		enum exposay_layout_state state;
621 
622 		state = exposay_layout(shell, shell_output);
623 
624 		if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW)
625 			animate = true;
626 	}
627 
628 	return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW
629 		       : EXPOSAY_LAYOUT_OVERVIEW;
630 }
631 
632 static void
exposay_check_state(struct desktop_shell * shell)633 exposay_check_state(struct desktop_shell *shell)
634 {
635 	enum exposay_layout_state state_new = shell->exposay.state_cur;
636 	int do_switch = 0;
637 
638 	/* Don't do anything whilst animations are running, just store up
639 	 * target state changes and only act on them when the animations have
640 	 * completed. */
641 	if (exposay_is_animating(shell))
642 		return;
643 
644 	switch (shell->exposay.state_target) {
645 	case EXPOSAY_TARGET_OVERVIEW:
646 		switch (shell->exposay.state_cur) {
647 		case EXPOSAY_LAYOUT_OVERVIEW:
648 			goto out;
649 		case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
650 			state_new = EXPOSAY_LAYOUT_OVERVIEW;
651 			break;
652 		default:
653 			state_new = exposay_transition_active(shell);
654 			break;
655 		}
656 		break;
657 
658 	case EXPOSAY_TARGET_SWITCH:
659 		do_switch = 1; /* fallthrough */
660 	case EXPOSAY_TARGET_CANCEL:
661 		switch (shell->exposay.state_cur) {
662 		case EXPOSAY_LAYOUT_INACTIVE:
663 			goto out;
664 		case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
665 			state_new = exposay_set_inactive(shell);
666 			break;
667 		default:
668 			state_new = exposay_transition_inactive(shell, do_switch);
669 			break;
670 		}
671 
672 		break;
673 	}
674 
675 out:
676 	shell->exposay.state_cur = state_new;
677 }
678 
679 static void
exposay_set_state(struct desktop_shell * shell,enum exposay_target_state state,struct weston_seat * seat)680 exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
681                   struct weston_seat *seat)
682 {
683 	shell->exposay.state_target = state;
684 	shell->exposay.seat = seat;
685 	exposay_check_state(shell);
686 }
687 
688 void
exposay_binding(struct weston_keyboard * keyboard,enum weston_keyboard_modifier modifier,void * data)689 exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier,
690 		void *data)
691 {
692 	struct desktop_shell *shell = data;
693 
694 	exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat);
695 }
696