blob: 2ba4fcd0824028464eec0b20446a7eadbacadb4d [file] [log] [blame]
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -04001/*
2 * Copyright © 2011 Kristian Høgsberg
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +02003 * Copyright © 2011 Collabora, Ltd.
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -04004 *
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"
Kristian Høgsberg27d38662011-10-20 13:11:12 -040036#include "cairo-util.h"
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -040037#include "window.h"
38
39#include <desktop-shell-client-protocol.h>
40
41struct desktop {
42 struct display *display;
43 struct desktop_shell *shell;
44 struct panel *panel;
45 struct window *background;
46 const char *background_path;
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +020047 struct unlock_dialog *unlock_dialog;
48 struct task unlock_task;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -040049};
50
51struct panel {
52 struct window *window;
Kristian Høgsbergbcee9a42011-10-12 00:36:16 -040053 struct window *menu;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -040054};
55
56struct panel_item {
Kristian Høgsberge28d05b2011-09-20 21:43:54 -040057 struct item *item;
58 struct panel *panel;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -040059 cairo_surface_t *icon;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -040060 int pressed;
61 const char *path;
62};
63
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +020064struct unlock_dialog {
65 struct window *window;
66 struct item *button;
67 int closing;
68
69 struct desktop *desktop;
70};
71
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -050072static char *key_background_image;
73static uint32_t key_panel_color;
74static char *key_launcher_icon;
75static char *key_launcher_path;
76static void launcher_section_done(void *data);
77
78static 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
83static 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
88static 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
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -040096static void
97sigchild_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
106static void
Kristian Høgsbergbcee9a42011-10-12 00:36:16 -0400107show_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
121static void
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400122panel_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
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400141static void
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400142panel_draw_item(struct item *item, void *data)
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400143{
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400144 cairo_t *cr = data;
145 struct panel_item *pi;
146 int x, y, width, height;
147 double dx, dy;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400148
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400149 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++;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400157 }
158
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400159 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);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400165 cairo_paint(cr);
166
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400167 if (window_get_focus_item(pi->panel->window) == item) {
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400168 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.4);
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400169 cairo_mask_surface(cr, pi->icon, x, y);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400170 }
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400171
172 cairo_translate(cr, width + 10, 0);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400173}
174
175static void
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500176set_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
185static void
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400186panel_redraw_handler(struct window *window, void *data)
187{
188 cairo_surface_t *surface;
189 cairo_t *cr;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400190
191 window_draw(window);
192 surface = window_get_surface(window);
193 cr = cairo_create(surface);
194 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500195 set_hex_color(cr, key_panel_color);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400196 cairo_paint(cr);
197
198 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400199 cairo_translate(cr, 10, 32 / 2);
200 window_for_each_item(window, panel_draw_item, cr);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400201
202 cairo_destroy(cr);
203 cairo_surface_destroy(surface);
204 window_flush(window);
205}
206
207static void
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400208panel_item_focus_handler(struct window *window,
209 struct item *focus, void *data)
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400210{
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400211 window_schedule_redraw(window);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400212}
213
214static void
215panel_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;
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400220 struct panel_item *pi;
221 struct item *focus;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400222
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400223 focus = window_get_focus_item(panel->window);
224 if (focus && button == BTN_LEFT) {
225 pi = item_get_user_data(focus);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400226 window_schedule_redraw(panel->window);
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400227 if (state == 0)
228 panel_activate_item(panel, pi);
Kristian Høgsbergbcee9a42011-10-12 00:36:16 -0400229 } else if (button == BTN_RIGHT) {
230 if (state)
231 show_menu(panel, input);
232 else
233 window_destroy(panel->menu);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400234 }
235}
236
237static struct panel *
238panel_create(struct display *display)
239{
240 struct panel *panel;
241
242 panel = malloc(sizeof *panel);
243 memset(panel, 0, sizeof *panel);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400244
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);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400252 window_set_button_handler(panel->window, panel_button_handler);
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400253 window_set_item_focus_handler(panel->window, panel_item_focus_handler);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400254
255 return panel;
256}
257
258static void
259panel_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);
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400267 item->panel = panel;
268 window_add_item(panel->window, item);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400269}
270
271static void
272background_draw(struct window *window, int width, int height, const char *path)
273{
274 cairo_surface_t *surface, *image;
Kristian Høgsberg7e690002011-09-08 18:18:02 -0400275 cairo_pattern_t *pattern;
276 cairo_matrix_t matrix;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400277 cairo_t *cr;
Kristian Høgsberg7e690002011-09-08 18:18:02 -0400278 double sx, sy;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400279
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) {
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400290 image = load_jpeg(path);
Kristian Høgsberg7e690002011-09-08 18:18:02 -0400291 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);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400298 cairo_paint(cr);
Kristian Høgsberg27d38662011-10-20 13:11:12 -0400299 cairo_surface_destroy(image);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400300 }
301
302 cairo_destroy(cr);
303 cairo_surface_destroy(surface);
304 window_flush(window);
305}
306
307static void
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +0200308unlock_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
360static void
361unlock_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
378static void
379unlock_dialog_redraw_handler(struct window *window, void *data)
380{
381 struct unlock_dialog *dialog = data;
382
383 unlock_dialog_draw(dialog);
384}
385
386static void
387unlock_dialog_keyboard_focus_handler(struct window *window,
388 struct input *device, void *data)
389{
390 window_schedule_redraw(window);
391}
392
393static void
394unlock_dialog_item_focus_handler(struct window *window,
395 struct item *focus, void *data)
396{
397 window_schedule_redraw(window);
398}
399
400static struct unlock_dialog *
401unlock_dialog_create(struct display *display)
402{
403 struct unlock_dialog *dialog;
404
405 dialog = malloc(sizeof *dialog);
406 if (!dialog)
407 return NULL;
408 memset(dialog, 0, sizeof *dialog);
409
410 dialog->window = window_create(display, 260, 230);
411 window_set_title(dialog->window, "Unlock your desktop");
412
413 window_set_user_data(dialog->window, dialog);
414 window_set_redraw_handler(dialog->window, unlock_dialog_redraw_handler);
415 window_set_keyboard_focus_handler(dialog->window,
416 unlock_dialog_keyboard_focus_handler);
417 window_set_button_handler(dialog->window, unlock_dialog_button_handler);
418 window_set_item_focus_handler(dialog->window,
419 unlock_dialog_item_focus_handler);
420 dialog->button = window_add_item(dialog->window, NULL);
421
422 unlock_dialog_draw(dialog);
423
424 return dialog;
425}
426
427static void
428unlock_dialog_destroy(struct unlock_dialog *dialog)
429{
430 window_destroy(dialog->window);
431 free(dialog);
432}
433
434static void
435unlock_dialog_finish(struct task *task, uint32_t events)
436{
437 struct desktop *desktop =
438 container_of(task, struct desktop, unlock_task);
439
440 desktop_shell_unlock(desktop->shell);
441 unlock_dialog_destroy(desktop->unlock_dialog);
442 desktop->unlock_dialog = NULL;
443}
444
445static void
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400446desktop_shell_configure(void *data,
447 struct desktop_shell *desktop_shell,
448 uint32_t time, uint32_t edges,
449 struct wl_surface *surface,
450 int32_t width, int32_t height)
451{
452 struct desktop *desktop = data;
453
454 if (surface == window_get_wl_surface(desktop->panel->window)) {
Kristian Høgsberge28d05b2011-09-20 21:43:54 -0400455 window_set_child_size(desktop->panel->window, width, 32);
456 window_schedule_redraw(desktop->panel->window);
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400457 } else if (surface == window_get_wl_surface(desktop->background)) {
458 background_draw(desktop->background,
459 width, height, desktop->background_path);
460 }
461}
462
Pekka Paalanen9ef3e012011-11-15 13:34:48 +0200463static void
464desktop_shell_prepare_lock_surface(void *data,
465 struct desktop_shell *desktop_shell)
466{
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +0200467 struct desktop *desktop = data;
468
469 if (!desktop->unlock_dialog) {
470 desktop->unlock_dialog =
471 unlock_dialog_create(desktop->display);
472 desktop->unlock_dialog->desktop = desktop;
473 }
474
475 desktop_shell_set_lock_surface(desktop_shell,
476 window_get_wl_surface(desktop->unlock_dialog->window));
Pekka Paalanen9ef3e012011-11-15 13:34:48 +0200477}
478
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400479static const struct desktop_shell_listener listener = {
Pekka Paalanen9ef3e012011-11-15 13:34:48 +0200480 desktop_shell_configure,
481 desktop_shell_prepare_lock_surface
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400482};
483
484static void
485global_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
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500497static void
498launcher_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}
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400513
514int main(int argc, char *argv[])
515{
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +0200516 struct desktop desktop = { 0 };
Pekka Paalanen668dd562011-11-15 11:45:40 +0200517 char *config_file;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400518
Pekka Paalanenbfbb26b2011-11-15 13:34:56 +0200519 desktop.unlock_task.run = unlock_dialog_finish;
520
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400521 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
Pekka Paalanen6cd281a2011-11-03 14:11:32 +0200527 /* The fd is our private, do not confuse our children with it. */
528 unsetenv("WAYLAND_SOCKET");
529
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400530 wl_display_add_global_listener(display_get_display(desktop.display),
531 global_handler, &desktop);
532
533 desktop.panel = panel_create(desktop.display);
534
Pekka Paalanen668dd562011-11-15 11:45:40 +0200535 config_file = config_file_path("wayland-desktop-shell.ini");
536 parse_config_file(config_file,
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500537 config_sections, ARRAY_LENGTH(config_sections),
538 &desktop);
Pekka Paalanen668dd562011-11-15 11:45:40 +0200539 free(config_file);
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500540
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400541 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);
Kristian Høgsbergac3a59a2011-11-14 22:43:37 -0500547 desktop.background_path = key_background_image;
Kristian Høgsberg0c29eb22011-09-06 18:02:34 -0400548 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}