21f306f3c60aba42430daed96ae004bf60eca2fe
[profile/ivi/ico-uxf-weston-plugin.git] / src / ico_exposay.c
1 /*
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.
6  *
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.
16  *
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.
24  */
25
26 #include <linux/input.h>
27
28 #include "ico_ivi_shell.h"
29
30 struct exposay_surface {
31     struct desktop_shell *shell;
32     struct weston_surface *surface;
33     struct weston_view *view;
34     struct wl_list link;
35
36     int x;
37     int y;
38     int width;
39     int height;
40     double scale;
41
42     int row;
43     int column;
44
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;
50 };
51
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);
56
57 static void
58 exposay_in_flight_inc(struct desktop_shell *shell)
59 {
60     shell->exposay.in_flight++;
61 }
62
63 static void
64 exposay_in_flight_dec(struct desktop_shell *shell)
65 {
66     if (--shell->exposay.in_flight > 0)
67         return;
68
69     exposay_check_state(shell);
70 }
71
72 static void
73 exposay_animate_in_done(struct weston_view_animation *animation, void *data)
74 {
75     struct exposay_surface *esurface = data;
76
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,
85                 0);
86
87     weston_view_geometry_dirty(esurface->view);
88     weston_compositor_schedule_repaint(esurface->view->surface->compositor);
89
90     exposay_in_flight_dec(esurface->shell);
91 }
92
93 static void
94 exposay_animate_in(struct exposay_surface *esurface)
95 {
96     exposay_in_flight_inc(esurface->shell);
97
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);
103 }
104
105 static void
106 exposay_animate_out_done(struct weston_view_animation *animation, void *data)
107 {
108     struct exposay_surface *esurface = data;
109     struct desktop_shell *shell = esurface->shell;
110
111     wl_list_remove(&esurface->link);
112     free(esurface);
113
114     exposay_in_flight_dec(shell);
115 }
116
117 static void
118 exposay_animate_out(struct exposay_surface *esurface)
119 {
120     exposay_in_flight_inc(esurface->shell);
121
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);
126
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);
132 }
133
134 static void
135 exposay_highlight_surface(struct desktop_shell *shell,
136                           struct exposay_surface *esurface)
137 {
138     struct weston_view *view = esurface->view;
139
140     shell->exposay.row_current = esurface->row;
141     shell->exposay.column_current = esurface->column;
142
143     activate(shell, view->surface, shell->exposay.seat);
144     shell->exposay.focus_current = view;
145 }
146
147 static int
148 exposay_is_animating(struct desktop_shell *shell)
149 {
150     if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
151         shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
152         return 0;
153
154     return (shell->exposay.in_flight > 0);
155 }
156
157 static void
158 exposay_pick(struct desktop_shell *shell, int x, int y)
159 {
160     struct exposay_surface *esurface;
161
162         if (exposay_is_animating(shell))
163             return;
164
165     wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
166         if (x < esurface->x || x > esurface->x + esurface->width)
167             continue;
168         if (y < esurface->y || y > esurface->y + esurface->height)
169             continue;
170
171         exposay_highlight_surface(shell, esurface);
172         return;
173     }
174 }
175
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)
181 {
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;
187     int w, h;
188     int i;
189     int last_row_removed = 0;
190
191     wl_list_init(&shell->exposay.surface_list);
192
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))
196             continue;
197         shell->exposay.num_surfaces++;
198     }
199
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;
207     }
208
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.
214      *
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.
217      *
218      * XXX: Surely there has to be a better way to express this maths,
219      *      right?!
220      */
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;
225
226     shell->exposay.hpadding_outer = (output->width / 10);
227     shell->exposay.vpadding_outer = (output->height / 10);
228     shell->exposay.padding_inner = 80;
229
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;
233
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;
237
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;
243
244     i = 0;
245     wl_list_for_each(view, &workspace->layer.view_list, layer_link) {
246         int pad;
247
248         pad = shell->exposay.surface_size + shell->exposay.padding_inner;
249
250         if (!get_shell_surface(view->surface))
251             continue;
252
253         esurface = malloc(sizeof(*esurface));
254         if (!esurface) {
255             exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
256                               shell->exposay.seat);
257             break;
258         }
259
260         wl_list_insert(&shell->exposay.surface_list, &esurface->link);
261         esurface->shell = shell;
262         esurface->view = view;
263
264         esurface->row = i / shell->exposay.grid_size;
265         esurface->column = i % shell->exposay.grid_size;
266
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;
271
272         if (esurface->row == shell->exposay.grid_size - 1)
273             esurface->x += (shell->exposay.surface_size + shell->exposay.padding_inner) * last_row_removed / 2;
274
275         if (view->surface->width > view->surface->height)
276             esurface->scale = shell->exposay.surface_size / (float) view->surface->width;
277         else
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;
281
282         if (shell->exposay.focus_current == esurface->view)
283             highlight = esurface;
284
285         set_alpha_if_fullscreen(get_shell_surface(view->surface));
286
287         exposay_animate_in(esurface);
288
289         i++;
290     }
291
292     if (highlight)
293         exposay_highlight_surface(shell, highlight);
294
295     weston_compositor_schedule_repaint(shell->compositor);
296
297     return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
298 }
299
300 static void
301 exposay_focus(struct weston_pointer_grab *grab)
302 {
303 }
304
305 static void
306 exposay_motion(struct weston_pointer_grab *grab, uint32_t time,
307            wl_fixed_t x, wl_fixed_t y)
308 {
309     struct desktop_shell *shell =
310         container_of(grab, struct desktop_shell, exposay.grab_ptr);
311
312     weston_pointer_move(grab->pointer, x, y);
313
314     exposay_pick(shell,
315                  wl_fixed_to_int(grab->pointer->x),
316                  wl_fixed_to_int(grab->pointer->y));
317 }
318
319 static void
320 exposay_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t button,
321                uint32_t state_w)
322 {
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;
327
328     if (button != BTN_LEFT)
329         return;
330
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;
335         return;
336     }
337
338     if (shell->exposay.focus_current == shell->exposay.clicked)
339         exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
340     else
341         shell->exposay.clicked = NULL;
342 }
343
344 static void
345 exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
346 {
347     struct desktop_shell *shell =
348         container_of(grab, struct desktop_shell, exposay.grab_ptr);
349
350     exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
351 }
352
353 static const struct weston_pointer_grab_interface exposay_ptr_grab = {
354     exposay_focus,
355     exposay_motion,
356     exposay_button,
357     exposay_pointer_grab_cancel,
358 };
359
360 static int
361 exposay_maybe_move(struct desktop_shell *shell, int row, int column)
362 {
363     struct exposay_surface *esurface;
364
365     wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
366         if (esurface->row != row || esurface->column != column)
367             continue;
368
369         exposay_highlight_surface(shell, esurface);
370         return 1;
371     }
372
373     return 0;
374 }
375
376 static void
377 exposay_key(struct weston_keyboard_grab *grab, uint32_t time, uint32_t key,
378             uint32_t state_w)
379 {
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;
384
385     if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
386         return;
387
388     switch (key) {
389     case KEY_ESC:
390         exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
391         break;
392     case KEY_ENTER:
393         exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
394         break;
395     case KEY_UP:
396         exposay_maybe_move(shell, shell->exposay.row_current - 1,
397                            shell->exposay.column_current);
398         break;
399     case KEY_DOWN:
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);
408         }
409         break;
410     case KEY_LEFT:
411         exposay_maybe_move(shell, shell->exposay.row_current,
412                            shell->exposay.column_current - 1);
413         break;
414     case KEY_RIGHT:
415         exposay_maybe_move(shell, shell->exposay.row_current,
416                            shell->exposay.column_current + 1);
417         break;
418     case KEY_TAB:
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);
425         break;
426     default:
427         break;
428     }
429 }
430
431 static void
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)
435 {
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;
439
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;
447         } else {
448             shell->exposay.mod_invalid = true;
449         }
450     } else {
451         if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
452             exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
453
454         shell->exposay.mod_invalid = false;
455         shell->exposay.mod_pressed = false;
456     }
457
458     return;
459 }
460
461 static void
462 exposay_cancel(struct weston_keyboard_grab *grab)
463 {
464     struct desktop_shell *shell =
465         container_of(grab, struct desktop_shell, exposay.grab_kbd);
466
467     exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
468 }
469
470 static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
471     exposay_key,
472     exposay_modifier,
473     exposay_cancel,
474 };
475
476 /**
477  * Called when the transition from overview -> inactive has completed.
478  */
479 static enum exposay_layout_state
480 exposay_set_inactive(struct desktop_shell *shell)
481 {
482     struct weston_seat *seat = shell->exposay.seat;
483
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;
488
489     return EXPOSAY_LAYOUT_INACTIVE;
490 }
491
492 /**
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)
496 {
497     struct exposay_surface *esurface;
498
499     /* Call activate() before we start the animations to avoid
500      * animating back the old state and then immediately transitioning
501      * to the new. */
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);
508
509     wl_list_for_each(esurface, &shell->exposay.surface_list, link)
510         exposay_animate_out(esurface);
511     weston_compositor_schedule_repaint(shell->compositor);
512
513     return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
514 }
515
516 static enum exposay_layout_state
517 exposay_transition_active(struct desktop_shell *shell)
518 {
519     struct weston_seat *seat = shell->exposay.seat;
520
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);
526
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);
532
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);
538
539     return exposay_layout(shell);
540 }
541
542 static void
543 exposay_check_state(struct desktop_shell *shell)
544 {
545     enum exposay_layout_state state_new = shell->exposay.state_cur;
546     int do_switch = 0;
547
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
550      * completed. */
551     if (exposay_is_animating(shell))
552         return;
553
554     switch (shell->exposay.state_target) {
555     case EXPOSAY_TARGET_OVERVIEW:
556         switch (shell->exposay.state_cur) {
557         case EXPOSAY_LAYOUT_OVERVIEW:
558             goto out;
559         case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
560             state_new = EXPOSAY_LAYOUT_OVERVIEW;
561             break;
562         default:
563             state_new = exposay_transition_active(shell);
564             break;
565         }
566         break;
567
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:
573             goto out;
574         case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
575             state_new = exposay_set_inactive(shell);
576             break;
577         default:
578             state_new = exposay_transition_inactive(shell, do_switch);
579             break;
580         }
581
582         break;
583     }
584
585 out:
586     shell->exposay.state_cur = state_new;
587 }
588
589 static void
590 exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
591                   struct weston_seat *seat)
592 {
593     shell->exposay.state_target = state;
594     shell->exposay.seat = seat;
595     exposay_check_state(shell);
596 }
597
598 void
599 exposay_binding(struct weston_seat *seat, enum weston_keyboard_modifier modifier,
600         void *data)
601 {
602     struct desktop_shell *shell = data;
603
604     exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, seat);
605 }