adf3a5c0d00a040bf6c151ed9ac54a9070c1916c
[profile/ivi/weston.git] / clients / desktop-shell.c
1 /*
2  * Copyright © 2011 Kristian Høgsberg
3  * Copyright © 2011 Collabora, Ltd.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting documentation, and
9  * that the name of the copyright holders not be used in advertising or
10  * publicity pertaining to distribution of the software without specific,
11  * written prior permission.  The copyright holders make no representations
12  * about the suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21  * OF THIS SOFTWARE.
22  */
23
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <math.h>
31 #include <cairo.h>
32 #include <sys/wait.h>
33 #include <linux/input.h>
34
35 #include "wayland-client.h"
36 #include "cairo-util.h"
37 #include "window.h"
38
39 #include <desktop-shell-client-protocol.h>
40
41 struct desktop {
42         struct display *display;
43         struct desktop_shell *shell;
44         struct panel *panel;
45         struct window *background;
46         const char *background_path;
47         struct unlock_dialog *unlock_dialog;
48         struct task unlock_task;
49 };
50
51 struct panel {
52         struct window *window;
53         struct window *menu;
54 };
55
56 struct panel_item {
57         struct item *item;
58         struct panel *panel;
59         cairo_surface_t *icon;
60         int pressed;
61         const char *path;
62 };
63
64 struct unlock_dialog {
65         struct window *window;
66         struct item *button;
67         int closing;
68
69         struct desktop *desktop;
70 };
71
72 static char *key_background_image;
73 static uint32_t key_panel_color;
74 static char *key_launcher_icon;
75 static char *key_launcher_path;
76 static void launcher_section_done(void *data);
77
78 static const struct config_key shell_config_keys[] = {
79         { "background-image", CONFIG_KEY_STRING, &key_background_image },
80         { "panel-color", CONFIG_KEY_INTEGER, &key_panel_color },
81 };
82
83 static const struct config_key launcher_config_keys[] = {
84         { "icon", CONFIG_KEY_STRING, &key_launcher_icon },
85         { "path", CONFIG_KEY_STRING, &key_launcher_path },
86 };
87
88 static const struct config_section config_sections[] = {
89         { "wayland-desktop-shell",
90           shell_config_keys, ARRAY_LENGTH(shell_config_keys) },
91         { "launcher",
92           launcher_config_keys, ARRAY_LENGTH(launcher_config_keys),
93           launcher_section_done }
94 };
95
96 static void
97 sigchild_handler(int s)
98 {
99         int status;
100         pid_t pid;
101
102         while (pid = waitpid(-1, &status, WNOHANG), pid > 0)
103                 fprintf(stderr, "child %d exited\n", pid);
104 }
105
106 static void
107 show_menu(struct panel *panel, struct input *input)
108 {
109         int32_t x, y, width = 200, height = 200;
110         struct display *display;
111
112         input_get_position(input, &x, &y);
113         display = window_get_display(panel->window);
114         panel->menu = window_create_transient(display, panel->window,
115                                               x - 10, y - 10, width, height);
116
117         window_draw(panel->menu);
118         window_flush(panel->menu);
119 }
120
121 static void
122 panel_activate_item(struct panel *panel, struct panel_item *item)
123 {
124         pid_t pid;
125
126         pid = fork();
127         if (pid < 0) {
128                 fprintf(stderr, "fork failed: %m\n");
129                 return;
130         }
131
132         if (pid)
133                 return;
134         
135         if (execl(item->path, item->path, NULL) < 0) {
136                 fprintf(stderr, "execl failed: %m\n");
137                 exit(1);
138         }
139 }
140
141 static void
142 panel_draw_item(struct item *item, void *data)
143 {
144         cairo_t *cr = data;
145         struct panel_item *pi;
146         int x, y, width, height;
147         double dx, dy;
148
149         pi = item_get_user_data(item);
150         width = cairo_image_surface_get_width(pi->icon);
151         height = cairo_image_surface_get_height(pi->icon);
152         x = 0;
153         y = -height / 2;
154         if (pi->pressed) {
155                 x++;
156                 y++;
157         }
158
159         dx = x;
160         dy = y;
161         cairo_user_to_device(cr, &dx, &dy);
162         item_set_allocation(item, dx, dy, width, height);
163
164         cairo_set_source_surface(cr, pi->icon, x, y);
165         cairo_paint(cr);
166
167         if (window_get_focus_item(pi->panel->window) == item) {
168                 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.4);
169                 cairo_mask_surface(cr, pi->icon, x, y);
170         }
171
172         cairo_translate(cr, width + 10, 0);
173 }
174
175 static void
176 set_hex_color(cairo_t *cr, uint32_t color)
177 {
178         cairo_set_source_rgba(cr, 
179                               ((color >> 16) & 0xff) / 255.0,
180                               ((color >>  8) & 0xff) / 255.0,
181                               ((color >>  0) & 0xff) / 255.0,
182                               ((color >> 24) & 0xff) / 255.0);
183 }
184
185 static void
186 panel_redraw_handler(struct window *window, void *data)
187 {
188         cairo_surface_t *surface;
189         cairo_t *cr;
190
191         window_draw(window);
192         surface = window_get_surface(window);
193         cr = cairo_create(surface);
194         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
195         set_hex_color(cr, key_panel_color);
196         cairo_paint(cr);
197
198         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
199         cairo_translate(cr, 10, 32 / 2);
200         window_for_each_item(window, panel_draw_item, cr);
201
202         cairo_destroy(cr);
203         cairo_surface_destroy(surface);
204         window_flush(window);
205 }
206
207 static void
208 panel_item_focus_handler(struct window *window,
209                          struct item *focus, void *data)
210 {
211         window_schedule_redraw(window);
212 }
213
214 static void
215 panel_button_handler(struct window *window,
216                      struct input *input, uint32_t time,
217                      int button, int state, void *data)
218 {
219         struct panel *panel = data;
220         struct panel_item *pi;
221         struct item *focus;
222
223         focus = window_get_focus_item(panel->window);
224         if (focus && button == BTN_LEFT) {
225                 pi = item_get_user_data(focus);
226                 window_schedule_redraw(panel->window);
227                 if (state == 0)
228                         panel_activate_item(panel, pi);
229         } else if (button == BTN_RIGHT) {
230                 if (state)
231                         show_menu(panel, input);
232                 else
233                         window_destroy(panel->menu);
234         }
235 }
236
237 static struct panel *
238 panel_create(struct display *display)
239 {
240         struct panel *panel;
241
242         panel = malloc(sizeof *panel);
243         memset(panel, 0, sizeof *panel);
244
245         panel->window = window_create(display, 0, 0);
246
247         window_set_title(panel->window, "panel");
248         window_set_decoration(panel->window, 0);
249         window_set_redraw_handler(panel->window, panel_redraw_handler);
250         window_set_custom(panel->window);
251         window_set_user_data(panel->window, panel);
252         window_set_button_handler(panel->window, panel_button_handler);
253         window_set_item_focus_handler(panel->window, panel_item_focus_handler);
254
255         return panel;
256 }
257
258 static void
259 panel_add_item(struct panel *panel, const char *icon, const char *path)
260 {
261         struct panel_item *item;
262
263         item = malloc(sizeof *item);
264         memset(item, 0, sizeof *item);
265         item->icon = cairo_image_surface_create_from_png(icon);
266         item->path = strdup(path);
267         item->panel = panel;
268         window_add_item(panel->window, item);
269 }
270
271 static void
272 background_draw(struct window *window, int width, int height, const char *path)
273 {
274         cairo_surface_t *surface, *image;
275         cairo_pattern_t *pattern;
276         cairo_matrix_t matrix;
277         cairo_t *cr;
278         double sx, sy;
279
280         window_set_child_size(window, width, height);
281         window_draw(window);
282         surface = window_get_surface(window);
283
284         cr = cairo_create(surface);
285         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
286         cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0);
287         cairo_paint(cr);
288
289         if (path) {
290                 image = load_jpeg(path);
291                 pattern = cairo_pattern_create_for_surface(image);
292                 sx = (double) cairo_image_surface_get_width(image) / width;
293                 sy = (double) cairo_image_surface_get_height(image) / height;
294                 cairo_matrix_init_scale(&matrix, sx, sy);
295                 cairo_pattern_set_matrix(pattern, &matrix);
296                 cairo_set_source(cr, pattern);
297                 cairo_pattern_destroy (pattern);
298                 cairo_paint(cr);
299                 cairo_surface_destroy(image);
300         }
301
302         cairo_destroy(cr);
303         cairo_surface_destroy(surface);
304         window_flush(window);
305 }
306
307 static void
308 unlock_dialog_draw(struct unlock_dialog *dialog)
309 {
310         struct rectangle allocation;
311         cairo_t *cr;
312         cairo_surface_t *surface;
313         cairo_pattern_t *pat;
314         double cx, cy, r, f;
315
316         window_draw(dialog->window);
317
318         surface = window_get_surface(dialog->window);
319         cr = cairo_create(surface);
320         window_get_child_allocation(dialog->window, &allocation);
321         cairo_rectangle(cr, allocation.x, allocation.y,
322                         allocation.width, allocation.height);
323         cairo_clip(cr);
324         cairo_push_group(cr);
325         cairo_translate(cr, allocation.x, allocation.y);
326
327         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
328         cairo_set_source_rgba(cr, 0, 0, 0, 0.6);
329         cairo_paint(cr);
330
331         if (window_get_focus_item(dialog->window) == dialog->button)
332                 f = 1.0;
333         else
334                 f = 0.7;
335
336         cx = allocation.width / 2.0;
337         cy = allocation.height / 2.0;
338         r = (cx < cy ? cx : cy) * 0.4;
339         pat = cairo_pattern_create_radial(cx, cy, r * 0.7, cx, cy, r);
340         cairo_pattern_add_color_stop_rgb(pat, 0.0, 0, 0.86 * f, 0);
341         cairo_pattern_add_color_stop_rgb(pat, 0.85, 0.2 * f, f, 0.2 * f);
342         cairo_pattern_add_color_stop_rgb(pat, 1.0, 0, 0.86 * f, 0);
343         cairo_set_source(cr, pat);
344         cairo_arc(cr, cx, cy, r, 0.0, 2.0 * M_PI);
345         cairo_fill(cr);
346
347         item_set_allocation(dialog->button,
348                             allocation.x + cx - r,
349                             allocation.y + cy - r, 2 * r, 2 * r);
350         cairo_pattern_destroy(pat);
351
352         cairo_pop_group_to_source(cr);
353         cairo_paint(cr);
354         cairo_destroy(cr);
355
356         window_flush(dialog->window);
357         cairo_surface_destroy(surface);
358 }
359
360 static void
361 unlock_dialog_button_handler(struct window *window,
362                              struct input *input, uint32_t time,
363                              int button, int state, void *data)
364 {
365         struct unlock_dialog *dialog = data;
366         struct desktop *desktop = dialog->desktop;
367         struct item *focus;
368
369         focus = window_get_focus_item(dialog->window);
370         if (focus && button == BTN_LEFT) {
371                 if (state == 0 && !dialog->closing) {
372                         display_defer(desktop->display, &desktop->unlock_task);
373                         dialog->closing = 1;
374                 }
375         }
376 }
377
378 static void
379 unlock_dialog_redraw_handler(struct window *window, void *data)
380 {
381         struct unlock_dialog *dialog = data;
382
383         unlock_dialog_draw(dialog);
384 }
385
386 static void
387 unlock_dialog_keyboard_focus_handler(struct window *window,
388                                      struct input *device, void *data)
389 {
390         window_schedule_redraw(window);
391 }
392
393 static void
394 unlock_dialog_item_focus_handler(struct window *window,
395                          struct item *focus, void *data)
396 {
397         window_schedule_redraw(window);
398 }
399
400 static struct unlock_dialog *
401 unlock_dialog_create(struct desktop *desktop)
402 {
403         struct display *display = desktop->display;
404         struct unlock_dialog *dialog;
405
406         dialog = malloc(sizeof *dialog);
407         if (!dialog)
408                 return NULL;
409         memset(dialog, 0, sizeof *dialog);
410
411         dialog->window = window_create(display, 260, 230);
412         window_set_title(dialog->window, "Unlock your desktop");
413
414         window_set_user_data(dialog->window, dialog);
415         window_set_redraw_handler(dialog->window, unlock_dialog_redraw_handler);
416         window_set_keyboard_focus_handler(dialog->window,
417                                           unlock_dialog_keyboard_focus_handler);
418         window_set_button_handler(dialog->window, unlock_dialog_button_handler);
419         window_set_item_focus_handler(dialog->window,
420                                       unlock_dialog_item_focus_handler);
421         dialog->button = window_add_item(dialog->window, NULL);
422
423         desktop_shell_set_lock_surface(desktop->shell,
424                                        window_get_wl_surface(dialog->window));
425
426         unlock_dialog_draw(dialog);
427
428         return dialog;
429 }
430
431 static void
432 unlock_dialog_destroy(struct unlock_dialog *dialog)
433 {
434         window_destroy(dialog->window);
435         free(dialog);
436 }
437
438 static void
439 unlock_dialog_finish(struct task *task, uint32_t events)
440 {
441         struct desktop *desktop =
442                         container_of(task, struct desktop, unlock_task);
443
444         desktop_shell_unlock(desktop->shell);
445         unlock_dialog_destroy(desktop->unlock_dialog);
446         desktop->unlock_dialog = NULL;
447 }
448
449 static void
450 desktop_shell_configure(void *data,
451                         struct desktop_shell *desktop_shell,
452                         uint32_t time, uint32_t edges,
453                         struct wl_surface *surface,
454                         int32_t width, int32_t height)
455 {
456         struct desktop *desktop = data;
457
458         if (surface == window_get_wl_surface(desktop->panel->window)) {
459                 window_set_child_size(desktop->panel->window, width, 32);
460                 window_schedule_redraw(desktop->panel->window);
461         } else if (surface == window_get_wl_surface(desktop->background)) {
462                 background_draw(desktop->background,
463                                 width, height, desktop->background_path);
464         }
465 }
466
467 static void
468 desktop_shell_prepare_lock_surface(void *data,
469                                    struct desktop_shell *desktop_shell)
470 {
471         struct desktop *desktop = data;
472
473         if (!desktop->unlock_dialog) {
474                 desktop->unlock_dialog = unlock_dialog_create(desktop);
475                 desktop->unlock_dialog->desktop = desktop;
476         }
477 }
478
479 static const struct desktop_shell_listener listener = {
480         desktop_shell_configure,
481         desktop_shell_prepare_lock_surface
482 };
483
484 static void
485 global_handler(struct wl_display *display, uint32_t id,
486                const char *interface, uint32_t version, void *data)
487 {
488         struct desktop *desktop = data;
489
490         if (!strcmp(interface, "desktop_shell")) {
491                 desktop->shell =
492                         wl_display_bind(display, id, &desktop_shell_interface);
493                 desktop_shell_add_listener(desktop->shell, &listener, desktop);
494         }
495 }
496
497 static void
498 launcher_section_done(void *data)
499 {
500         struct desktop *desktop = data;
501
502         if (key_launcher_icon == NULL || key_launcher_path == NULL) {
503                 fprintf(stderr, "invalid launcher section\n");
504                 return;
505         }
506
507         panel_add_item(desktop->panel, key_launcher_icon, key_launcher_path);
508         free(key_launcher_icon);
509         key_launcher_icon = NULL;
510         free(key_launcher_path);
511         key_launcher_path = NULL;
512 }
513
514 int main(int argc, char *argv[])
515 {
516         struct desktop desktop = { 0 };
517         char *config_file;
518
519         desktop.unlock_task.run = unlock_dialog_finish;
520
521         desktop.display = display_create(&argc, &argv, NULL);
522         if (desktop.display == NULL) {
523                 fprintf(stderr, "failed to create display: %m\n");
524                 return -1;
525         }
526
527         /* The fd is our private, do not confuse our children with it. */
528         unsetenv("WAYLAND_SOCKET");
529
530         wl_display_add_global_listener(display_get_display(desktop.display),
531                                        global_handler, &desktop);
532
533         desktop.panel = panel_create(desktop.display);
534
535         config_file = config_file_path("wayland-desktop-shell.ini");
536         parse_config_file(config_file,
537                           config_sections, ARRAY_LENGTH(config_sections),
538                           &desktop);
539         free(config_file);
540
541         desktop_shell_set_panel(desktop.shell,
542                                 window_get_wl_surface(desktop.panel->window));
543
544         desktop.background = window_create(desktop.display, 0, 0);
545         window_set_decoration(desktop.background, 0);
546         window_set_custom(desktop.background);
547         desktop.background_path = key_background_image;
548         desktop_shell_set_background(desktop.shell,
549                                      window_get_wl_surface(desktop.background));
550
551         signal(SIGCHLD, sigchild_handler);
552
553         display_run(desktop.display);
554
555         return 0;
556 }