maynard.c 14.9 KB
Newer Older
1
2
/*
 * Copyright (c) 2013 Tiago Vignatti
3
 * Copyright (c) 2013-2014 Collabora Ltd.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

Tiago Vignatti's avatar
Tiago Vignatti committed
24
25
26
27
28
29
30
#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkwayland.h>

#include "desktop-shell-client-protocol.h"
31
#include "shell-helper-client-protocol.h"
32

33
#include "maynard-resources.h"
34

35
#include "app-icon.h"
36
#include "clock.h"
37
#include "favorites.h"
38
#include "launcher.h"
39
40
#include "panel.h"
#include "vertical-clock.h"
Tiago Vignatti's avatar
Tiago Vignatti committed
41

42
43
extern char **environ; /* defined by libc */

44
gchar *filename = "/usr/share/themes/Adwaita/backgrounds/morning.jpg";
45
46
47
48
49
50

struct element {
	GtkWidget *window;
	GdkPixbuf *pixbuf;
	struct wl_surface *surface;
};
Tiago Vignatti's avatar
Tiago Vignatti committed
51
52
53
54
55
56

struct desktop {
	struct wl_display *display;
	struct wl_registry *registry;
	struct desktop_shell *shell;
	struct wl_output *output;
57
	struct shell_helper *helper;
Tiago Vignatti's avatar
Tiago Vignatti committed
58
59

	GdkDisplay *gdk_display;
Tiago Vignatti's avatar
Tiago Vignatti committed
60

61
62
	struct element *background;
	struct element *panel;
Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
63
	struct element *launcher_grid;
Jonny Lamb's avatar
Jonny Lamb committed
64
	struct element *clock;
65
66
67

	guint initial_panel_timeout_id;
	guint hide_panel_idle_id;
68
69

	gboolean grid_visible;
70
	gboolean volume_visible;
Tiago Vignatti's avatar
Tiago Vignatti committed
71
72
};

73
74
75
76
77
78
79
80
81
82
83
static gboolean panel_window_enter_cb (GtkWidget *widget,
				       GdkEventCrossing *event,
				       struct desktop *desktop);
static gboolean panel_window_leave_cb (GtkWidget *widget,
				       GdkEventCrossing *event,
				       struct desktop *desktop);

static gboolean
connect_enter_leave_signals (gpointer data)
{
	struct desktop *desktop = data;
84
	GList *l;
85
86
87
88
89
90

	g_signal_connect (desktop->panel->window, "enter-notify-event",
			  G_CALLBACK (panel_window_enter_cb), desktop);
	g_signal_connect (desktop->panel->window, "leave-notify-event",
			  G_CALLBACK (panel_window_leave_cb), desktop);

Jonny Lamb's avatar
Jonny Lamb committed
91
92
93
94
95
	g_signal_connect (desktop->clock->window, "enter-notify-event",
			  G_CALLBACK (panel_window_enter_cb), desktop);
	g_signal_connect (desktop->clock->window, "leave-notify-event",
			  G_CALLBACK (panel_window_leave_cb), desktop);

96
97
98
99
100
	g_signal_connect (desktop->launcher_grid->window, "enter-notify-event",
			  G_CALLBACK (panel_window_enter_cb), desktop);
	g_signal_connect (desktop->launcher_grid->window, "leave-notify-event",
			  G_CALLBACK (panel_window_leave_cb), desktop);

101
	return G_SOURCE_REMOVE;
102
103
}

Tiago Vignatti's avatar
Tiago Vignatti committed
104
105
106
107
108
109
110
static void
desktop_shell_configure(void *data,
		struct desktop_shell *desktop_shell,
		uint32_t edges,
		struct wl_surface *surface,
		int32_t width, int32_t height)
{
111
	struct desktop *desktop = data;
112
	int window_height;
113
	int grid_width, grid_height;
114
115
116

	gtk_widget_set_size_request (desktop->background->window,
				     width, height);
Tiago Vignatti's avatar
Tiago Vignatti committed
117

118
	/* TODO: make this height a little nicer */
119
	window_height = height * MAYNARD_PANEL_HEIGHT_RATIO;
120
	gtk_window_resize (GTK_WINDOW (desktop->panel->window),
121
			   MAYNARD_PANEL_WIDTH, window_height);
122

123
	maynard_launcher_calculate (MAYNARD_LAUNCHER(desktop->launcher_grid->window),
124
125
126
127
128
				       &grid_width, &grid_height, NULL);
	gtk_widget_set_size_request (desktop->launcher_grid->window,
				     grid_width,
				     grid_height);

129
130
131
	shell_helper_move_surface(desktop->helper,
				  desktop->panel->surface,
				  0, (height - window_height) / 2);
132

133
	gtk_window_resize (GTK_WINDOW (desktop->clock->window),
134
135
			   MAYNARD_CLOCK_WIDTH,
			   MAYNARD_CLOCK_HEIGHT);
Jonny Lamb's avatar
Jonny Lamb committed
136
137
138

	shell_helper_move_surface(desktop->helper,
				  desktop->clock->surface,
139
				  MAYNARD_PANEL_WIDTH,
140
				  (height - window_height) / 2);
Jonny Lamb's avatar
Jonny Lamb committed
141

142
143
	shell_helper_move_surface(desktop->helper,
				  desktop->launcher_grid->surface,
144
145
				  /* TODO: massive hack so window is always onscreen */
				  - grid_width + 1,
146
				  ((height - window_height) / 2) + MAYNARD_CLOCK_HEIGHT);
147

148
	desktop_shell_desktop_ready(desktop->shell);
149
150
151
152
153

	/* TODO: why does the panel signal leave on drawing for
	 * startup? we don't want to have to have this silly
	 * timeout. */
	g_timeout_add_seconds (1, connect_enter_leave_signals, desktop);
Tiago Vignatti's avatar
Tiago Vignatti committed
154
155
156
157
158
159
}

static void
desktop_shell_prepare_lock_surface(void *data,
		struct desktop_shell *desktop_shell)
{
160
	desktop_shell_unlock (desktop_shell);
Tiago Vignatti's avatar
Tiago Vignatti committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
}

static void
desktop_shell_grab_cursor(void *data, struct desktop_shell *desktop_shell,
		uint32_t cursor)
{
}

static const struct desktop_shell_listener listener = {
	desktop_shell_configure,
	desktop_shell_prepare_lock_surface,
	desktop_shell_grab_cursor
};

175
176
177
178
179
180
181
182
183
184
185
186
187
188
static void
launcher_grid_toggle (GtkWidget *widget, struct desktop *desktop)
{
	if (desktop->grid_visible) {
		shell_helper_slide_surface_back(desktop->helper,
						desktop->launcher_grid->surface);
	} else {
		int width;

		gtk_widget_get_size_request (desktop->launcher_grid->window,
					     &width, NULL);

		shell_helper_slide_surface(desktop->helper,
					   desktop->launcher_grid->surface,
189
190
					   /* TODO: massive hack so window is always onscreen */
					   width + MAYNARD_PANEL_WIDTH - 1, 0);
191
192
193
194
195
	}

	desktop->grid_visible = !desktop->grid_visible;
}

196
static void
Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
197
launcher_grid_create (struct desktop *desktop)
198
{
Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
199
	struct element *launcher_grid;
200
	GdkWindow *gdk_window;
201

Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
202
203
	launcher_grid = malloc (sizeof *launcher_grid);
	memset (launcher_grid, 0, sizeof *launcher_grid);
204

205
	launcher_grid->window = maynard_launcher_new (desktop->background->window);
206
207
	gdk_window = gtk_widget_get_window(launcher_grid->window);
	launcher_grid->surface = gdk_wayland_window_get_wl_surface(gdk_window);
Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
208

209
210
211
212
213
	gdk_wayland_window_set_use_custom_surface(gdk_window);
	shell_helper_add_surface_to_layer(desktop->helper,
					  launcher_grid->surface,
					  desktop->panel->surface);

214
215
216
	g_signal_connect(launcher_grid->window, "app-launched",
			 G_CALLBACK(launcher_grid_toggle), desktop);

217
	gtk_widget_show_all(launcher_grid->window);
Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
218
219
220
221

	desktop->launcher_grid = launcher_grid;
}

222
223
224
225
226
227
228
229
230
231
static void
volume_changed_cb (MaynardClock *clock,
		   gdouble value,
		   const gchar *icon_name,
		   struct desktop *desktop)
{
	maynard_panel_set_volume_icon_name (
		MAYNARD_PANEL (desktop->panel->window), icon_name);
}

Jonny Lamb's avatar
Jonny Lamb committed
232
233
234
235
236
237
238
239
240
static GtkWidget *
clock_create (struct desktop *desktop)
{
	struct element *clock;
	GdkWindow *gdk_window;

	clock = malloc(sizeof *clock);
	memset(clock, 0, sizeof *clock);

241
	clock->window = maynard_clock_new();
Jonny Lamb's avatar
Jonny Lamb committed
242

243
244
245
	g_signal_connect (clock->window, "volume-changed",
			  G_CALLBACK (volume_changed_cb), desktop);

Jonny Lamb's avatar
Jonny Lamb committed
246
247
248
	gdk_window = gtk_widget_get_window(clock->window);
	clock->surface = gdk_wayland_window_get_wl_surface(gdk_window);

249
250
251
252
	gdk_wayland_window_set_use_custom_surface(gdk_window);
	shell_helper_add_surface_to_layer(desktop->helper, clock->surface,
					  desktop->panel->surface);

Jonny Lamb's avatar
Jonny Lamb committed
253
254
255
256
257
	gtk_widget_show_all (clock->window);

	desktop->clock = clock;
}

258
259
260
261
262
263
264
265
266
267
268
269
static void
volume_toggled_cb (GtkWidget *widget,
		   struct desktop *desktop)
{
	desktop->volume_visible = !desktop->volume_visible;

	maynard_clock_show_volume (MAYNARD_CLOCK (desktop->clock->window),
				   desktop->volume_visible);
	maynard_panel_show_volume_previous (MAYNARD_PANEL (desktop->panel->window),
					    desktop->volume_visible);
}

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
static gboolean
panel_window_enter_cb (GtkWidget *widget,
		       GdkEventCrossing *event,
		       struct desktop *desktop)
{
	if (desktop->initial_panel_timeout_id > 0) {
		g_source_remove (desktop->initial_panel_timeout_id);
		desktop->initial_panel_timeout_id = 0;
	}

	if (desktop->hide_panel_idle_id > 0) {
		g_source_remove (desktop->hide_panel_idle_id);
		desktop->hide_panel_idle_id = 0;
		return;
	}

	shell_helper_slide_surface_back(desktop->helper,
					desktop->panel->surface);
Jonny Lamb's avatar
Jonny Lamb committed
288
289
	shell_helper_slide_surface_back(desktop->helper,
					desktop->clock->surface);
290

291
	maynard_panel_set_expand(MAYNARD_PANEL(desktop->panel->window),
292
				    TRUE);
Jonny Lamb's avatar
Jonny Lamb committed
293

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
	return FALSE;
}

static gboolean
leave_panel_idle_cb (gpointer data)
{
	struct desktop *desktop = data;
	gint width;

	desktop->hide_panel_idle_id = 0;

	gtk_window_get_size (GTK_WINDOW (desktop->clock->window),
			     &width, NULL);

	shell_helper_slide_surface(desktop->helper,
				   desktop->panel->surface,
310
				   MAYNARD_VERTICAL_CLOCK_WIDTH - MAYNARD_PANEL_WIDTH, 0);
Jonny Lamb's avatar
Jonny Lamb committed
311
312
	shell_helper_slide_surface(desktop->helper,
				   desktop->clock->surface,
313
				   MAYNARD_VERTICAL_CLOCK_WIDTH - MAYNARD_PANEL_WIDTH - width, 0);
314

315
	maynard_panel_set_expand(MAYNARD_PANEL(desktop->panel->window),
316
317
318
319
320
				 FALSE);

	maynard_clock_show_volume (MAYNARD_CLOCK (desktop->clock->window), FALSE);
	maynard_panel_show_volume_previous (MAYNARD_PANEL (desktop->panel->window), FALSE);
	desktop->volume_visible = FALSE;
Jonny Lamb's avatar
Jonny Lamb committed
321

322
	return G_SOURCE_REMOVE;
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
}

static gboolean
panel_window_leave_cb (GtkWidget *widget,
		       GdkEventCrossing *event,
		       struct desktop *desktop)
{
	if (desktop->initial_panel_timeout_id > 0) {
		g_source_remove (desktop->initial_panel_timeout_id);
		desktop->initial_panel_timeout_id = 0;
	}

	if (desktop->hide_panel_idle_id > 0)
		return;

338
339
340
	if (desktop->grid_visible)
		return;

341
342
343
344
345
346
347
348
349
350
351
	desktop->hide_panel_idle_id = g_idle_add (leave_panel_idle_cb, desktop);

	return FALSE;
}

static gboolean
panel_hide_timeout_cb (gpointer data)
{
	struct desktop *desktop = data;

	panel_window_leave_cb (NULL, NULL, desktop);
352
353

	return G_SOURCE_REMOVE;
354
355
}

356
357
358
359
static void
panel_create(struct desktop *desktop)
{
	struct element *panel;
360
	GdkWindow *gdk_window;
361
362
363
364

	panel = malloc(sizeof *panel);
	memset(panel, 0, sizeof *panel);

365
	panel->window = maynard_panel_new();
366

367
368
	g_signal_connect(panel->window, "app-menu-toggled",
			 G_CALLBACK(launcher_grid_toggle), desktop);
369
370
	g_signal_connect(panel->window, "volume-toggled",
			 G_CALLBACK(volume_toggled_cb), desktop);
371

372
373
	desktop->initial_panel_timeout_id =
		g_timeout_add_seconds(2, panel_hide_timeout_cb, desktop);
374

375
	/* set it up as the panel */
376
377
378
379
380
381
382
	gdk_window = gtk_widget_get_window(panel->window);
	gdk_wayland_window_set_use_custom_surface(gdk_window);

	panel->surface = gdk_wayland_window_get_wl_surface(gdk_window);
	desktop_shell_set_user_data(desktop->shell, desktop);
	desktop_shell_set_panel(desktop->shell, desktop->output,
				panel->surface);
383
384
	desktop_shell_set_panel_position(desktop->shell,
					 DESKTOP_SHELL_PANEL_POSITION_LEFT);
385
386
387
388
389
390

	gtk_widget_show_all(panel->window);

	desktop->panel = panel;
}

Tiago Vignatti's avatar
Tiago Vignatti committed
391
392
393
394
395
396
/* Expose callback for the drawing area */
static gboolean
draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
{
	struct desktop *desktop = data;

397
	gdk_cairo_set_source_pixbuf (cr, desktop->background->pixbuf, 0, 0);
Tiago Vignatti's avatar
Tiago Vignatti committed
398
399
400
401
402
403
404
405
406
407
408
409
	cairo_paint (cr);

	return TRUE;
}

/* Destroy handler for the window */
static void
destroy_cb (GObject *object, gpointer data)
{
	gtk_main_quit ();
}

Tiago Vignatti's avatar
Tiago Vignatti committed
410
411
412
static void
background_create(struct desktop *desktop)
{
Tiago Vignatti's avatar
Tiago Vignatti committed
413
	GdkWindow *gdk_window;
414
415
416
417
	struct element *background;

	background = malloc(sizeof *background);
	memset(background, 0, sizeof *background);
Tiago Vignatti's avatar
Tiago Vignatti committed
418

Tiago Vignatti's avatar
Tiago Vignatti committed
419
	/* TODO: get the "right" directory */
420
421
	background->pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
	if (!background->pixbuf) {
Tiago Vignatti's avatar
Tiago Vignatti committed
422
423
424
		g_message ("Could not load background.");
		exit (EXIT_FAILURE);
	}
Tiago Vignatti's avatar
Tiago Vignatti committed
425

426
	background->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Tiago Vignatti's avatar
Tiago Vignatti committed
427

428
	g_signal_connect (background->window, "destroy",
Tiago Vignatti's avatar
Tiago Vignatti committed
429
			  G_CALLBACK (destroy_cb), NULL);
Tiago Vignatti's avatar
Tiago Vignatti committed
430

431
	g_signal_connect (background->window, "draw",
Tiago Vignatti's avatar
Tiago Vignatti committed
432
			  G_CALLBACK (draw_cb), desktop);
Tiago Vignatti's avatar
Tiago Vignatti committed
433

434
	gtk_window_set_title(GTK_WINDOW(background->window), "maynard");
435
436
	gtk_window_set_decorated(GTK_WINDOW(background->window), FALSE);
	gtk_widget_realize(background->window);
Tiago Vignatti's avatar
Tiago Vignatti committed
437

438
	gdk_window = gtk_widget_get_window(background->window);
Tiago Vignatti's avatar
Tiago Vignatti committed
439
	gdk_wayland_window_set_use_custom_surface(gdk_window);
Tiago Vignatti's avatar
Tiago Vignatti committed
440

441
442
	background->surface = gdk_wayland_window_get_wl_surface(gdk_window);
	desktop_shell_set_user_data(desktop->shell, desktop);
Tiago Vignatti's avatar
Tiago Vignatti committed
443
	desktop_shell_set_background(desktop->shell, desktop->output,
444
445
446
		background->surface);

	desktop->background = background;
Tiago Vignatti's avatar
Tiago Vignatti committed
447

448
	gtk_widget_show_all(background->window);
Tiago Vignatti's avatar
Tiago Vignatti committed
449
450
}

451
452
453
454
455
456
457
458
459
static void
css_setup(struct desktop *desktop)
{
	GtkCssProvider *provider;
	GFile *file;
	GError *error = NULL;

	provider = gtk_css_provider_new ();

460
	file = g_file_new_for_uri ("resource:///org/raspberry-pi/maynard/style.css");
461
462
463
464
465
466
467
468
469
470
471
472
473
474

	if (!gtk_css_provider_load_from_file (provider, file, &error)) {
		g_warning ("Failed to load CSS file: %s", error->message);
		g_clear_error (&error);
		g_object_unref (file);
		return;
	}

	gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
						   GTK_STYLE_PROVIDER (provider), 600);

	g_object_unref (file);
}

Tiago Vignatti's avatar
Tiago Vignatti committed
475
476
477
478
479
480
481
482
static void
registry_handle_global(void *data, struct wl_registry *registry,
		uint32_t name, const char *interface, uint32_t version)
{
	struct desktop *d = data;

	if (!strcmp(interface, "desktop_shell")) {
		d->shell = wl_registry_bind(registry, name,
483
				&desktop_shell_interface, 3);
Tiago Vignatti's avatar
Tiago Vignatti committed
484
485
486
487
488
489
		desktop_shell_add_listener(d->shell, &listener, d);
	} else if (!strcmp(interface, "wl_output")) {

		/* TODO: create multiple outputs */
		d->output = wl_registry_bind(registry, name,
					     &wl_output_interface, 1);
490
491
492
	} else if (!strcmp(interface, "shell_helper")) {
		d->helper = wl_registry_bind(registry, name,
					     &shell_helper_interface, 1);
Tiago Vignatti's avatar
Tiago Vignatti committed
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
	}
}

static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
		uint32_t name)
{
}

static const struct wl_registry_listener registry_listener = {
	registry_handle_global,
	registry_handle_global_remove
};

int
main(int argc, char *argv[])
{
	struct desktop *desktop;

512
513
	gdk_set_allowed_backends ("wayland");

Tiago Vignatti's avatar
Tiago Vignatti committed
514
515
	gtk_init(&argc, &argv);

516
	g_resources_register (maynard_get_resource ());
517

Tiago Vignatti's avatar
Tiago Vignatti committed
518
519
520
	desktop = malloc(sizeof *desktop);
	desktop->output = NULL;
	desktop->shell = NULL;
521
	desktop->helper = NULL;
Tiago Vignatti's avatar
Tiago Vignatti committed
522
523
524
525
526
527
528
529
530
531
532
533
534

	desktop->gdk_display = gdk_display_get_default();
	desktop->display =
		gdk_wayland_display_get_wl_display(desktop->gdk_display);
	if (desktop->display == NULL) {
		fprintf(stderr, "failed to get display: %m\n");
		return -1;
	}

	desktop->registry = wl_display_get_registry(desktop->display);
	wl_registry_add_listener(desktop->registry,
			&registry_listener, desktop);

535
536
537
	/* Wait until we have been notified about the compositor,
	 * shell, and shell helper objects */
	while (!desktop->output || !desktop->shell || !desktop->helper)
Tiago Vignatti's avatar
Tiago Vignatti committed
538
539
		wl_display_roundtrip (desktop->display);

540
	desktop->grid_visible = FALSE;
541
	desktop->volume_visible = FALSE;
542

543
	css_setup(desktop);
544
545
546
547
	background_create(desktop);

	/* panel needs to be first so the clock and launcher grid can
	 * be added to its layer */
548
	panel_create(desktop);
549
	clock_create(desktop);
550
	launcher_grid_create (desktop);
Emilio Pozuelo Monfort's avatar
Emilio Pozuelo Monfort committed
551

Tiago Vignatti's avatar
Tiago Vignatti committed
552
553
	gtk_main();

554
555
	/* TODO cleanup */
	return EXIT_SUCCESS;
Tiago Vignatti's avatar
Tiago Vignatti committed
556
}