Replace fprintf() by weston_log()
[profile/ivi/weston.git] / src / 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 <string.h>
27 #include <linux/input.h>
28
29 #include "compositor.h"
30 #include "tablet-shell-server-protocol.h"
31 #include "log.h"
32
33 /*
34  * TODO: Don't fade back from black until we've received a lockscreen
35  * attachment.
36  */
37
38 enum {
39         STATE_STARTING,
40         STATE_LOCKED,
41         STATE_HOME,
42         STATE_SWITCHER,
43         STATE_TASK
44 };
45
46 struct tablet_shell {
47         struct wl_resource resource;
48
49         struct wl_listener lock_listener;
50         struct wl_listener unlock_listener;
51         struct wl_listener destroy_listener;
52
53         struct weston_compositor *compositor;
54         struct weston_process process;
55         struct wl_client *client;
56
57         struct weston_surface *surface;
58
59         struct weston_surface *lockscreen_surface;
60         struct wl_listener lockscreen_listener;
61         struct weston_layer lockscreen_layer;
62
63         struct weston_surface *home_surface;
64         struct weston_layer homescreen_layer;
65
66         struct weston_surface *switcher_surface;
67         struct wl_listener switcher_listener;
68
69         struct 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 tablet_client {
77         struct wl_resource resource;
78         struct tablet_shell *shell;
79         struct wl_client *client;
80         struct weston_surface *surface;
81         char *name;
82 };
83
84 static void
85 tablet_shell_destroy(struct wl_listener *listener, void *data);
86
87 static struct tablet_shell *
88 get_shell(struct weston_compositor *compositor)
89 {
90         struct wl_listener *l;
91
92         l = wl_signal_get(&compositor->destroy_signal, tablet_shell_destroy);
93         if (l)
94                 return container_of(l, struct tablet_shell, destroy_listener);
95
96         return NULL;
97 }
98
99 static void
100 tablet_shell_sigchld(struct weston_process *process, int status)
101 {
102         struct tablet_shell *shell =
103                 container_of(process, struct tablet_shell, process);
104
105         shell->process.pid = 0;
106
107         weston_log("weston-tablet-shell crashed, exit code %d\n", status);
108 }
109
110 static void
111 tablet_shell_set_state(struct tablet_shell *shell, int state)
112 {
113         static const char *states[] = {
114                 "STARTING", "LOCKED", "HOME", "SWITCHER", "TASK"
115         };
116
117         weston_log("switching to state %s (from %s)\n",
118                 states[state], states[shell->state]);
119         shell->previous_state = shell->state;
120         shell->state = state;
121 }
122
123 static void
124 tablet_shell_surface_configure(struct weston_surface *surface,
125                                int32_t sx, int32_t sy)
126 {
127         struct tablet_shell *shell = get_shell(surface->compositor);
128         int32_t width, height;
129
130         if (weston_surface_is_mapped(surface))
131                 return;
132
133         width = surface->buffer->width;
134         height = surface->buffer->height;
135
136         weston_surface_configure(surface, 0, 0, width, height);
137
138         if (surface == shell->lockscreen_surface) {
139                         wl_list_insert(&shell->lockscreen_layer.surface_list,
140                                         &surface->layer_link);
141         } else if (surface == shell->switcher_surface) {
142                 /* */
143         } else if (surface == shell->home_surface) {
144                 if (shell->state == STATE_STARTING) {
145                         /* homescreen always visible, at the bottom */
146                         wl_list_insert(&shell->homescreen_layer.surface_list,
147                                         &surface->layer_link);
148
149                         tablet_shell_set_state(shell, STATE_LOCKED);
150                         shell->previous_state = STATE_HOME;
151                         tablet_shell_send_show_lockscreen(&shell->resource);
152                 }
153         } else if (shell->current_client &&
154                    shell->current_client->surface != surface &&
155                    shell->current_client->client == surface->surface.resource.client) {
156                 tablet_shell_set_state(shell, STATE_TASK);
157                 shell->current_client->surface = surface;
158                 weston_zoom_run(surface, 0.3, 1.0, NULL, NULL);
159         }
160
161         wl_list_insert(&shell->compositor->surface_list, &surface->link);
162         weston_surface_assign_output(surface);
163 }
164
165 static void
166 handle_lockscreen_surface_destroy(struct wl_listener *listener, void *data)
167 {
168         struct tablet_shell *shell =
169                 container_of(listener,
170                              struct tablet_shell, lockscreen_listener);
171
172         shell->lockscreen_surface = NULL;
173         tablet_shell_set_state(shell, shell->previous_state);
174 }
175
176 static void
177 tablet_shell_set_lockscreen(struct wl_client *client,
178                             struct wl_resource *resource,
179                             struct wl_resource *surface_resource)
180 {
181         struct tablet_shell *shell = resource->data;
182         struct weston_surface *es = surface_resource->data;
183
184         weston_surface_set_position(es, 0, 0);
185         shell->lockscreen_surface = es;
186         shell->lockscreen_surface->configure = tablet_shell_surface_configure;
187         shell->lockscreen_listener.notify = handle_lockscreen_surface_destroy;
188         wl_signal_add(&es->surface.resource.destroy_signal,
189                       &shell->lockscreen_listener);
190 }
191
192 static void
193 handle_switcher_surface_destroy(struct wl_listener *listener, void *data)
194 {
195         struct tablet_shell *shell =
196                 container_of(listener,
197                              struct tablet_shell, switcher_listener);
198
199         shell->switcher_surface = NULL;
200         if (shell->state != STATE_LOCKED)
201                 tablet_shell_set_state(shell, shell->previous_state);
202 }
203
204 static void
205 tablet_shell_set_switcher(struct wl_client *client,
206                           struct wl_resource *resource,
207                           struct wl_resource *surface_resource)
208 {
209         struct tablet_shell *shell = resource->data;
210         struct weston_surface *es = surface_resource->data;
211
212         /* FIXME: Switcher should be centered and the compositor
213          * should do the tinting of the background.  With the cache
214          * layer idea, we should be able to hit the framerate on the
215          * fade/zoom in. */
216         shell->switcher_surface = es;
217         weston_surface_set_position(shell->switcher_surface, 0, 0);
218
219         shell->switcher_listener.notify = handle_switcher_surface_destroy;
220         wl_signal_add(&es->surface.resource.destroy_signal,
221                       &shell->switcher_listener);
222 }
223
224 static void
225 tablet_shell_set_homescreen(struct wl_client *client,
226                             struct wl_resource *resource,
227                             struct wl_resource *surface_resource)
228 {
229         struct tablet_shell *shell = resource->data;
230
231         shell->home_surface = surface_resource->data;
232         shell->home_surface->configure = tablet_shell_surface_configure;
233
234         weston_surface_set_position(shell->home_surface, 0, 0);
235 }
236
237 static void
238 minimize_zoom_done(struct weston_zoom *zoom, void *data)
239 {
240         struct tablet_shell *shell = data;
241         struct weston_compositor *compositor = shell->compositor;
242         struct weston_seat *seat;
243
244         wl_list_for_each(seat, &compositor->seat_list, link)
245                 weston_surface_activate(shell->home_surface, seat);
246 }
247
248 static void
249 tablet_shell_switch_to(struct tablet_shell *shell,
250                              struct weston_surface *surface)
251 {
252         struct weston_compositor *compositor = shell->compositor;
253         struct weston_seat *seat;
254         struct weston_surface *current;
255
256         if (shell->state == STATE_SWITCHER) {
257                 wl_list_remove(&shell->switcher_listener.link);
258                 shell->switcher_surface = NULL;
259         };
260
261         if (surface == shell->home_surface) {
262                 tablet_shell_set_state(shell, STATE_HOME);
263
264                 if (shell->current_client && shell->current_client->surface) {
265                         current = shell->current_client->surface;
266                         weston_zoom_run(current, 1.0, 0.3,
267                                       minimize_zoom_done, shell);
268                 }
269         } else {
270                 fprintf(stderr, "switch to %p\n", surface);
271                 wl_list_for_each(seat, &compositor->seat_list, link)
272                         weston_surface_activate(surface, seat);
273                 tablet_shell_set_state(shell, STATE_TASK);
274                 weston_zoom_run(surface, 0.3, 1.0, NULL, NULL);
275         }
276 }
277
278 static void
279 tablet_shell_show_grid(struct wl_client *client,
280                        struct wl_resource *resource,
281                        struct wl_resource *surface_resource)
282 {
283         struct tablet_shell *shell = resource->data;
284         struct weston_surface *es = surface_resource->data;
285
286         tablet_shell_switch_to(shell, es);
287 }
288
289 static void
290 tablet_shell_show_panels(struct wl_client *client,
291                          struct wl_resource *resource,
292                          struct wl_resource *surface_resource)
293 {
294         struct tablet_shell *shell = resource->data;
295         struct weston_surface *es = surface_resource->data;
296
297         tablet_shell_switch_to(shell, es);
298 }
299
300 static void
301 destroy_tablet_client(struct wl_resource *resource)
302 {
303         struct tablet_client *tablet_client =
304                 container_of(resource, struct tablet_client, resource);
305
306         free(tablet_client->name);
307         free(tablet_client);
308 }
309
310 static void
311 tablet_client_destroy(struct wl_client *client,
312                       struct wl_resource *resource)
313 {
314         wl_resource_destroy(resource);
315 }
316
317 static void
318 tablet_client_activate(struct wl_client *client, struct wl_resource *resource)
319 {
320         struct tablet_client *tablet_client = resource->data;
321         struct tablet_shell *shell = tablet_client->shell;
322
323         shell->current_client = tablet_client;
324         if (!tablet_client->surface)
325                 return;
326
327         tablet_shell_switch_to(shell, tablet_client->surface);
328 }
329
330 static const struct tablet_client_interface tablet_client_implementation = {
331         tablet_client_destroy,
332         tablet_client_activate
333 };
334
335 static void
336 tablet_shell_create_client(struct wl_client *client,
337                            struct wl_resource *resource,
338                            uint32_t id, const char *name, int fd)
339 {
340         struct tablet_shell *shell = resource->data;
341         struct weston_compositor *compositor = shell->compositor;
342         struct tablet_client *tablet_client;
343
344         tablet_client = malloc(sizeof *tablet_client);
345         if (tablet_client == NULL) {
346                 wl_resource_post_no_memory(resource);
347                 return;
348         }
349
350         tablet_client->client = wl_client_create(compositor->wl_display, fd);
351         tablet_client->shell = shell;
352         tablet_client->name = strdup(name);
353
354         tablet_client->resource.destroy = destroy_tablet_client;
355         tablet_client->resource.object.id = id;
356         tablet_client->resource.object.interface =
357                 &tablet_client_interface;
358         tablet_client->resource.object.implementation =
359                 (void (**)(void)) &tablet_client_implementation;
360
361         wl_client_add_resource(client, &tablet_client->resource);
362         tablet_client->surface = NULL;
363         shell->current_client = tablet_client;
364
365         weston_log("created client %p, id %d, name %s, fd %d\n",
366                 tablet_client->client, id, name, fd);
367 }
368
369 static const struct tablet_shell_interface tablet_shell_implementation = {
370         tablet_shell_set_lockscreen,
371         tablet_shell_set_switcher,
372         tablet_shell_set_homescreen,
373         tablet_shell_show_grid,
374         tablet_shell_show_panels,
375         tablet_shell_create_client
376 };
377
378 static void
379 launch_ux_daemon(struct tablet_shell *shell)
380 {
381         const char *shell_exe = LIBEXECDIR "/weston-tablet-shell";
382
383         shell->client = weston_client_launch(shell->compositor,
384                                            &shell->process,
385                                            shell_exe, tablet_shell_sigchld);
386 }
387
388 static void
389 toggle_switcher(struct tablet_shell *shell)
390 {
391         switch (shell->state) {
392         case STATE_SWITCHER:
393                 tablet_shell_send_hide_switcher(&shell->resource);
394                 break;
395         default:
396                 tablet_shell_send_show_switcher(&shell->resource);
397                 tablet_shell_set_state(shell, STATE_SWITCHER);
398                 break;
399         }
400 }
401
402 static void
403 tablet_shell_lock(struct wl_listener *listener, void *data)
404 {
405         struct tablet_shell *shell =
406                 container_of(listener, struct tablet_shell, lock_listener);
407
408         if (shell->state == STATE_LOCKED)
409                 return;
410         if (shell->state == STATE_SWITCHER)
411                 tablet_shell_send_hide_switcher(&shell->resource);
412
413         tablet_shell_send_show_lockscreen(&shell->resource);
414         tablet_shell_set_state(shell, STATE_LOCKED);
415 }
416
417 static void
418 tablet_shell_unlock(struct wl_listener *listener, void *data)
419 {
420         struct tablet_shell *shell =
421                 container_of(listener, struct tablet_shell, lock_listener);
422
423         weston_compositor_wake(shell->compositor);
424 }
425
426 static void
427 go_home(struct tablet_shell *shell, struct weston_seat *seat)
428 {
429         if (shell->state == STATE_SWITCHER)
430                 tablet_shell_send_hide_switcher(&shell->resource);
431
432         weston_surface_activate(shell->home_surface, seat);
433
434         tablet_shell_set_state(shell, STATE_HOME);
435 }
436
437 static int
438 long_press_handler(void *data)
439 {
440         struct tablet_shell *shell = data;
441
442         shell->long_press_active = 0;
443         toggle_switcher(shell);
444
445         return 1;
446 }
447
448 static void
449 menu_key_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
450 {
451         struct tablet_shell *shell = data;
452
453         if (shell->state == STATE_LOCKED)
454                 return;
455
456         toggle_switcher(shell);
457 }
458
459 static void
460 home_key_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
461 {
462         struct tablet_shell *shell = data;
463
464         if (shell->state == STATE_LOCKED)
465                 return;
466
467         if (1) {
468                 wl_event_source_timer_update(shell->long_press_source, 500);
469                 shell->long_press_active = 1;
470         } else if (shell->long_press_active) {
471                 /* This code has never been run ... */
472                 wl_event_source_timer_update(shell->long_press_source, 0);
473                 shell->long_press_active = 0;
474
475                 switch (shell->state) {
476                 case STATE_HOME:
477                 case STATE_SWITCHER:
478                         toggle_switcher(shell);
479                         break;
480                 default:
481                         go_home(shell, (struct weston_seat *) seat);
482                         break;
483                 }
484         }
485 }
486
487 static void
488 destroy_tablet_shell(struct wl_resource *resource)
489 {
490 }
491
492 static void
493 bind_tablet_shell(struct wl_client *client, void *data, uint32_t version,
494                   uint32_t id)
495 {
496         struct tablet_shell *shell = data;
497
498         if (shell->client != client)
499                 /* Throw an error or just let the client fail when it
500                  * tries to access the object?. */
501                 return;
502
503         shell->resource.object.id = id;
504         shell->resource.object.interface = &tablet_shell_interface;
505         shell->resource.object.implementation =
506                 (void (**)(void)) &tablet_shell_implementation;
507         shell->resource.client = client;
508         shell->resource.data = shell;
509         shell->resource.destroy = destroy_tablet_shell;
510
511         wl_client_add_resource(client, &shell->resource);
512 }
513
514 static void
515 tablet_shell_destroy(struct wl_listener *listener, void *data)
516 {
517         struct tablet_shell *shell =
518                 container_of(listener, struct tablet_shell, destroy_listener);
519
520         if (shell->home_surface)
521                 shell->home_surface->configure = NULL;
522
523         if (shell->lockscreen_surface)
524                 shell->lockscreen_surface->configure = NULL;
525
526         wl_event_source_remove(shell->long_press_source);
527         free(shell);
528 }
529
530 void
531 shell_init(struct weston_compositor *compositor);
532
533 WL_EXPORT void
534 shell_init(struct weston_compositor *compositor)
535 {
536         struct tablet_shell *shell;
537         struct wl_event_loop *loop;
538
539         shell = malloc(sizeof *shell);
540         if (shell == NULL)
541                 return;
542
543         memset(shell, 0, sizeof *shell);
544         shell->compositor = compositor;
545
546         shell->destroy_listener.notify = tablet_shell_destroy;
547         wl_signal_add(&compositor->destroy_signal, &shell->destroy_listener);
548         shell->lock_listener.notify = tablet_shell_lock;
549         wl_signal_add(&compositor->lock_signal, &shell->lock_listener);
550         shell->unlock_listener.notify = tablet_shell_unlock;
551         wl_signal_add(&compositor->unlock_signal, &shell->unlock_listener);
552
553         /* FIXME: This will make the object available to all clients. */
554         wl_display_add_global(compositor->wl_display, &tablet_shell_interface,
555                               shell, bind_tablet_shell);
556
557         loop = wl_display_get_event_loop(compositor->wl_display);
558         shell->long_press_source =
559                 wl_event_loop_add_timer(loop, long_press_handler, shell);
560
561         weston_compositor_add_key_binding(compositor, KEY_LEFTMETA, 0,
562                                           home_key_binding, shell);
563         weston_compositor_add_key_binding(compositor, KEY_RIGHTMETA, 0,
564                                           home_key_binding, shell);
565         weston_compositor_add_key_binding(compositor, KEY_LEFTMETA,
566                                           MODIFIER_SUPER, home_key_binding,
567                                           shell);
568         weston_compositor_add_key_binding(compositor, KEY_RIGHTMETA,
569                                           MODIFIER_SUPER, home_key_binding,
570                                           shell);
571         weston_compositor_add_key_binding(compositor, KEY_COMPOSE, 0,
572                                           menu_key_binding, shell);
573
574         weston_layer_init(&shell->homescreen_layer,
575                           &compositor->cursor_layer.link);
576         weston_layer_init(&shell->lockscreen_layer,
577                           &compositor->cursor_layer.link);
578         launch_ux_daemon(shell);
579
580         tablet_shell_set_state(shell, STATE_STARTING);
581 }