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 /* Compute each surface size and then inner pad (10% of surface size).
211 * After that, it's necessary to recompute surface size (90% of its
212 * original size). Also, each surface can't be bigger than half the
213 * exposay area width and height.
214 */
215 static void
exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area,struct exposay_output * eoutput)216 exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area, struct exposay_output *eoutput)
217 {
218 if (exposay_area.height < exposay_area.width)
219 eoutput->surface_size = exposay_area.height / eoutput->grid_size;
220 else
221 eoutput->surface_size = exposay_area.width / eoutput->grid_size;
222
223 eoutput->padding_inner = eoutput->surface_size / 10;
224 eoutput->surface_size -= eoutput->padding_inner;
225
226 if ((uint32_t)eoutput->surface_size > (exposay_area.width / 2))
227 eoutput->surface_size = exposay_area.width / 2;
228 if ((uint32_t)eoutput->surface_size > (exposay_area.height / 2))
229 eoutput->surface_size = exposay_area.height / 2;
230 }
231
232 /* Compute the exposay top/left margin in order to centralize it */
233 static void
exposay_margin_size(struct desktop_shell * shell,pixman_rectangle32_t exposay_area,int row_size,int column_size,int * left_margin,int * top_margin)234 exposay_margin_size(struct desktop_shell *shell, pixman_rectangle32_t exposay_area,
235 int row_size, int column_size, int *left_margin, int *top_margin)
236 {
237 (*left_margin) = exposay_area.x + (exposay_area.width - row_size) / 2;
238 (*top_margin) = exposay_area.y + (exposay_area.height - column_size) / 2;
239 }
240
241 /* Pretty lame layout for now; just tries to make a square. Should take
242 * aspect ratio into account really. Also needs to be notified of surface
243 * addition and removal and adjust layout/animate accordingly.
244 *
245 * Lay the grid out as square as possible, losing surfaces from the
246 * bottom row if required. Start with fixed padding of a 10% margin
247 * around the outside, and maximise the area made available to surfaces
248 * after this. Also, add an inner padding between surfaces that varies
249 * with the surface size (10% of its size).
250 *
251 * If we can't make a square grid, add one extra row at the bottom which
252 * will have a smaller number of columns.
253 */
254 static enum exposay_layout_state
exposay_layout(struct desktop_shell * shell,struct shell_output * shell_output)255 exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output)
256 {
257 struct workspace *workspace = shell->exposay.workspace;
258 struct weston_output *output = shell_output->output;
259 struct exposay_output *eoutput = &shell_output->eoutput;
260 struct weston_view *view;
261 struct exposay_surface *esurface, *highlight = NULL;
262 pixman_rectangle32_t exposay_area;
263 int pad, row_size, column_size, left_margin, top_margin;
264 int last_row_size, last_row_margin_increase;
265 int populated_rows;
266 int i;
267
268 eoutput->num_surfaces = 0;
269 wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
270 if (!get_shell_surface(view->surface))
271 continue;
272 if (view->output != output)
273 continue;
274 eoutput->num_surfaces++;
275 }
276
277 if (eoutput->num_surfaces == 0) {
278 eoutput->grid_size = 0;
279 eoutput->padding_inner = 0;
280 eoutput->surface_size = 0;
281 return EXPOSAY_LAYOUT_OVERVIEW;
282 }
283
284 /* Get exposay area and position, taking into account
285 * the shell panel position and size */
286 get_output_work_area(shell, output, &exposay_area);
287
288 /* Compute grid size */
289 eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces));
290 if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces)
291 eoutput->grid_size++;
292
293 /* Compute each surface size and the inner padding between them */
294 exposay_surface_and_inner_pad_size(exposay_area, eoutput);
295
296 /* Compute each row/column size */
297 pad = eoutput->surface_size + eoutput->padding_inner;
298 row_size = (pad * eoutput->grid_size) - eoutput->padding_inner;
299 /* We may have empty rows that should be desconsidered to compute
300 * column size */
301 populated_rows = ceil(eoutput->num_surfaces / (float) eoutput->grid_size);
302 column_size = (pad * populated_rows) - eoutput->padding_inner;
303
304 /* The last row size can be different, since it may have less surfaces
305 * than the grid size. Also, its margin may be increased to centralize
306 * its surfaces, in the case where we don't have a perfect grid. */
307 last_row_size = ((eoutput->num_surfaces % eoutput->grid_size) * pad) - eoutput->padding_inner;
308 if (eoutput->num_surfaces % eoutput->grid_size)
309 last_row_margin_increase = (row_size - last_row_size) / 2;
310 else
311 last_row_margin_increase = 0;
312
313 /* Compute a top/left margin to centralize the exposay */
314 exposay_margin_size(shell, exposay_area, row_size, column_size, &left_margin, &top_margin);
315
316 i = 0;
317 wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
318
319 if (!get_shell_surface(view->surface))
320 continue;
321 if (view->output != output)
322 continue;
323
324 esurface = malloc(sizeof(*esurface));
325 if (!esurface) {
326 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
327 shell->exposay.seat);
328 break;
329 }
330
331 wl_list_insert(&shell->exposay.surface_list, &esurface->link);
332 esurface->shell = shell;
333 esurface->eoutput = eoutput;
334 esurface->view = view;
335
336 esurface->row = i / eoutput->grid_size;
337 esurface->column = i % eoutput->grid_size;
338
339 esurface->x = left_margin + (pad * esurface->column);
340 esurface->y = top_margin + (pad * esurface->row);
341
342 /* If this is the last row, increase left margin (it sums 0 if
343 * we have a perfect square) to centralize the surfaces */
344 if (eoutput->num_surfaces / eoutput->grid_size == esurface->row)
345 esurface->x += last_row_margin_increase;
346
347 if (view->surface->width > view->surface->height)
348 esurface->scale = eoutput->surface_size / (float) view->surface->width;
349 else
350 esurface->scale = eoutput->surface_size / (float) view->surface->height;
351 esurface->width = view->surface->width * esurface->scale;
352 esurface->height = view->surface->height * esurface->scale;
353
354 /* Surfaces are usually rectangular, but their exposay surfaces
355 * are square. centralize them in their own square */
356 if (esurface->width > esurface->height)
357 esurface->y += (esurface->width - esurface->height) / 2;
358 else
359 esurface->x += (esurface->height - esurface->width) / 2;
360
361 if (shell->exposay.focus_current == esurface->view)
362 highlight = esurface;
363
364 exposay_animate_in(esurface);
365
366 /* We want our destroy handler to be after the animation
367 * destroy handler in the list, this way when the view is
368 * destroyed, the animation can safely call the animation
369 * completion callback before we free the esurface in our
370 * destroy handler.
371 */
372 esurface->view_destroy_listener.notify = handle_view_destroy;
373 wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener);
374
375 i++;
376 }
377
378 if (highlight) {
379 shell->exposay.focus_current = NULL;
380 exposay_highlight_surface(shell, highlight);
381 }
382
383 weston_compositor_schedule_repaint(shell->compositor);
384
385 return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
386 }
387
388 static void
exposay_focus(struct weston_pointer_grab * grab)389 exposay_focus(struct weston_pointer_grab *grab)
390 {
391 }
392
393 static void
exposay_motion(struct weston_pointer_grab * grab,const struct timespec * time,struct weston_pointer_motion_event * event)394 exposay_motion(struct weston_pointer_grab *grab,
395 const struct timespec *time,
396 struct weston_pointer_motion_event *event)
397 {
398 struct desktop_shell *shell =
399 container_of(grab, struct desktop_shell, exposay.grab_ptr);
400
401 weston_pointer_move(grab->pointer, event);
402
403 exposay_pick(shell,
404 wl_fixed_to_int(grab->pointer->x),
405 wl_fixed_to_int(grab->pointer->y));
406 }
407
408 static void
exposay_button(struct weston_pointer_grab * grab,const struct timespec * time,uint32_t button,uint32_t state_w)409 exposay_button(struct weston_pointer_grab *grab, const struct timespec *time,
410 uint32_t button, uint32_t state_w)
411 {
412 struct desktop_shell *shell =
413 container_of(grab, struct desktop_shell, exposay.grab_ptr);
414 struct weston_seat *seat = grab->pointer->seat;
415 enum wl_pointer_button_state state = state_w;
416
417 if (button != BTN_LEFT)
418 return;
419
420 /* Store the surface we clicked on, and don't do anything if we end up
421 * releasing on a different surface. */
422 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
423 shell->exposay.clicked = shell->exposay.focus_current;
424 return;
425 }
426
427 if (shell->exposay.focus_current == shell->exposay.clicked)
428 exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
429 else
430 shell->exposay.clicked = NULL;
431 }
432
433 static void
exposay_axis(struct weston_pointer_grab * grab,const struct timespec * time,struct weston_pointer_axis_event * event)434 exposay_axis(struct weston_pointer_grab *grab,
435 const struct timespec *time,
436 struct weston_pointer_axis_event *event)
437 {
438 }
439
440 static void
exposay_axis_source(struct weston_pointer_grab * grab,uint32_t source)441 exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source)
442 {
443 }
444
445 static void
exposay_frame(struct weston_pointer_grab * grab)446 exposay_frame(struct weston_pointer_grab *grab)
447 {
448 }
449
450 static void
exposay_pointer_grab_cancel(struct weston_pointer_grab * grab)451 exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
452 {
453 struct desktop_shell *shell =
454 container_of(grab, struct desktop_shell, exposay.grab_ptr);
455
456 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
457 }
458
459 static const struct weston_pointer_grab_interface exposay_ptr_grab = {
460 exposay_focus,
461 exposay_motion,
462 exposay_button,
463 exposay_axis,
464 exposay_axis_source,
465 exposay_frame,
466 exposay_pointer_grab_cancel,
467 };
468
469 static int
exposay_maybe_move(struct desktop_shell * shell,int row,int column)470 exposay_maybe_move(struct desktop_shell *shell, int row, int column)
471 {
472 struct exposay_surface *esurface;
473
474 wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
475 if (esurface->eoutput != shell->exposay.cur_output ||
476 esurface->row != row || esurface->column != column)
477 continue;
478
479 exposay_highlight_surface(shell, esurface);
480 return 1;
481 }
482
483 return 0;
484 }
485
486 static void
exposay_key(struct weston_keyboard_grab * grab,const struct timespec * time,uint32_t key,uint32_t state_w)487 exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time,
488 uint32_t key, uint32_t state_w)
489 {
490 struct weston_seat *seat = grab->keyboard->seat;
491 struct desktop_shell *shell =
492 container_of(grab, struct desktop_shell, exposay.grab_kbd);
493 enum wl_keyboard_key_state state = state_w;
494
495 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
496 return;
497
498 switch (key) {
499 case KEY_ESC:
500 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
501 break;
502 case KEY_ENTER:
503 exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
504 break;
505 case KEY_UP:
506 exposay_maybe_move(shell, shell->exposay.row_current - 1,
507 shell->exposay.column_current);
508 break;
509 case KEY_DOWN:
510 /* Special case for trying to move to the bottom row when it
511 * has fewer items than all the others. */
512 if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
513 shell->exposay.column_current) &&
514 shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) {
515 exposay_maybe_move(shell, shell->exposay.row_current + 1,
516 (shell->exposay.cur_output->num_surfaces %
517 shell->exposay.cur_output->grid_size) - 1);
518 }
519 break;
520 case KEY_LEFT:
521 exposay_maybe_move(shell, shell->exposay.row_current,
522 shell->exposay.column_current - 1);
523 break;
524 case KEY_RIGHT:
525 exposay_maybe_move(shell, shell->exposay.row_current,
526 shell->exposay.column_current + 1);
527 break;
528 case KEY_TAB:
529 /* Try to move right, then down (and to the leftmost column),
530 * then if all else fails, to the top left. */
531 if (!exposay_maybe_move(shell, shell->exposay.row_current,
532 shell->exposay.column_current + 1) &&
533 !exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
534 exposay_maybe_move(shell, 0, 0);
535 break;
536 default:
537 break;
538 }
539 }
540
541 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)542 exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
543 uint32_t mods_depressed, uint32_t mods_latched,
544 uint32_t mods_locked, uint32_t group)
545 {
546 struct desktop_shell *shell =
547 container_of(grab, struct desktop_shell, exposay.grab_kbd);
548 struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
549
550 /* We want to know when mod has been pressed and released.
551 * FIXME: There is a problem here: if mod is pressed, then a key
552 * is pressed and released, then mod is released, we will treat that
553 * as if only mod had been pressed and released. */
554 if (seat->modifier_state) {
555 if (seat->modifier_state == shell->binding_modifier) {
556 shell->exposay.mod_pressed = true;
557 } else {
558 shell->exposay.mod_invalid = true;
559 }
560 } else {
561 if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
562 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
563
564 shell->exposay.mod_invalid = false;
565 shell->exposay.mod_pressed = false;
566 }
567
568 return;
569 }
570
571 static void
exposay_cancel(struct weston_keyboard_grab * grab)572 exposay_cancel(struct weston_keyboard_grab *grab)
573 {
574 struct desktop_shell *shell =
575 container_of(grab, struct desktop_shell, exposay.grab_kbd);
576
577 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
578 }
579
580 static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
581 exposay_key,
582 exposay_modifier,
583 exposay_cancel,
584 };
585
586 /**
587 * Called when the transition from overview -> inactive has completed.
588 */
589 static enum exposay_layout_state
exposay_set_inactive(struct desktop_shell * shell)590 exposay_set_inactive(struct desktop_shell *shell)
591 {
592 struct weston_seat *seat = shell->exposay.seat;
593 struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
594 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
595
596 if (pointer)
597 weston_pointer_end_grab(pointer);
598
599 if (keyboard) {
600 weston_keyboard_end_grab(keyboard);
601 if (keyboard->input_method_resource)
602 keyboard->grab = &keyboard->input_method_grab;
603 }
604
605 return EXPOSAY_LAYOUT_INACTIVE;
606 }
607
608 /**
609 * Begins the transition from overview to inactive. */
610 static enum exposay_layout_state
exposay_transition_inactive(struct desktop_shell * shell,int switch_focus)611 exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
612 {
613 struct exposay_surface *esurface;
614
615 /* Call activate() before we start the animations to avoid
616 * animating back the old state and then immediately transitioning
617 * to the new. */
618 if (switch_focus && shell->exposay.focus_current)
619 activate(shell, shell->exposay.focus_current,
620 shell->exposay.seat,
621 WESTON_ACTIVATE_FLAG_CONFIGURE);
622 else if (shell->exposay.focus_prev)
623 activate(shell, shell->exposay.focus_prev,
624 shell->exposay.seat,
625 WESTON_ACTIVATE_FLAG_CONFIGURE);
626
627 wl_list_for_each(esurface, &shell->exposay.surface_list, link)
628 exposay_animate_out(esurface);
629 weston_compositor_schedule_repaint(shell->compositor);
630
631 return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
632 }
633
634 static enum exposay_layout_state
exposay_transition_active(struct desktop_shell * shell)635 exposay_transition_active(struct desktop_shell *shell)
636 {
637 struct weston_seat *seat = shell->exposay.seat;
638 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
639 struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
640 struct shell_output *shell_output;
641 bool animate = false;
642
643 shell->exposay.workspace = get_current_workspace(shell);
644 shell->exposay.focus_prev = get_default_view(keyboard->focus);
645 shell->exposay.focus_current = get_default_view(keyboard->focus);
646 shell->exposay.clicked = NULL;
647 wl_list_init(&shell->exposay.surface_list);
648
649 lower_fullscreen_layer(shell, NULL);
650 shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
651 weston_keyboard_start_grab(keyboard,
652 &shell->exposay.grab_kbd);
653 weston_keyboard_set_focus(keyboard, NULL);
654
655 shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
656 if (pointer) {
657 weston_pointer_start_grab(pointer,
658 &shell->exposay.grab_ptr);
659 weston_pointer_clear_focus(pointer);
660 }
661 wl_list_for_each(shell_output, &shell->output_list, link) {
662 enum exposay_layout_state state;
663
664 state = exposay_layout(shell, shell_output);
665
666 if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW)
667 animate = true;
668 }
669
670 return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW
671 : EXPOSAY_LAYOUT_OVERVIEW;
672 }
673
674 static void
exposay_check_state(struct desktop_shell * shell)675 exposay_check_state(struct desktop_shell *shell)
676 {
677 enum exposay_layout_state state_new = shell->exposay.state_cur;
678 int do_switch = 0;
679
680 /* Don't do anything whilst animations are running, just store up
681 * target state changes and only act on them when the animations have
682 * completed. */
683 if (exposay_is_animating(shell))
684 return;
685
686 switch (shell->exposay.state_target) {
687 case EXPOSAY_TARGET_OVERVIEW:
688 switch (shell->exposay.state_cur) {
689 case EXPOSAY_LAYOUT_OVERVIEW:
690 goto out;
691 case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
692 state_new = EXPOSAY_LAYOUT_OVERVIEW;
693 break;
694 default:
695 state_new = exposay_transition_active(shell);
696 break;
697 }
698 break;
699
700 case EXPOSAY_TARGET_SWITCH:
701 do_switch = 1; /* fallthrough */
702 case EXPOSAY_TARGET_CANCEL:
703 switch (shell->exposay.state_cur) {
704 case EXPOSAY_LAYOUT_INACTIVE:
705 goto out;
706 case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
707 state_new = exposay_set_inactive(shell);
708 break;
709 default:
710 state_new = exposay_transition_inactive(shell, do_switch);
711 break;
712 }
713
714 break;
715 }
716
717 out:
718 shell->exposay.state_cur = state_new;
719 }
720
721 static void
exposay_set_state(struct desktop_shell * shell,enum exposay_target_state state,struct weston_seat * seat)722 exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
723 struct weston_seat *seat)
724 {
725 shell->exposay.state_target = state;
726 shell->exposay.seat = seat;
727 exposay_check_state(shell);
728 }
729
730 void
exposay_binding(struct weston_keyboard * keyboard,enum weston_keyboard_modifier modifier,void * data)731 exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier,
732 void *data)
733 {
734 struct desktop_shell *shell = data;
735
736 exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat);
737 }
738