8d7dfe0698b8a14761f538791a2b467c51b3c6f3
[profile/ivi/weston.git] / compositor / meego-tablet-shell.c
1 /*
2  * Copyright © 2011 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include <sys/wait.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <fcntl.h>
31 #include <linux/input.h>
32
33 #include "compositor.h"
34 #include "meego-tablet-server-protocol.h"
35
36 /*
37  * TODO: Don't fade back from black until we've received a lockscreen
38  * attachment.
39  */
40
41 enum {
42         STATE_STARTING,
43         STATE_LOCKED,
44         STATE_HOME,
45         STATE_SWITCHER,
46         STATE_TASK
47 };
48
49 struct meego_tablet_shell {
50         struct wl_resource resource;
51
52         struct wlsc_shell shell;
53
54         struct wlsc_compositor *compositor;
55         struct wlsc_process process;
56         struct wlsc_input_device *device;
57         struct wl_client *client;
58
59         struct wlsc_surface *surface;
60
61         struct wlsc_surface *lockscreen_surface;
62         struct wl_listener lockscreen_listener;
63
64         struct wlsc_surface *home_surface;
65
66         struct wlsc_surface *switcher_surface;
67         struct wl_listener switcher_listener;
68
69         struct meego_tablet_client *current_client;
70
71         int state, previous_state;
72         int long_press_active;
73         struct wl_event_source *long_press_source;
74 };
75
76 struct meego_tablet_client {
77         struct wl_resource resource;
78         struct meego_tablet_shell *shell;
79         struct wl_client *client;
80         struct wlsc_surface *surface;
81         char *name;
82 };
83
84 struct meego_tablet_zoom {
85         struct wlsc_surface *surface;
86         struct wlsc_animation animation;
87         struct wlsc_spring spring;
88         struct wlsc_transform transform;
89         struct wl_listener listener;
90         struct meego_tablet_shell *shell;
91         void (*done)(struct meego_tablet_zoom *zoom);
92 };
93
94
95 static void
96 meego_tablet_shell_sigchld(struct wlsc_process *process, int status)
97 {
98         struct meego_tablet_shell *shell =
99                 container_of(process, struct meego_tablet_shell, process);
100
101         shell->process.pid = 0;
102
103         fprintf(stderr, "meego-ux-daemon crashed, exit code %d\n", status);
104 }
105
106 static void
107 meego_tablet_shell_set_state(struct meego_tablet_shell *shell, int state)
108 {
109         static const char *states[] = {
110                 "STARTING", "LOCKED", "HOME", "SWITCHER", "TASK"
111         };
112
113         fprintf(stderr, "switching to state %s (from %s)\n",
114                 states[state], states[shell->state]);
115         shell->previous_state = shell->state;
116         shell->state = state;
117 }
118
119 static void
120 meego_tablet_zoom_destroy(struct meego_tablet_zoom *zoom)
121 {
122         wl_list_remove(&zoom->animation.link);
123         zoom->surface->transform = NULL;
124         if (zoom->done)
125                 zoom->done(zoom);
126         free(zoom);
127 }
128
129 static void
130 handle_zoom_surface_destroy(struct wl_listener *listener,
131                             struct wl_resource *resource, uint32_t time)
132 {
133         struct meego_tablet_zoom *zoom =
134                 container_of(listener, struct meego_tablet_zoom, listener);
135
136         fprintf(stderr, "animation surface gone\n");
137         meego_tablet_zoom_destroy(zoom);
138 }
139
140 static void
141 meego_tablet_zoom_frame(struct wlsc_animation *animation,
142                         struct wlsc_output *output, uint32_t msecs)
143 {
144         struct meego_tablet_zoom *zoom =
145                 container_of(animation, struct meego_tablet_zoom, animation);
146         struct wlsc_surface *es = zoom->surface;
147         GLfloat scale;
148
149         wlsc_spring_update(&zoom->spring, msecs);
150
151         if (wlsc_spring_done(&zoom->spring)) {
152                 fprintf(stderr, "animation done\n");
153                 meego_tablet_zoom_destroy(zoom);
154         }
155
156         scale = zoom->spring.current;
157         wlsc_matrix_init(&zoom->transform.matrix);
158         wlsc_matrix_translate(&zoom->transform.matrix,
159                               -es->width / 2.0, -es->height / 2.0, 0);
160         wlsc_matrix_scale(&zoom->transform.matrix, scale, scale, scale);
161         wlsc_matrix_translate(&zoom->transform.matrix,
162                               es->width / 2.0, es->height / 2.0, 0);
163
164         scale = 1.0 / zoom->spring.current;
165         wlsc_matrix_init(&zoom->transform.inverse);
166         wlsc_matrix_scale(&zoom->transform.inverse, scale, scale, scale);
167
168         wlsc_surface_damage(es);
169 }
170
171 static struct meego_tablet_zoom *
172 meego_tablet_zoom_run(struct meego_tablet_shell *shell,
173                       struct wlsc_surface *surface,
174                       GLfloat start, GLfloat stop)
175 {
176         struct meego_tablet_zoom *zoom;
177
178         fprintf(stderr, "starting animation for surface %p\n", surface);
179
180         zoom = malloc(sizeof *zoom);
181         if (!zoom)
182                 return NULL;
183
184         zoom->shell = shell;
185         zoom->surface = surface;
186         zoom->done = NULL;
187         surface->transform = &zoom->transform;
188         wlsc_spring_init(&zoom->spring, 200.0, start, stop);
189         zoom->spring.timestamp = wlsc_compositor_get_time();
190         zoom->animation.frame = meego_tablet_zoom_frame;
191         meego_tablet_zoom_frame(&zoom->animation, NULL,
192                                 zoom->spring.timestamp);
193
194         zoom->listener.func = handle_zoom_surface_destroy;
195         wl_list_insert(surface->surface.resource.destroy_listener_list.prev,
196                        &zoom->listener.link);
197
198         wl_list_insert(shell->compositor->animation_list.prev,
199                        &zoom->animation.link);
200
201         return zoom;
202 }
203
204 /* FIXME: We should be handling map, not attach...  Map is when the
205  * surface becomes visible, which is what we want to catch.  Attach
206  * will happen whenever the surface changes. */
207
208 static void
209 meego_tablet_shell_attach(struct wlsc_shell *base,
210                           struct wlsc_surface *surface)
211 {
212         struct meego_tablet_shell *shell =
213                 container_of(base, struct meego_tablet_shell, shell);
214
215         surface->x = 0;
216         surface->y = 0;
217
218         if (surface == shell->lockscreen_surface) {
219                 wlsc_compositor_fade(shell->compositor, 0.0);
220                 wlsc_compositor_wake(shell->compositor);
221         } else if (surface == shell->switcher_surface) {
222                 /* */
223         } else if (surface == shell->home_surface) {
224                 if (shell->state == STATE_STARTING) {
225                         meego_tablet_shell_set_state(shell, STATE_LOCKED);
226                         shell->previous_state = STATE_HOME;
227                         wl_resource_post_event(&shell->resource,
228                                                MEEGO_TABLET_SHELL_SHOW_LOCKSCREEN);
229                 }
230         } else if (shell->current_client &&
231                    shell->current_client->surface != surface &&
232                    shell->current_client->client == surface->surface.resource.client) {
233                 meego_tablet_shell_set_state(shell, STATE_TASK);
234                 shell->current_client->surface = surface;
235                 meego_tablet_zoom_run(shell, surface, 0.3, 1.0);
236         }
237 }
238
239 static void
240 handle_lockscreen_surface_destroy(struct wl_listener *listener,
241                                   struct wl_resource *resource, uint32_t time)
242 {
243         struct meego_tablet_shell *shell =
244                 container_of(listener,
245                              struct meego_tablet_shell, lockscreen_listener);
246
247         shell->lockscreen_surface = NULL;
248         meego_tablet_shell_set_state(shell, shell->previous_state);
249 }
250
251 static void
252 tablet_shell_set_lockscreen(struct wl_client *client,
253                             struct wl_resource *resource,
254                             struct wl_surface *surface)
255 {
256         struct meego_tablet_shell *shell = resource->data;
257         struct wlsc_surface *es = (struct wlsc_surface *) surface;
258         struct wlsc_input_device *device =
259                 (struct wlsc_input_device *) shell->compositor->input_device;
260
261         es->x = 0;
262         es->y = 0;
263         wlsc_surface_activate(es, device, wlsc_compositor_get_time());
264         shell->lockscreen_surface = es;
265
266         shell->lockscreen_listener.func = handle_lockscreen_surface_destroy;
267         wl_list_insert(es->surface.resource.destroy_listener_list.prev,
268                        &shell->lockscreen_listener.link);
269 }
270
271 static void
272 handle_switcher_surface_destroy(struct wl_listener *listener,
273                                 struct wl_resource *resource, uint32_t time)
274 {
275         struct meego_tablet_shell *shell =
276                 container_of(listener,
277                              struct meego_tablet_shell, switcher_listener);
278
279         shell->switcher_surface = NULL;
280         if (shell->state != STATE_LOCKED)
281                 meego_tablet_shell_set_state(shell, shell->previous_state);
282 }
283
284 static void
285 tablet_shell_set_switcher(struct wl_client *client,
286                           struct wl_resource *resource,
287                           struct wl_surface *surface)
288 {
289         struct meego_tablet_shell *shell = resource->data;
290         struct wlsc_input_device *device =
291                 (struct wlsc_input_device *) shell->compositor->input_device;
292         struct wlsc_surface *es = (struct wlsc_surface *) surface;
293
294         /* FIXME: Switcher should be centered and the compositor
295          * should do the tinting of the background.  With the cache
296          * layer idea, we should be able to hit the framerate on the
297          * fade/zoom in. */
298         shell->switcher_surface = es;
299         shell->switcher_surface->x = 0;
300         shell->switcher_surface->y = 0;
301
302         wlsc_surface_activate(es, device, wlsc_compositor_get_time());
303
304         shell->switcher_listener.func = handle_switcher_surface_destroy;
305         wl_list_insert(es->surface.resource.destroy_listener_list.prev,
306                        &shell->switcher_listener.link);
307 }
308
309 static void
310 tablet_shell_set_homescreen(struct wl_client *client,
311                             struct wl_resource *resource,
312                             struct wl_surface *surface)
313 {
314         struct meego_tablet_shell *shell = resource->data;
315         struct wlsc_input_device *device =
316                 (struct wlsc_input_device *) shell->compositor->input_device;
317
318         shell->home_surface = (struct wlsc_surface *) surface;
319         shell->home_surface->x = 0;
320         shell->home_surface->y = 0;
321
322         wlsc_surface_activate(shell->home_surface, device,
323                               wlsc_compositor_get_time());
324 }
325
326 static void
327 minimize_zoom_done(struct meego_tablet_zoom *zoom)
328 {
329         struct meego_tablet_shell *shell = zoom->shell;
330         struct wlsc_compositor *compositor = shell->compositor;
331         struct wlsc_input_device *device =
332                 (struct wlsc_input_device *) compositor->input_device;
333
334         wlsc_surface_activate(shell->home_surface,
335                               device, wlsc_compositor_get_time());
336 }
337
338 static void
339 meego_tablet_shell_switch_to(struct meego_tablet_shell *shell,
340                              struct wlsc_surface *surface)
341 {
342         struct wlsc_compositor *compositor = shell->compositor;
343         struct wlsc_input_device *device =
344                 (struct wlsc_input_device *) compositor->input_device;
345         struct wlsc_surface *current;
346         struct meego_tablet_zoom *zoom;
347
348         if (shell->state == STATE_SWITCHER) {
349                 wl_list_remove(&shell->switcher_listener.link);
350                 shell->switcher_surface = NULL;
351         };
352
353         if (surface == shell->home_surface) {
354                 meego_tablet_shell_set_state(shell, STATE_HOME);
355
356                 if (shell->current_client && shell->current_client->surface) {
357                         current = shell->current_client->surface;
358                         zoom = meego_tablet_zoom_run(shell, current, 1.0, 0.3);
359                         zoom->done = minimize_zoom_done;
360                 }
361         } else {
362                 fprintf(stderr, "switch to %p\n", surface);
363                 wlsc_surface_activate(surface, device,
364                                       wlsc_compositor_get_time());
365                 meego_tablet_shell_set_state(shell, STATE_TASK);
366                 meego_tablet_zoom_run(shell, surface, 0.3, 1.0);
367         }
368 }
369
370 static void
371 tablet_shell_show_grid(struct wl_client *client,
372                        struct wl_resource *resource,
373                        struct wl_surface *surface)
374 {
375         struct meego_tablet_shell *shell = resource->data;
376
377         meego_tablet_shell_switch_to(shell, (struct wlsc_surface *) surface);
378 }
379
380 static void
381 tablet_shell_show_panels(struct wl_client *client,
382                          struct wl_resource *resource,
383                          struct wl_surface *surface)
384 {
385         struct meego_tablet_shell *shell = resource->data;
386
387         meego_tablet_shell_switch_to(shell, (struct wlsc_surface *) surface);
388 }
389
390 static void
391 destroy_tablet_client(struct wl_resource *resource)
392 {
393         struct meego_tablet_client *tablet_client =
394                 container_of(resource, struct meego_tablet_client, resource);
395
396         free(tablet_client->name);
397         free(tablet_client);
398 }
399
400 static void
401 tablet_client_destroy(struct wl_client *client, struct wl_resource *resource)
402 {
403         wl_resource_destroy(resource, wlsc_compositor_get_time());
404 }
405
406 static void
407 tablet_client_activate(struct wl_client *client, struct wl_resource *resource)
408 {
409         struct meego_tablet_client *tablet_client = resource->data;
410         struct meego_tablet_shell *shell = tablet_client->shell;
411
412         shell->current_client = tablet_client;
413         if (!tablet_client->surface)
414                 return;
415
416         meego_tablet_shell_switch_to(shell, tablet_client->surface);
417 }
418
419 static const struct meego_tablet_client_interface tablet_client_interface = {
420         tablet_client_destroy,
421         tablet_client_activate
422 };
423
424 static void
425 tablet_shell_create_client(struct wl_client *client,
426                            struct wl_resource *resource,
427                            uint32_t id, const char *name, int fd)
428 {
429         struct meego_tablet_shell *shell = resource->data;
430         struct wlsc_compositor *compositor = shell->compositor;
431         struct meego_tablet_client *tablet_client;
432
433         tablet_client = malloc(sizeof *tablet_client);
434         if (tablet_client == NULL) {
435                 wl_client_post_no_memory(client);
436                 return;
437         }
438
439         tablet_client->client = wl_client_create(compositor->wl_display, fd);
440         tablet_client->shell = shell;
441         tablet_client->name = strdup(name);
442
443         tablet_client->resource.destroy = destroy_tablet_client;
444         tablet_client->resource.object.id = id;
445         tablet_client->resource.object.interface =
446                 &meego_tablet_client_interface;
447         tablet_client->resource.object.implementation =
448                 (void (**)(void)) &tablet_client_interface;
449
450         wl_client_add_resource(client, &tablet_client->resource);
451         tablet_client->surface = NULL;
452         shell->current_client = tablet_client;
453
454         fprintf(stderr, "created client %p, id %d, name %s, fd %d\n",
455                 tablet_client->client, id, name, fd);
456 }
457
458 static const struct meego_tablet_shell_interface tablet_shell_interface = {
459         tablet_shell_set_lockscreen,
460         tablet_shell_set_switcher,
461         tablet_shell_set_homescreen,
462         tablet_shell_show_grid,
463         tablet_shell_show_panels,
464         tablet_shell_create_client
465 };
466
467 static void
468 launch_ux_daemon(struct meego_tablet_shell *shell)
469 {
470         struct wlsc_compositor *compositor = shell->compositor;
471         char s[32];
472         int sv[2], flags;
473
474         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
475                 fprintf(stderr, "socketpair failed\n");
476                 return;
477         }
478
479         shell->process.pid = fork();
480         shell->process.cleanup = meego_tablet_shell_sigchld;
481
482         switch (shell->process.pid) {
483         case 0:
484                 /* SOCK_CLOEXEC closes both ends, so we need to unset
485                  * the flag on the client fd. */
486                 flags = fcntl(sv[1], F_GETFD);
487                 if (flags != -1)
488                         fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
489
490                 snprintf(s, sizeof s, "%d", sv[1]);
491                 setenv("WAYLAND_SOCKET", s, 1);
492                 setenv("EGL_PLATFORM", "wayland", 1);
493                 setenv("QT_QPA_PLATFORM", "waylandgl", 1);
494                 if (execl("/usr/libexec/meego-ux-daemon",
495                           "/usr/libexec/meego-ux-daemon", NULL) < 0)
496                         fprintf(stderr, "exec failed: %m\n");
497                 exit(-1);
498
499         default:
500                 close(sv[1]);
501                 shell->client =
502                         wl_client_create(compositor->wl_display, sv[0]);
503                 wlsc_watch_process(&shell->process);
504                 break;
505
506         case -1:
507                 fprintf(stderr, "failed to fork\n");
508                 break;
509         }
510 }
511
512 static void
513 toggle_switcher(struct meego_tablet_shell *shell)
514 {
515         switch (shell->state) {
516         case STATE_SWITCHER:
517                 wl_resource_post_event(&shell->resource,
518                                      MEEGO_TABLET_SHELL_HIDE_SWITCHER);
519                 break;
520         default:
521                 wl_resource_post_event(&shell->resource,
522                                        MEEGO_TABLET_SHELL_SHOW_SWITCHER);
523                 meego_tablet_shell_set_state(shell, STATE_SWITCHER);
524                 break;
525         }
526 }
527
528 static void
529 meego_tablet_shell_lock(struct wlsc_shell *base)
530 {
531         struct meego_tablet_shell *shell =
532                 container_of(base, struct meego_tablet_shell, shell);
533
534         if (shell->state == STATE_LOCKED)
535                 return;
536         if (shell->state == STATE_SWITCHER)
537                 wl_resource_post_event(&shell->resource,
538                                        MEEGO_TABLET_SHELL_HIDE_SWITCHER);
539
540         wl_resource_post_event(&shell->resource,
541                                MEEGO_TABLET_SHELL_SHOW_LOCKSCREEN);
542         
543         meego_tablet_shell_set_state(shell, STATE_LOCKED);
544 }
545
546 static void
547 go_home(struct meego_tablet_shell *shell)
548 {
549         struct wlsc_input_device *device =
550                 (struct wlsc_input_device *) shell->compositor->input_device;
551
552         if (shell->state == STATE_SWITCHER)
553                 wl_resource_post_event(&shell->resource,
554                                        MEEGO_TABLET_SHELL_HIDE_SWITCHER);
555
556         wlsc_surface_activate(shell->home_surface, device,
557                               wlsc_compositor_get_time());
558
559         meego_tablet_shell_set_state(shell, STATE_HOME);
560 }
561
562 static int
563 long_press_handler(void *data)
564 {
565         struct meego_tablet_shell *shell = data;
566
567         shell->long_press_active = 0;
568         toggle_switcher(shell);
569
570         return 1;
571 }
572
573 static void
574 menu_key_binding(struct wl_input_device *device, uint32_t time,
575                  uint32_t key, uint32_t button, uint32_t state, void *data)
576 {
577         struct meego_tablet_shell *shell = data;
578
579         if (shell->state == STATE_LOCKED)
580                 return;
581
582         if (state)
583                 toggle_switcher(shell);
584 }
585
586 static void
587 home_key_binding(struct wl_input_device *device, uint32_t time,
588                  uint32_t key, uint32_t button, uint32_t state, void *data)
589 {
590         struct meego_tablet_shell *shell = data;
591
592         if (shell->state == STATE_LOCKED)
593                 return;
594
595         shell->device = (struct wlsc_input_device *) device;
596
597         if (state) {
598                 wl_event_source_timer_update(shell->long_press_source, 500);
599                 shell->long_press_active = 1;
600         } else if (shell->long_press_active) {
601                 wl_event_source_timer_update(shell->long_press_source, 0);
602                 shell->long_press_active = 0;
603
604                 switch (shell->state) {
605                 case STATE_HOME:
606                 case STATE_SWITCHER:
607                         toggle_switcher(shell);
608                         break;
609                 default:
610                         go_home(shell);
611                         break;
612                 }
613         }
614 }
615
616 static void
617 meego_tablet_shell_set_selection_focus(struct wlsc_shell *shell,
618                                        struct wl_selection *selection,
619                                        struct wl_surface *surface,
620                                        uint32_t time)
621 {
622 }
623
624 static void
625 bind_shell(struct wl_client *client,
626            struct wl_object *global, uint32_t version)
627 {
628         struct meego_tablet_shell *shell =
629                 container_of(global,
630                              struct meego_tablet_shell, resource.object);
631
632         if (shell->client != client)
633                 /* Throw an error or just let the client fail when it
634                  * tries to access the object?. */
635                 return;
636
637         shell->resource.client = client;
638 }
639
640 void
641 shell_init(struct wlsc_compositor *compositor);
642
643 WL_EXPORT void
644 shell_init(struct wlsc_compositor *compositor)
645 {
646         struct meego_tablet_shell *shell;
647         struct wl_event_loop *loop;
648
649         shell = malloc(sizeof *shell);
650         if (shell == NULL)
651                 return;
652
653         memset(shell, 0, sizeof *shell);
654         shell->compositor = compositor;
655
656         shell->resource.object.interface = &meego_tablet_shell_interface;
657         shell->resource.object.implementation =
658                 (void (**)(void)) &tablet_shell_interface;
659         wl_display_add_object(compositor->wl_display, &shell->resource.object);
660
661         /* FIXME: This will make the object available to all clients. */
662         wl_display_add_global(compositor->wl_display,
663                               &shell->resource.object, bind_shell);
664
665         loop = wl_display_get_event_loop(compositor->wl_display);
666         shell->long_press_source =
667                 wl_event_loop_add_timer(loop, long_press_handler, shell);
668
669         wlsc_compositor_add_binding(compositor, KEY_LEFTMETA, 0, 0,
670                                     home_key_binding, shell);
671         wlsc_compositor_add_binding(compositor, KEY_RIGHTMETA, 0, 0,
672                                     home_key_binding, shell);
673         wlsc_compositor_add_binding(compositor, KEY_LEFTMETA, 0,
674                                     MODIFIER_SUPER, home_key_binding, shell);
675         wlsc_compositor_add_binding(compositor, KEY_RIGHTMETA, 0,
676                                     MODIFIER_SUPER, home_key_binding, shell);
677         wlsc_compositor_add_binding(compositor, KEY_COMPOSE, 0, 0,
678                                     menu_key_binding, shell);
679
680         compositor->shell = &shell->shell;
681
682         shell->shell.lock = meego_tablet_shell_lock;
683         shell->shell.attach = meego_tablet_shell_attach;
684         shell->shell.set_selection_focus =
685                 meego_tablet_shell_set_selection_focus;
686         launch_ux_daemon(shell);
687
688         wlsc_spring_init(&compositor->fade.spring, 40.0, 1.0, 1.0);
689         meego_tablet_shell_set_state(shell, STATE_STARTING);
690 }