2 * Copyright © 2010-2012 Intel Corporation
3 * Copyright © 2011-2012 Collabora, Ltd.
4 * Copyright © 2013 Raspberry Pi Foundation
5 * Copyright © 2013-2014 TOYOTA MOTOR CORPORATION.
7 * Permission to use, copy, modify, distribute, and sell this software and
8 * its documentation for any purpose is hereby granted without fee, provided
9 * that the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of the copyright holders not be used in
12 * advertising or publicity pertaining to distribution of the software
13 * without specific, written prior permission. The copyright holders make
14 * no representations about the suitability of this software for any
15 * purpose. It is provided "as is" without express or implied warranty.
17 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
18 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
22 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 #include <linux/input.h>
28 #include "ico_ivi_shell.h"
30 struct exposay_surface {
31 struct desktop_shell *shell;
32 struct weston_surface *surface;
33 struct weston_view *view;
45 /* The animations only apply a transformation for their own lifetime,
46 * and don't have an option to indefinitely maintain the
47 * transformation in a steady state - so, we apply our own once the
48 * animation has finished. */
49 struct weston_transform transform;
52 static void exposay_set_state(struct desktop_shell *shell,
53 enum exposay_target_state state,
54 struct weston_seat *seat);
55 static void exposay_check_state(struct desktop_shell *shell);
58 exposay_in_flight_inc(struct desktop_shell *shell)
60 shell->exposay.in_flight++;
64 exposay_in_flight_dec(struct desktop_shell *shell)
66 if (--shell->exposay.in_flight > 0)
69 exposay_check_state(shell);
73 exposay_animate_in_done(struct weston_view_animation *animation, void *data)
75 struct exposay_surface *esurface = data;
77 wl_list_insert(&esurface->view->geometry.transformation_list,
78 &esurface->transform.link);
79 weston_matrix_init(&esurface->transform.matrix);
80 weston_matrix_scale(&esurface->transform.matrix,
81 esurface->scale, esurface->scale, 1.0f);
82 weston_matrix_translate(&esurface->transform.matrix,
83 esurface->x - esurface->view->geometry.x,
84 esurface->y - esurface->view->geometry.y,
87 weston_view_geometry_dirty(esurface->view);
88 weston_compositor_schedule_repaint(esurface->view->surface->compositor);
90 exposay_in_flight_dec(esurface->shell);
94 exposay_animate_in(struct exposay_surface *esurface)
96 exposay_in_flight_inc(esurface->shell);
98 weston_move_scale_run(esurface->view,
99 esurface->x - esurface->view->geometry.x,
100 esurface->y - esurface->view->geometry.y,
101 1.0, esurface->scale, 0,
102 exposay_animate_in_done, esurface);
106 exposay_animate_out_done(struct weston_view_animation *animation, void *data)
108 struct exposay_surface *esurface = data;
109 struct desktop_shell *shell = esurface->shell;
111 wl_list_remove(&esurface->link);
114 exposay_in_flight_dec(shell);
118 exposay_animate_out(struct exposay_surface *esurface)
120 exposay_in_flight_inc(esurface->shell);
122 /* Remove the static transformation set up by
123 * exposay_transform_in_done(). */
124 wl_list_remove(&esurface->transform.link);
125 weston_view_geometry_dirty(esurface->view);
127 weston_move_scale_run(esurface->view,
128 esurface->x - esurface->view->geometry.x,
129 esurface->y - esurface->view->geometry.y,
130 1.0, esurface->scale, 1,
131 exposay_animate_out_done, esurface);
135 exposay_highlight_surface(struct desktop_shell *shell,
136 struct exposay_surface *esurface)
138 struct weston_view *view = esurface->view;
140 shell->exposay.row_current = esurface->row;
141 shell->exposay.column_current = esurface->column;
143 activate(shell, view->surface, shell->exposay.seat);
144 shell->exposay.focus_current = view;
148 exposay_is_animating(struct desktop_shell *shell)
150 if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
151 shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
154 return (shell->exposay.in_flight > 0);
158 exposay_pick(struct desktop_shell *shell, int x, int y)
160 struct exposay_surface *esurface;
162 if (exposay_is_animating(shell))
165 wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
166 if (x < esurface->x || x > esurface->x + esurface->width)
168 if (y < esurface->y || y > esurface->y + esurface->height)
171 exposay_highlight_surface(shell, esurface);
176 /* Pretty lame layout for now; just tries to make a square. Should take
177 * aspect ratio into account really. Also needs to be notified of surface
178 * addition and removal and adjust layout/animate accordingly. */
179 static enum exposay_layout_state
180 exposay_layout(struct desktop_shell *shell)
182 struct workspace *workspace = shell->exposay.workspace;
183 struct weston_compositor *compositor = shell->compositor;
184 struct weston_output *output = get_default_output(compositor);
185 struct weston_view *view;
186 struct exposay_surface *esurface, *highlight = NULL;
189 int last_row_removed = 0;
191 wl_list_init(&shell->exposay.surface_list);
193 shell->exposay.num_surfaces = 0;
194 wl_list_for_each(view, &workspace->layer.view_list, layer_link) {
195 if (!get_shell_surface(view->surface))
197 shell->exposay.num_surfaces++;
200 if (shell->exposay.num_surfaces == 0) {
201 shell->exposay.grid_size = 0;
202 shell->exposay.hpadding_outer = 0;
203 shell->exposay.vpadding_outer = 0;
204 shell->exposay.padding_inner = 0;
205 shell->exposay.surface_size = 0;
206 return EXPOSAY_LAYOUT_OVERVIEW;
209 /* Lay the grid out as square as possible, losing surfaces from the
210 * bottom row if required. Start with fixed padding of a 10% margin
211 * around the outside and 80px internal padding between surfaces, and
212 * maximise the area made available to surfaces after this, but only
213 * to a maximum of 1/3rd the total output size.
215 * If we can't make a square grid, add one extra row at the bottom
216 * which will have a smaller number of columns.
218 * XXX: Surely there has to be a better way to express this maths,
221 shell->exposay.grid_size = floor(sqrtf(shell->exposay.num_surfaces));
222 if (pow(shell->exposay.grid_size, 2) != shell->exposay.num_surfaces)
223 shell->exposay.grid_size++;
224 last_row_removed = pow(shell->exposay.grid_size, 2) - shell->exposay.num_surfaces;
226 shell->exposay.hpadding_outer = (output->width / 10);
227 shell->exposay.vpadding_outer = (output->height / 10);
228 shell->exposay.padding_inner = 80;
230 w = output->width - (shell->exposay.hpadding_outer * 2);
231 w -= shell->exposay.padding_inner * (shell->exposay.grid_size - 1);
232 w /= shell->exposay.grid_size;
234 h = output->height - (shell->exposay.vpadding_outer * 2);
235 h -= shell->exposay.padding_inner * (shell->exposay.grid_size - 1);
236 h /= shell->exposay.grid_size;
238 shell->exposay.surface_size = (w < h) ? w : h;
239 if (shell->exposay.surface_size > (output->width / 2))
240 shell->exposay.surface_size = output->width / 2;
241 if (shell->exposay.surface_size > (output->height / 2))
242 shell->exposay.surface_size = output->height / 2;
245 wl_list_for_each(view, &workspace->layer.view_list, layer_link) {
248 pad = shell->exposay.surface_size + shell->exposay.padding_inner;
250 if (!get_shell_surface(view->surface))
253 esurface = malloc(sizeof(*esurface));
255 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
256 shell->exposay.seat);
260 wl_list_insert(&shell->exposay.surface_list, &esurface->link);
261 esurface->shell = shell;
262 esurface->view = view;
264 esurface->row = i / shell->exposay.grid_size;
265 esurface->column = i % shell->exposay.grid_size;
267 esurface->x = shell->exposay.hpadding_outer;
268 esurface->x += pad * esurface->column;
269 esurface->y = shell->exposay.vpadding_outer;
270 esurface->y += pad * esurface->row;
272 if (esurface->row == shell->exposay.grid_size - 1)
273 esurface->x += (shell->exposay.surface_size + shell->exposay.padding_inner) * last_row_removed / 2;
275 if (view->surface->width > view->surface->height)
276 esurface->scale = shell->exposay.surface_size / (float) view->surface->width;
278 esurface->scale = shell->exposay.surface_size / (float) view->surface->height;
279 esurface->width = view->surface->width * esurface->scale;
280 esurface->height = view->surface->height * esurface->scale;
282 if (shell->exposay.focus_current == esurface->view)
283 highlight = esurface;
285 set_alpha_if_fullscreen(get_shell_surface(view->surface));
287 exposay_animate_in(esurface);
293 exposay_highlight_surface(shell, highlight);
295 weston_compositor_schedule_repaint(shell->compositor);
297 return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
301 exposay_focus(struct weston_pointer_grab *grab)
306 exposay_motion(struct weston_pointer_grab *grab, uint32_t time,
307 wl_fixed_t x, wl_fixed_t y)
309 struct desktop_shell *shell =
310 container_of(grab, struct desktop_shell, exposay.grab_ptr);
312 weston_pointer_move(grab->pointer, x, y);
315 wl_fixed_to_int(grab->pointer->x),
316 wl_fixed_to_int(grab->pointer->y));
320 exposay_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t button,
323 struct desktop_shell *shell =
324 container_of(grab, struct desktop_shell, exposay.grab_ptr);
325 struct weston_seat *seat = grab->pointer->seat;
326 enum wl_pointer_button_state state = state_w;
328 if (button != BTN_LEFT)
331 /* Store the surface we clicked on, and don't do anything if we end up
332 * releasing on a different surface. */
333 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
334 shell->exposay.clicked = shell->exposay.focus_current;
338 if (shell->exposay.focus_current == shell->exposay.clicked)
339 exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
341 shell->exposay.clicked = NULL;
345 exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
347 struct desktop_shell *shell =
348 container_of(grab, struct desktop_shell, exposay.grab_ptr);
350 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
353 static const struct weston_pointer_grab_interface exposay_ptr_grab = {
357 exposay_pointer_grab_cancel,
361 exposay_maybe_move(struct desktop_shell *shell, int row, int column)
363 struct exposay_surface *esurface;
365 wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
366 if (esurface->row != row || esurface->column != column)
369 exposay_highlight_surface(shell, esurface);
377 exposay_key(struct weston_keyboard_grab *grab, uint32_t time, uint32_t key,
380 struct weston_seat *seat = grab->keyboard->seat;
381 struct desktop_shell *shell =
382 container_of(grab, struct desktop_shell, exposay.grab_kbd);
383 enum wl_keyboard_key_state state = state_w;
385 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
390 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
393 exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
396 exposay_maybe_move(shell, shell->exposay.row_current - 1,
397 shell->exposay.column_current);
400 /* Special case for trying to move to the bottom row when it
401 * has fewer items than all the others. */
402 if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
403 shell->exposay.column_current) &&
404 shell->exposay.row_current < (shell->exposay.grid_size - 1)) {
405 exposay_maybe_move(shell, shell->exposay.row_current + 1,
406 (shell->exposay.num_surfaces %
407 shell->exposay.grid_size) - 1);
411 exposay_maybe_move(shell, shell->exposay.row_current,
412 shell->exposay.column_current - 1);
415 exposay_maybe_move(shell, shell->exposay.row_current,
416 shell->exposay.column_current + 1);
419 /* Try to move right, then down (and to the leftmost column),
420 * then if all else fails, to the top left. */
421 if (!exposay_maybe_move(shell, shell->exposay.row_current,
422 shell->exposay.column_current + 1) &&
423 !exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
424 exposay_maybe_move(shell, 0, 0);
432 exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
433 uint32_t mods_depressed, uint32_t mods_latched,
434 uint32_t mods_locked, uint32_t group)
436 struct desktop_shell *shell =
437 container_of(grab, struct desktop_shell, exposay.grab_kbd);
438 struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
440 /* We want to know when mod has been pressed and released.
441 * FIXME: There is a problem here: if mod is pressed, then a key
442 * is pressed and released, then mod is released, we will treat that
443 * as if only mod had been pressed and released. */
444 if (seat->modifier_state) {
445 if (seat->modifier_state == shell->binding_modifier) {
446 shell->exposay.mod_pressed = true;
448 shell->exposay.mod_invalid = true;
451 if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
452 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
454 shell->exposay.mod_invalid = false;
455 shell->exposay.mod_pressed = false;
462 exposay_cancel(struct weston_keyboard_grab *grab)
464 struct desktop_shell *shell =
465 container_of(grab, struct desktop_shell, exposay.grab_kbd);
467 exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
470 static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
477 * Called when the transition from overview -> inactive has completed.
479 static enum exposay_layout_state
480 exposay_set_inactive(struct desktop_shell *shell)
482 struct weston_seat *seat = shell->exposay.seat;
484 weston_keyboard_end_grab(seat->keyboard);
485 weston_pointer_end_grab(seat->pointer);
486 if (seat->keyboard->input_method_resource)
487 seat->keyboard->grab = &seat->keyboard->input_method_grab;
489 return EXPOSAY_LAYOUT_INACTIVE;
493 * Begins the transition from overview to inactive. */
494 static enum exposay_layout_state
495 exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
497 struct exposay_surface *esurface;
499 /* Call activate() before we start the animations to avoid
500 * animating back the old state and then immediately transitioning
502 if (switch_focus && shell->exposay.focus_current)
503 activate(shell, shell->exposay.focus_current->surface,
504 shell->exposay.seat);
505 else if (shell->exposay.focus_prev)
506 activate(shell, shell->exposay.focus_prev->surface,
507 shell->exposay.seat);
509 wl_list_for_each(esurface, &shell->exposay.surface_list, link)
510 exposay_animate_out(esurface);
511 weston_compositor_schedule_repaint(shell->compositor);
513 return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
516 static enum exposay_layout_state
517 exposay_transition_active(struct desktop_shell *shell)
519 struct weston_seat *seat = shell->exposay.seat;
521 shell->exposay.workspace = get_current_workspace(shell);
522 shell->exposay.focus_prev = get_default_view (seat->keyboard->focus);
523 shell->exposay.focus_current = get_default_view (seat->keyboard->focus);
524 shell->exposay.clicked = NULL;
525 wl_list_init(&shell->exposay.surface_list);
527 lower_fullscreen_layer(shell);
528 shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
529 weston_keyboard_start_grab(seat->keyboard,
530 &shell->exposay.grab_kbd);
531 weston_keyboard_set_focus(seat->keyboard, NULL);
533 shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
534 weston_pointer_start_grab(seat->pointer,
535 &shell->exposay.grab_ptr);
536 weston_pointer_set_focus(seat->pointer, NULL,
537 seat->pointer->x, seat->pointer->y);
539 return exposay_layout(shell);
543 exposay_check_state(struct desktop_shell *shell)
545 enum exposay_layout_state state_new = shell->exposay.state_cur;
548 /* Don't do anything whilst animations are running, just store up
549 * target state changes and only act on them when the animations have
551 if (exposay_is_animating(shell))
554 switch (shell->exposay.state_target) {
555 case EXPOSAY_TARGET_OVERVIEW:
556 switch (shell->exposay.state_cur) {
557 case EXPOSAY_LAYOUT_OVERVIEW:
559 case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
560 state_new = EXPOSAY_LAYOUT_OVERVIEW;
563 state_new = exposay_transition_active(shell);
568 case EXPOSAY_TARGET_SWITCH:
569 do_switch = 1; /* fallthrough */
570 case EXPOSAY_TARGET_CANCEL:
571 switch (shell->exposay.state_cur) {
572 case EXPOSAY_LAYOUT_INACTIVE:
574 case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
575 state_new = exposay_set_inactive(shell);
578 state_new = exposay_transition_inactive(shell, do_switch);
586 shell->exposay.state_cur = state_new;
590 exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
591 struct weston_seat *seat)
593 shell->exposay.state_target = state;
594 shell->exposay.seat = seat;
595 exposay_check_state(shell);
599 exposay_binding(struct weston_seat *seat, enum weston_keyboard_modifier modifier,
602 struct desktop_shell *shell = data;
604 exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, seat);