1 /*
2  * application: Single-instance managing application and single-instance
3  *              objects like window manager and so on.
4  *
5  * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  * MA 02110-1301, USA.
21  *
22  *
23  */
24 
25 /**
26  * SECTION:application
27  * @short_description: The core application class
28  * @include: xfdashboard/application.h
29  *
30  * #XfdashboardApplication is a single instance object. Its main purpose
31  * is to setup and start-up the application and also to manage other
32  * (mainly single instance) objects.
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include <libxfdashboard/application.h>
40 
41 #include <glib/gi18n-lib.h>
42 #include <clutter/x11/clutter-x11.h>
43 #include <gtk/gtk.h>
44 #include <garcon/garcon.h>
45 #include <libxfce4ui/libxfce4ui.h>
46 
47 #include <libxfdashboard/stage.h>
48 #include <libxfdashboard/types.h>
49 #include <libxfdashboard/view-manager.h>
50 #include <libxfdashboard/applications-view.h>
51 #include <libxfdashboard/windows-view.h>
52 #include <libxfdashboard/search-view.h>
53 #include <libxfdashboard/search-manager.h>
54 #include <libxfdashboard/applications-search-provider.h>
55 #include <libxfdashboard/utils.h>
56 #include <libxfdashboard/theme.h>
57 #include <libxfdashboard/focus-manager.h>
58 #include <libxfdashboard/bindings-pool.h>
59 #include <libxfdashboard/application-database.h>
60 #include <libxfdashboard/application-tracker.h>
61 #include <libxfdashboard/plugins-manager.h>
62 #include <libxfdashboard/window-tracker-backend.h>
63 #include <libxfdashboard/marshal.h>
64 #include <libxfdashboard/compat.h>
65 #include <libxfdashboard/debug.h>
66 
67 
68 /* Define this class in GObject system */
69 struct _XfdashboardApplicationPrivate
70 {
71 	/* Properties related */
72 	gboolean							isDaemon;
73 	gboolean							isSuspended;
74 	gchar								*themeName;
75 
76 	/* Instance related */
77 	gboolean							initialized;
78 	gboolean							isQuitting;
79 	gboolean							forcedNewInstance;
80 
81 	XfconfChannel						*xfconfChannel;
82 	XfdashboardStage					*stage;
83 	XfdashboardViewManager				*viewManager;
84 	XfdashboardSearchManager			*searchManager;
85 	XfdashboardFocusManager				*focusManager;
86 
87 	XfdashboardTheme					*theme;
88 	gulong								xfconfThemeChangedSignalID;
89 
90 	XfdashboardBindingsPool				*bindings;
91 
92 	XfdashboardApplicationDatabase		*appDatabase;
93 	XfdashboardApplicationTracker		*appTracker;
94 
95 	XfceSMClient						*sessionManagementClient;
96 
97 	XfdashboardPluginsManager			*pluginManager;
98 
99 	XfdashboardWindowTrackerBackend		*windowTrackerBackend;
100 };
101 
102 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardApplication,
103 							xfdashboard_application,
104 							G_TYPE_APPLICATION)
105 
106 /* Properties */
107 enum
108 {
109 	PROP_0,
110 
111 	PROP_DAEMONIZED,
112 	PROP_SUSPENDED,
113 	PROP_THEME_NAME,
114 	PROP_STAGE,
115 
116 	PROP_LAST
117 };
118 
119 static GParamSpec* XfdashboardApplicationProperties[PROP_LAST]={ 0, };
120 
121 /* Signals */
122 enum
123 {
124 	SIGNAL_INITIALIZED,
125 	SIGNAL_QUIT,
126 	SIGNAL_SHUTDOWN_FINAL,
127 
128 	SIGNAL_SUSPEND,
129 	SIGNAL_RESUME,
130 
131 	SIGNAL_THEME_LOADING,
132 	SIGNAL_THEME_LOADED,
133 	SIGNAL_THEME_CHANGED,
134 
135 	SIGNAL_APPLICATION_LAUNCHED,
136 
137 	/* Actions */
138 	ACTION_EXIT,
139 
140 	SIGNAL_LAST
141 };
142 
143 static guint XfdashboardApplicationSignals[SIGNAL_LAST]={ 0, };
144 
145 
146 /* IMPLEMENTATION: Private variables and methods */
147 #define XFDASHBOARD_APP_ID					"de.froevel.nomad.xfdashboard"
148 #define XFDASHBOARD_XFCONF_CHANNEL			"xfdashboard"
149 
150 #define THEME_NAME_XFCONF_PROP				"/theme"
151 #define DEFAULT_THEME_NAME					"xfdashboard"
152 
153 /* Single instance of application */
154 static XfdashboardApplication*		_xfdashboard_application=NULL;
155 
156 /* Forward declarations */
157 static void _xfdashboard_application_activate(GApplication *inApplication);
158 
159 /* Quit application depending on daemon mode and force parameter */
_xfdashboard_application_quit(XfdashboardApplication * self,gboolean inForceQuit)160 static void _xfdashboard_application_quit(XfdashboardApplication *self, gboolean inForceQuit)
161 {
162 	XfdashboardApplicationPrivate	*priv;
163 	gboolean						shouldQuit;
164 
165 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(self));
166 
167 	priv=self->priv;
168 	shouldQuit=FALSE;
169 
170 	/* Check if we should really quit this instance */
171 	if(inForceQuit==TRUE || priv->isDaemon==FALSE) shouldQuit=TRUE;
172 
173 	/* Do nothing if application is already quitting. This can happen if
174 	 * application is running in daemon mode (primary instance) and another
175 	 * instance was called with "quit" or "restart" parameter which would
176 	 * cause this function to be called twice.
177 	 */
178 	if(priv->isQuitting) return;
179 
180 	/* If application is not in daemon mode or if forced is set to TRUE
181 	 * destroy all stage windows ...
182 	 */
183 	if(shouldQuit==TRUE)
184 	{
185 		/* Set flag that application is going to quit */
186 		priv->isQuitting=TRUE;
187 
188 		/* If application is told to quit, set the restart style to something
189 		 * when it won't restart itself.
190 		 */
191 		if(priv->sessionManagementClient &&
192 			XFCE_IS_SM_CLIENT(priv->sessionManagementClient))
193 		{
194 			xfce_sm_client_set_restart_style(priv->sessionManagementClient, XFCE_SM_CLIENT_RESTART_NORMAL);
195 		}
196 
197 		/* Emit "quit" signal */
198 		g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_QUIT], 0);
199 
200 		/* Destroy stage */
201 		if(priv->stage)
202 		{
203 			clutter_actor_destroy(CLUTTER_ACTOR(priv->stage));
204 			priv->stage=NULL;
205 		}
206 
207 		/* Really quit application here and now */
208 		if(priv->initialized)
209 		{
210 			/* Release extra reference on application which causes g_application_run()
211 			 * to exit when returning.
212 			 */
213 			g_application_release(G_APPLICATION(self));
214 		}
215 	}
216 		/* ... otherwise emit "suspend" signal */
217 		else
218 		{
219 			/* Only send signal if not suspended already */
220 			if(!priv->isSuspended)
221 			{
222 				/* Send signal */
223 				g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_SUSPEND], 0);
224 
225 				/* Set flag for suspension */
226 				priv->isSuspended=TRUE;
227 				g_object_notify_by_pspec(G_OBJECT(self), XfdashboardApplicationProperties[PROP_SUSPENDED]);
228 			}
229 		}
230 }
231 
232 /* Action "exit" was called at application */
_xfdashboard_application_action_exit(XfdashboardApplication * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)233 static gboolean _xfdashboard_application_action_exit(XfdashboardApplication *self,
234 														XfdashboardFocusable *inSource,
235 														const gchar *inAction,
236 														ClutterEvent *inEvent)
237 {
238 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), CLUTTER_EVENT_PROPAGATE);
239 
240 	/* Quit application */
241 	_xfdashboard_application_quit(self, FALSE);
242 
243 	/* Prevent the default handler being called */
244 	return(CLUTTER_EVENT_STOP);
245 }
246 
247 /* The session is going to quit */
_xfdashboard_application_on_session_quit(XfdashboardApplication * self,gpointer inUserData)248 static void _xfdashboard_application_on_session_quit(XfdashboardApplication *self,
249 														gpointer inUserData)
250 {
251 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(self));
252 	g_return_if_fail(XFCE_IS_SM_CLIENT(inUserData));
253 
254 	/* Force application to quit */
255 	XFDASHBOARD_DEBUG(self, MISC, "Received 'quit' from session management client - initiating shutdown");
256 	_xfdashboard_application_quit(self, TRUE);
257 }
258 
259 /* A stage window should be destroyed */
_xfdashboard_application_on_delete_stage(XfdashboardApplication * self,ClutterEvent * inEvent,gpointer inUserData)260 static gboolean _xfdashboard_application_on_delete_stage(XfdashboardApplication *self,
261 															ClutterEvent *inEvent,
262 															gpointer inUserData)
263 {
264 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), CLUTTER_EVENT_PROPAGATE);
265 
266 	/* Quit application */
267 	_xfdashboard_application_quit(self, FALSE);
268 
269 	/* Prevent the default handler being called */
270 	return(CLUTTER_EVENT_STOP);
271 }
272 
273 /* Set theme name and reload theme */
_xfdashboard_application_set_theme_name(XfdashboardApplication * self,const gchar * inThemeName)274 static void _xfdashboard_application_set_theme_name(XfdashboardApplication *self, const gchar *inThemeName)
275 {
276 	XfdashboardApplicationPrivate	*priv;
277 	GError							*error;
278 	XfdashboardTheme				*theme;
279 
280 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(self));
281 	g_return_if_fail(inThemeName && *inThemeName);
282 
283 	priv=self->priv;
284 	error=NULL;
285 
286 	/* Set value if changed */
287 	if(g_strcmp0(priv->themeName, inThemeName)!=0)
288 	{
289 		/* Create new theme instance */
290 		theme=xfdashboard_theme_new(inThemeName);
291 
292 		/* Emit signal that theme is going to be loaded */
293 		g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_THEME_LOADING], 0, theme);
294 
295 		/* Load theme */
296 		if(!xfdashboard_theme_load(theme, &error))
297 		{
298 			/* Show critical warning at console */
299 			g_critical("Could not load theme '%s': %s",
300 						inThemeName,
301 						(error && error->message) ? error->message : "unknown error");
302 
303 			/* Show warning as notification */
304 			xfdashboard_notify(NULL,
305 								"dialog-error",
306 								_("Could not load theme '%s': %s"),
307 								inThemeName,
308 								(error && error->message) ? error->message : _("unknown error"));
309 
310 			/* Release allocated resources */
311 			if(error!=NULL) g_error_free(error);
312 			g_object_unref(theme);
313 
314 			return;
315 		}
316 
317 		/* Emit signal that theme was loaded successfully and will soon be applied */
318 		g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_THEME_LOADED], 0, theme);
319 
320 		/* Set value */
321 		if(priv->themeName) g_free(priv->themeName);
322 		priv->themeName=g_strdup(inThemeName);
323 
324 		/* Notify about property change */
325 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardApplicationProperties[PROP_THEME_NAME]);
326 
327 		/* Release current theme and store new one */
328 		if(priv->theme) g_object_unref(priv->theme);
329 		priv->theme=theme;
330 
331 		/* Emit signal that theme has changed to get all top-level actors
332 		 * to apply new theme.
333 		 */
334 		g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_THEME_CHANGED], 0, priv->theme);
335 	}
336 }
337 
338 /* Perform full initialization of this application instance */
_xfdashboard_application_initialize_full(XfdashboardApplication * self)339 static gboolean _xfdashboard_application_initialize_full(XfdashboardApplication *self)
340 {
341 	XfdashboardApplicationPrivate	*priv;
342 	GError							*error;
343 #if !GARCON_CHECK_VERSION(0,3,0)
344 	const gchar						*desktop;
345 #endif
346 	XfceSMClientRestartStyle		sessionManagementRestartStyle;
347 
348 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), FALSE);
349 
350 	priv=self->priv;
351 	error=NULL;
352 
353 	/* Initialize garcon for current desktop environment */
354 #if !GARCON_CHECK_VERSION(0,3,0)
355 	desktop=g_getenv("XDG_CURRENT_DESKTOP");
356 	if(G_LIKELY(desktop==NULL))
357 	{
358 		/* If we could not determine current desktop environment
359 		 * assume Xfce as this application is developed for this DE.
360 		 */
361 		desktop="XFCE";
362 	}
363 		/* If desktop environment was found but has no name
364 		 * set NULL to get all menu items shown.
365 		 */
366 		else if(*desktop==0) desktop=NULL;
367 
368 	garcon_set_environment(desktop);
369 #else
370 	garcon_set_environment_xdg(GARCON_ENVIRONMENT_XFCE);
371 #endif
372 
373 	/* Setup the session management */
374 	sessionManagementRestartStyle=XFCE_SM_CLIENT_RESTART_IMMEDIATELY;
375 	if(priv->forcedNewInstance) sessionManagementRestartStyle=XFCE_SM_CLIENT_RESTART_NORMAL;
376 
377 	priv->sessionManagementClient=xfce_sm_client_get();
378 	xfce_sm_client_set_priority(priv->sessionManagementClient, XFCE_SM_CLIENT_PRIORITY_DEFAULT);
379 	xfce_sm_client_set_restart_style(priv->sessionManagementClient, sessionManagementRestartStyle);
380 	g_signal_connect_swapped(priv->sessionManagementClient, "quit", G_CALLBACK(_xfdashboard_application_on_session_quit), self);
381 
382 	if(!xfce_sm_client_connect(priv->sessionManagementClient, &error))
383 	{
384 		g_warning("Failed to connect to session manager: %s",
385 					(error && error->message) ? error->message : "unknown error");
386 		g_clear_error(&error);
387 	}
388 
389 	/* Initialize xfconf */
390 	if(!xfconf_init(&error))
391 	{
392 		g_critical("Could not initialize xfconf: %s",
393 					(error && error->message) ? error->message : "unknown error");
394 		if(error) g_error_free(error);
395 		return(FALSE);
396 	}
397 
398 	priv->xfconfChannel=xfconf_channel_get(XFDASHBOARD_XFCONF_CHANNEL);
399 
400 	/* Set up keyboard and pointer bindings */
401 	priv->bindings=xfdashboard_bindings_pool_get_default();
402 	if(!priv->bindings)
403 	{
404 		g_critical("Could not initialize bindings");
405 		return(FALSE);
406 	}
407 
408 	if(!xfdashboard_bindings_pool_load(priv->bindings, &error))
409 	{
410 		g_critical("Could not load bindings: %s",
411 					(error && error->message) ? error->message : "unknown error");
412 		if(error!=NULL) g_error_free(error);
413 		return(FALSE);
414 	}
415 
416 	/* Create single-instance of window tracker backend to keep it alive while
417 	 * application is running and to avoid multiple reinitializations. It must
418 	 * be create before any class using a window tracker.
419 	 */
420 	priv->windowTrackerBackend=xfdashboard_window_tracker_backend_get_default();
421 	if(!priv->windowTrackerBackend)
422 	{
423 		g_critical("Could not setup window tracker backend");
424 		return(FALSE);
425 	}
426 
427 	/* Set up application database */
428 	priv->appDatabase=xfdashboard_application_database_get_default();
429 	if(!priv->appDatabase)
430 	{
431 		g_critical("Could not initialize application database");
432 		return(FALSE);
433 	}
434 
435 	if(!xfdashboard_application_database_load(priv->appDatabase, &error))
436 	{
437 		g_critical("Could not load application database: %s",
438 					(error && error->message) ? error->message : "unknown error");
439 		if(error!=NULL) g_error_free(error);
440 		return(FALSE);
441 	}
442 
443 	/* Set up application tracker */
444 	priv->appTracker=xfdashboard_application_tracker_get_default();
445 	if(!priv->appTracker)
446 	{
447 		g_critical("Could not initialize application tracker");
448 		return(FALSE);
449 	}
450 
451 	/* Register built-in views (order of registration is important) */
452 	priv->viewManager=xfdashboard_view_manager_get_default();
453 
454 	xfdashboard_view_manager_register(priv->viewManager, "builtin.windows", XFDASHBOARD_TYPE_WINDOWS_VIEW);
455 	xfdashboard_view_manager_register(priv->viewManager, "builtin.applications", XFDASHBOARD_TYPE_APPLICATIONS_VIEW);
456 	xfdashboard_view_manager_register(priv->viewManager, "builtin.search", XFDASHBOARD_TYPE_SEARCH_VIEW);
457 
458 	/* Register built-in search providers */
459 	priv->searchManager=xfdashboard_search_manager_get_default();
460 
461 	xfdashboard_search_manager_register(priv->searchManager, "builtin.applications", XFDASHBOARD_TYPE_APPLICATIONS_SEARCH_PROVIDER);
462 
463 	/* Create single-instance of focus manager to keep it alive while
464 	 * application is running.
465 	 */
466 	priv->focusManager=xfdashboard_focus_manager_get_default();
467 
468 	/* Create single-instance of plugin manager to keep it alive while
469 	 * application is running.
470 	 */
471 	priv->pluginManager=xfdashboard_plugins_manager_get_default();
472 	if(!priv->pluginManager)
473 	{
474 		g_critical("Could not initialize plugin manager");
475 		return(FALSE);
476 	}
477 
478 	if(!xfdashboard_plugins_manager_setup(priv->pluginManager))
479 	{
480 		g_critical("Could not setup plugin manager");
481 		return(FALSE);
482 	}
483 
484 	/* Set up and load theme */
485 	priv->xfconfThemeChangedSignalID=xfconf_g_property_bind(priv->xfconfChannel,
486 															THEME_NAME_XFCONF_PROP,
487 															G_TYPE_STRING,
488 															self,
489 															"theme-name");
490 	if(!priv->xfconfThemeChangedSignalID)
491 	{
492 		g_warning("Could not create binding between xfconf property and local resource for theme change notification.");
493 	}
494 
495 	/* Set up default theme in Xfcond if property in channel does not exist
496 	 * because it indicates first start.
497 	 */
498 	if(!xfconf_channel_has_property(priv->xfconfChannel, THEME_NAME_XFCONF_PROP))
499 	{
500 		xfconf_channel_set_string(priv->xfconfChannel,
501 									THEME_NAME_XFCONF_PROP,
502 									DEFAULT_THEME_NAME);
503 	}
504 
505 	/* At this time the theme must have been loaded, either because we
506 	 * set the default theme name because of missing theme property in
507 	 * xfconf channel or the value of xfconf channel property has been read
508 	 * and set when setting up binding (between xfconf property and local property)
509 	 * what caused a call to function to set theme name in this object
510 	 * and also caused a reload of theme.
511 	 * So if no theme object is set in this object then loading theme has
512 	 * failed and we have to return FALSE.
513 	 */
514 	if(!priv->theme) return(FALSE);
515 
516 	/* Create stage containing all monitors */
517 	priv->stage=XFDASHBOARD_STAGE(xfdashboard_stage_new());
518 	g_signal_connect_swapped(priv->stage, "delete-event", G_CALLBACK(_xfdashboard_application_on_delete_stage), self);
519 
520 	/* Emit signal 'theme-changed' to get current theme loaded at each stage created */
521 	g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_THEME_CHANGED], 0, priv->theme);
522 
523 	/* Initialization was successful so send signal and return TRUE */
524 	g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_INITIALIZED], 0);
525 
526 #ifdef DEBUG
527 	xfdashboard_notify(NULL, NULL, _("Welcome to %s (%s)!"), PACKAGE_NAME, PACKAGE_VERSION);
528 #else
529 	xfdashboard_notify(NULL, NULL, _("Welcome to %s!"), PACKAGE_NAME);
530 #endif
531 
532 	return(TRUE);
533 }
534 
535 /* Switch to requested view */
_xfdashboard_application_switch_to_view(XfdashboardApplication * self,const gchar * inInternalViewName)536 static void _xfdashboard_application_switch_to_view(XfdashboardApplication *self, const gchar *inInternalViewName)
537 {
538 	XfdashboardApplicationPrivate	*priv;
539 
540 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(self));
541 
542 	priv=self->priv;
543 
544 	/* If no view name was specified then do nothing and return immediately */
545 	if(!inInternalViewName ||
546 		!inInternalViewName[0])
547 	{
548 		XFDASHBOARD_DEBUG(self, MISC, "No view to switch to specified");
549 		return;
550 	}
551 
552 	/* Tell stage to switch requested view */
553 	XFDASHBOARD_DEBUG(self, MISC,
554 						"Trying to switch to view '%s'",
555 						inInternalViewName);
556 	xfdashboard_stage_set_switch_to_view(priv->stage, inInternalViewName);
557 }
558 
559 /* Handle command-line on primary instance */
_xfdashboard_application_handle_command_line_arguments(XfdashboardApplication * self,gint inArgc,gchar ** inArgv)560 static gint _xfdashboard_application_handle_command_line_arguments(XfdashboardApplication *self,
561 																	gint inArgc,
562 																	gchar **inArgv)
563 {
564 	XfdashboardApplicationPrivate	*priv;
565 	GOptionContext					*context;
566 	gboolean						result;
567 	GError							*error;
568 	gboolean						optionDaemonize;
569 	gboolean						optionQuit;
570 	gboolean						optionRestart;
571 	gboolean						optionToggle;
572 	gchar							*optionSwitchToView;
573 	gboolean						optionVersion;
574 	GOptionEntry					entries[]=
575 									{
576 										{ "daemonize", 'd', 0, G_OPTION_ARG_NONE, &optionDaemonize, N_("Fork to background"), NULL },
577 										{ "quit", 'q', 0, G_OPTION_ARG_NONE, &optionQuit, N_("Quit running instance"), NULL },
578 										{ "restart", 'r', 0, G_OPTION_ARG_NONE, &optionRestart, N_("Restart running instance"), NULL },
579 										{ "toggle", 't', 0, G_OPTION_ARG_NONE, &optionToggle, N_("Toggles visibility if running instance was started in daemon mode otherwise it quits running non-daemon instance"), NULL },
580 										{ "view", 0, 0, G_OPTION_ARG_STRING, &optionSwitchToView, N_("The ID of view to switch to on startup or resume"), "ID" },
581 										{ "version", 'v', 0, G_OPTION_ARG_NONE, &optionVersion, N_("Show version"), NULL },
582 										{ NULL }
583 									};
584 
585 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), XFDASHBOARD_APPLICATION_ERROR_FAILED);
586 
587 	priv=self->priv;
588 	error=NULL;
589 
590 	/* Set up options */
591 	optionDaemonize=FALSE;
592 	optionQuit=FALSE;
593 	optionRestart=FALSE;
594 	optionToggle=FALSE;
595 	optionSwitchToView=NULL;
596 	optionVersion=FALSE;
597 
598 	/* Setup command-line options */
599 	context=g_option_context_new(N_(""));
600 	g_option_context_set_summary(context, N_("A Gnome Shell like dashboard for Xfce4 - version " PACKAGE_VERSION));
601 	g_option_context_set_translation_domain(context, GETTEXT_PACKAGE);
602 	g_option_context_add_group(context, gtk_get_option_group(TRUE));
603 	g_option_context_add_group(context, clutter_get_option_group_without_init());
604 	g_option_context_add_group(context, xfce_sm_client_get_option_group(inArgc, inArgv));
605 	g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE);
606 
607 #ifdef DEBUG
608 #ifdef XFDASHBOARD_ENABLE_DEBUG
609 	g_print("** Use environment variable XFDASHBOARD_DEBUG to enable debug messages\n");
610 	g_print("** To get a list of debug categories set XFDASHBOARD_DEBUG=help\n");
611 #endif
612 #endif
613 
614 	if(!g_option_context_parse(context, &inArgc, &inArgv, &error))
615 	{
616 		/* Show error */
617 		g_print(N_("%s\n"),
618 				(error && error->message) ? error->message : _("unknown error"));
619 		if(error)
620 		{
621 			g_error_free(error);
622 			error=NULL;
623 		}
624 
625 		/* Release allocated resources */
626 		if(optionSwitchToView) g_free(optionSwitchToView);
627 		if(context) g_option_context_free(context);
628 
629 		return(XFDASHBOARD_APPLICATION_ERROR_FAILED);
630 	}
631 
632 	/* Set up debug flags */
633 #ifdef XFDASHBOARD_ENABLE_DEBUG
634 	{
635 		const gchar					*environment;
636 		static const GDebugKey		debugKeys[]=
637 									{
638 										{ "misc", XFDASHBOARD_DEBUG_MISC },
639 										{ "actor", XFDASHBOARD_DEBUG_ACTOR },
640 										{ "style", XFDASHBOARD_DEBUG_STYLE },
641 										{ "styling", XFDASHBOARD_DEBUG_STYLE },
642 										{ "theme", XFDASHBOARD_DEBUG_THEME },
643 										{ "apps", XFDASHBOARD_DEBUG_APPLICATIONS },
644 										{ "applications", XFDASHBOARD_DEBUG_APPLICATIONS },
645 										{ "images", XFDASHBOARD_DEBUG_IMAGES },
646 										{ "windows", XFDASHBOARD_DEBUG_WINDOWS },
647 										{ "window-tracker", XFDASHBOARD_DEBUG_WINDOWS },
648 										{ "animation", XFDASHBOARD_DEBUG_ANIMATION },
649 										{ "animations", XFDASHBOARD_DEBUG_ANIMATION },
650 										{ "plugin", XFDASHBOARD_DEBUG_PLUGINS },
651 										{ "plugins", XFDASHBOARD_DEBUG_PLUGINS },
652 									};
653 
654 		/* Parse debug flags */
655 		environment=g_getenv("XFDASHBOARD_DEBUG");
656 		if(environment)
657 		{
658 			xfdashboard_debug_flags=
659 				g_parse_debug_string(environment,
660 										debugKeys,
661 										G_N_ELEMENTS(debugKeys));
662 			environment=NULL;
663 		}
664 
665 		/* Parse object names to debug */
666 		environment=g_getenv("XFDASHBOARD_DEBUG");
667 		if(environment)
668 		{
669 			if(G_UNLIKELY(xfdashboard_debug_classes))
670 			{
671 				g_strfreev(xfdashboard_debug_classes);
672 				xfdashboard_debug_classes=NULL;
673 			}
674 
675 			xfdashboard_debug_classes=g_strsplit(environment, ",", -1);
676 			environment=NULL;
677 		}
678 	}
679 #endif
680 
681 	/* If this application instance is a remote instance do not handle any
682 	 * command-line argument. The arguments will be sent to the primary instance,
683 	 * handled there and the exit code will be sent back to the remote instance.
684 	 * We can check for remote instance with g_application_get_is_remote() because
685 	 * the application was tried to get registered either by local_command_line
686 	 * signal handler or by g_application_run().
687 	 */
688 	if(g_application_get_is_remote(G_APPLICATION(self)))
689 	{
690 		XFDASHBOARD_DEBUG(self, MISC, "Do not handle command-line parameters on remote application instance");
691 
692 		/* One exception is "--version" */
693 		if(optionVersion)
694 		{
695 			g_print("Remote instance: %s-%s\n", PACKAGE_NAME, PACKAGE_VERSION);
696 		}
697 
698 		/* Release allocated resources */
699 		if(optionSwitchToView) g_free(optionSwitchToView);
700 		if(context) g_option_context_free(context);
701 
702 		/* No errors so far and no errors will happen as we do not handle
703 		 * any command-line arguments here.
704 		 */
705 		return(XFDASHBOARD_APPLICATION_ERROR_NONE);
706 	}
707 	XFDASHBOARD_DEBUG(self, MISC, "Handling command-line parameters on primary application instance");
708 
709 	/* Handle options: restart
710 	 *
711 	 * First handle option 'restart' to quit this application early. Also return
712 	 * immediately with status XFDASHBOARD_APPLICATION_ERROR_RESTART to tell
713 	 * remote instance to perform a restart and to become the new primary instance.
714 	 * This option requires that application was initialized.
715 	 */
716 	if(optionRestart &&
717 		priv->initialized)
718 	{
719 		/* Quit existing instance for restart */
720 		XFDASHBOARD_DEBUG(self, MISC, "Received request to restart application!");
721 		_xfdashboard_application_quit(self, TRUE);
722 
723 		/* Release allocated resources */
724 		if(optionSwitchToView) g_free(optionSwitchToView);
725 		if(context) g_option_context_free(context);
726 
727 		/* Return state to restart this applicationa */
728 		return(XFDASHBOARD_APPLICATION_ERROR_RESTART);
729 	}
730 
731 	/* Handle options: quit
732 	 *
733 	 * Now check for the next application stopping option 'quit'. We check for it
734 	 * to quit this application early and to prevent further and unnecessary check
735 	 * for other options.
736 	 */
737 	if(optionQuit)
738 	{
739 		/* Quit existing instance */
740 		XFDASHBOARD_DEBUG(self, MISC, "Received request to quit running instance!");
741 		_xfdashboard_application_quit(self, TRUE);
742 
743 		/* Release allocated resources */
744 		if(optionSwitchToView) g_free(optionSwitchToView);
745 		if(context) g_option_context_free(context);
746 
747 		return(XFDASHBOARD_APPLICATION_ERROR_QUIT);
748 	}
749 
750 	/* Handle options: toggle
751 	 *
752 	 * Now check if we should toggle the state of application. That means
753 	 * if application is running daemon mode, resume it if suspended otherwise
754 	 * suspend it. If application is running but not in daemon just quit the
755 	 * application. If application was not inited yet, skip toggling state and
756 	 * perform a normal start-up.
757 	 */
758 	if(optionToggle &&
759 		priv->initialized)
760 	{
761 		/* If application is running in daemon mode, toggle between suspend/resume ... */
762 		if(priv->isDaemon)
763 		{
764 			if(priv->isSuspended)
765 			{
766 				/* Switch to view if requested */
767 				_xfdashboard_application_switch_to_view(self, optionSwitchToView);
768 
769 				/* Show application again */
770 				_xfdashboard_application_activate(G_APPLICATION(self));
771 			}
772 				else
773 				{
774 					/* Hide application */
775 					_xfdashboard_application_quit(self, FALSE);
776 				}
777 		}
778 			/* ... otherwise if not running in daemon mode, just quit */
779 			else
780 			{
781 				/* Hide application */
782 				_xfdashboard_application_quit(self, FALSE);
783 			}
784 
785 		/* Release allocated resources */
786 		if(optionSwitchToView) g_free(optionSwitchToView);
787 		if(context) g_option_context_free(context);
788 
789 		/* Stop here because option was handled and application does not get initialized */
790 		return(XFDASHBOARD_APPLICATION_ERROR_NONE);
791 	}
792 
793 	/* Handle options: daemonize
794 	 *
795 	 * Check if application shoud run in daemon mode. A daemonized instance runs in
796 	 * background and does not present the stage initially. The application must not
797 	 * be initialized yet as it can only be done on start-up.
798 	 *
799 	 * Does not work if a new instance was forced.
800 	 */
801 	if(optionDaemonize &&
802 		!priv->initialized)
803 	{
804 		if(!priv->forcedNewInstance)
805 		{
806 			priv->isDaemon=optionDaemonize;
807 			g_object_notify_by_pspec(G_OBJECT(self), XfdashboardApplicationProperties[PROP_DAEMONIZED]);
808 
809 			if(priv->isDaemon)
810 			{
811 				priv->isSuspended=TRUE;
812 				g_object_notify_by_pspec(G_OBJECT(self), XfdashboardApplicationProperties[PROP_SUSPENDED]);
813 			}
814 		}
815 			else
816 			{
817 				g_warning("Cannot daemonized because a temporary new instance of application was forced.");
818 			}
819 	}
820 
821 	/* Handle options: version
822 	 *
823 	 * Show the version of this (maybe daemonized) instance.
824 	 */
825 	if(optionVersion)
826 	{
827 		if(priv->isDaemon)
828 		{
829 			g_print("Daemon instance: %s-%s\n", PACKAGE_NAME, PACKAGE_VERSION);
830 		}
831 			else
832 			{
833 				g_print("Version: %s-%s\n", PACKAGE_NAME, PACKAGE_VERSION);
834 				return(XFDASHBOARD_APPLICATION_ERROR_QUIT);
835 			}
836 	}
837 
838 	/* Check if this instance needs to be initialized fully */
839 	if(!priv->initialized)
840 	{
841 		/* Perform full initialization of this application instance */
842 		result=_xfdashboard_application_initialize_full(self);
843 		if(result==FALSE) return(XFDASHBOARD_APPLICATION_ERROR_FAILED);
844 
845 		/* Switch to view if requested */
846 		_xfdashboard_application_switch_to_view(self, optionSwitchToView);
847 
848 		/* Show application if not started daemonized */
849 		if(!priv->isDaemon) clutter_actor_show(CLUTTER_ACTOR(priv->stage));
850 
851 		/* Take extra reference on the application to keep application in
852 		 * function g_application_run() alive when returning.
853 		 */
854 		g_application_hold(G_APPLICATION(self));
855 	}
856 
857 	/* Check if this instance need to be activated. Is should only be done
858 	 * if instance is initialized
859 	 */
860 	if(priv->initialized)
861 	{
862 		/* Switch to view if requested */
863 		_xfdashboard_application_switch_to_view(self, optionSwitchToView);
864 
865 		/* Show application */
866 		_xfdashboard_application_activate(G_APPLICATION(self));
867 	}
868 
869 	/* Release allocated resources */
870 	if(optionSwitchToView) g_free(optionSwitchToView);
871 	if(context) g_option_context_free(context);
872 
873 	/* All done successfully so return status code 0 for success */
874 	priv->initialized=TRUE;
875 	return(XFDASHBOARD_APPLICATION_ERROR_NONE);
876 }
877 
878 /* IMPLEMENTATION: GApplication */
879 
880 /* Received "activate" signal on primary instance */
_xfdashboard_application_activate(GApplication * inApplication)881 static void _xfdashboard_application_activate(GApplication *inApplication)
882 {
883 	XfdashboardApplication			*self;
884 	XfdashboardApplicationPrivate	*priv;
885 
886 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(inApplication));
887 
888 	self=XFDASHBOARD_APPLICATION(inApplication);
889 	priv=self->priv;
890 
891 	/* Emit "resume" signal */
892 	g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_RESUME], 0);
893 
894 	/* Unset flag for suspension */
895 	if(priv->isSuspended)
896 	{
897 		priv->isSuspended=FALSE;
898 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardApplicationProperties[PROP_SUSPENDED]);
899 	}
900 }
901 
902 /* Handle command-line on primary instance */
_xfdashboard_application_command_line(GApplication * inApplication,GApplicationCommandLine * inCommandLine)903 static int _xfdashboard_application_command_line(GApplication *inApplication, GApplicationCommandLine *inCommandLine)
904 {
905 	XfdashboardApplication			*self;
906 	gint							argc;
907 	gchar							**argv;
908 	gint							exitStatus;
909 
910 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(inApplication), XFDASHBOARD_APPLICATION_ERROR_FAILED);
911 
912 	self=XFDASHBOARD_APPLICATION(inApplication);
913 
914 	/* Get number of command-line arguments and the arguments */
915 	argv=g_application_command_line_get_arguments(inCommandLine, &argc);
916 
917 	/* Parse command-line and get exit status code */
918 	exitStatus=_xfdashboard_application_handle_command_line_arguments(self, argc, argv);
919 
920 	/* Release allocated resources */
921 	if(argv) g_strfreev(argv);
922 
923 	/* Return exit status code */
924 	return(exitStatus);
925 }
926 
927 /* Check and handle command-line on local instance regardless if this one
928  * is the primary instance or a remote one. This functions checks for arguments
929  * which can be handled locally, e.g. '--help'. Otherwise they will be send to
930  * the primary instance.
931  */
_xfdashboard_application_local_command_line(GApplication * inApplication,gchar *** ioArguments,int * outExitStatus)932 static gboolean _xfdashboard_application_local_command_line(GApplication *inApplication,
933 															gchar ***ioArguments,
934 															int *outExitStatus)
935 {
936 	XfdashboardApplication			*self;
937 	GError							*error;
938 
939 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(inApplication), TRUE);
940 
941 	self=XFDASHBOARD_APPLICATION(inApplication);
942 	error=NULL;
943 
944 	/* Set exit status code initially to success result but can be changed later */
945 	if(outExitStatus) *outExitStatus=XFDASHBOARD_APPLICATION_ERROR_NONE;
946 
947 	/* Try to register application to determine early if this instance will be
948 	 * the primary application instance or a remote one.
949 	 */
950 	if(!g_application_register(inApplication, NULL, &error))
951 	{
952 		g_critical("Unable to register application: %s",
953 					(error && error->message) ? error->message : "Unknown error");
954 		if(error)
955 		{
956 			g_error_free(error);
957 			error=NULL;
958 		}
959 
960 		/* Set error status code */
961 		if(outExitStatus) *outExitStatus=XFDASHBOARD_APPLICATION_ERROR_FAILED;
962 
963 		/* Return TRUE to indicate that command-line does not need further processing */
964 		return(TRUE);
965 	}
966 
967 	/* If this is a remote instance we need to parse command-line now */
968 	if(g_application_get_is_remote(inApplication))
969 	{
970 		gint						argc;
971 		gchar						**argv;
972 		gchar						**originArgv;
973 		gint						exitStatus;
974 		gint						i;
975 
976 		/* We have to make a extra copy of the command-line arguments, since
977 		 * g_option_context_parse() in command-line argument handling function
978 		 * might remove parameters from the arguments list and maybe we need
979 		 * them to sent the arguments to primary instance if not handled locally
980 		 * like '--help'.
981 		 */
982 		originArgv=*ioArguments;
983 
984 		argc=g_strv_length(originArgv);
985 		argv=g_new0(gchar*, argc+1);
986 		for(i=0; i<=argc; i++) argv[i]=g_strdup(originArgv[i]);
987 
988 		/* Parse command-line and store exit status code */
989 		exitStatus=_xfdashboard_application_handle_command_line_arguments(self, argc, argv);
990 		if(outExitStatus) *outExitStatus=exitStatus;
991 
992 		/* Release allocated resources */
993 		if(argv) g_strfreev(argv);
994 
995 		/* If exit status code indicates an error then return TRUE here to indicate
996 		 * that command-line does not need further processing.
997 		 */
998 		if(exitStatus==XFDASHBOARD_APPLICATION_ERROR_FAILED) return(TRUE);
999 	}
1000 
1001 	/* Return FALSE to indicate that command-line was not completely handled
1002 	 * and needs further processing, e.g. this is the primary instance or a remote
1003 	 * instance which could not handle the arguments locally.
1004 	 */
1005 	return(FALSE);
1006 }
1007 
1008 
1009 /* IMPLEMENTATION: GObject */
1010 
1011 /* Dispose this object */
_xfdashboard_application_dispose(GObject * inObject)1012 static void _xfdashboard_application_dispose(GObject *inObject)
1013 {
1014 	XfdashboardApplication			*self=XFDASHBOARD_APPLICATION(inObject);
1015 	XfdashboardApplicationPrivate	*priv=self->priv;
1016 
1017 	/* Ensure "is-quitting" flag is set just in case someone asks */
1018 	priv->isQuitting=TRUE;
1019 
1020 	/* Signal "shutdown-final" of application */
1021 	g_signal_emit(self, XfdashboardApplicationSignals[SIGNAL_SHUTDOWN_FINAL], 0);
1022 
1023 	/* Release allocated resources */
1024 	if(priv->windowTrackerBackend)
1025 	{
1026 		g_object_unref(priv->windowTrackerBackend);
1027 		priv->windowTrackerBackend=NULL;
1028 	}
1029 
1030 	if(priv->pluginManager)
1031 	{
1032 		g_object_unref(priv->pluginManager);
1033 		priv->pluginManager=NULL;
1034 	}
1035 
1036 	if(priv->xfconfThemeChangedSignalID)
1037 	{
1038 		xfconf_g_property_unbind(priv->xfconfThemeChangedSignalID);
1039 		priv->xfconfThemeChangedSignalID=0L;
1040 	}
1041 
1042 	if(priv->viewManager)
1043 	{
1044 		/* Unregisters all remaining registered views - no need to unregister them here */
1045 		g_object_unref(priv->viewManager);
1046 		priv->viewManager=NULL;
1047 	}
1048 
1049 	if(priv->searchManager)
1050 	{
1051 		/* Unregisters all remaining registered providers - no need to unregister them here */
1052 		g_object_unref(priv->searchManager);
1053 		priv->searchManager=NULL;
1054 	}
1055 
1056 	if(priv->focusManager)
1057 	{
1058 		/* Unregisters all remaining registered focusable actors.
1059 		 * There is no need to unregister them here.
1060 		 */
1061 		g_object_unref(priv->focusManager);
1062 		priv->focusManager=NULL;
1063 	}
1064 
1065 	if(priv->bindings)
1066 	{
1067 		g_object_unref(priv->bindings);
1068 		priv->bindings=NULL;
1069 	}
1070 
1071 	if(priv->appDatabase)
1072 	{
1073 		g_object_unref(priv->appDatabase);
1074 		priv->appDatabase=NULL;
1075 	}
1076 
1077 	if(priv->appTracker)
1078 	{
1079 		g_object_unref(priv->appTracker);
1080 		priv->appTracker=NULL;
1081 	}
1082 
1083 	if(priv->theme)
1084 	{
1085 		g_object_unref(priv->theme);
1086 		priv->theme=NULL;
1087 	}
1088 
1089 	if(priv->themeName)
1090 	{
1091 		g_free(priv->themeName);
1092 		priv->themeName=NULL;
1093 	}
1094 
1095 	if(priv->stage)
1096 	{
1097 		g_object_unref(priv->stage);
1098 		priv->stage=NULL;
1099 	}
1100 
1101 	/* Shutdown session management */
1102 	if(priv->sessionManagementClient)
1103 	{
1104 		/* This instance looks like to be disposed normally and not like a crash
1105 		 * so set the restart style at session management to something that it
1106 		 * will not restart itself but shutting down.
1107 		 */
1108 		if(XFCE_IS_SM_CLIENT(priv->sessionManagementClient))
1109 		{
1110 			xfce_sm_client_set_restart_style(priv->sessionManagementClient, XFCE_SM_CLIENT_RESTART_NORMAL);
1111 		}
1112 
1113 		priv->sessionManagementClient=NULL;
1114 	}
1115 
1116 	/* Shutdown xfconf */
1117 	priv->xfconfChannel=NULL;
1118 	xfconf_shutdown();
1119 
1120 	/* Unset singleton */
1121 	if(G_LIKELY(G_OBJECT(_xfdashboard_application)==inObject)) _xfdashboard_application=NULL;
1122 
1123 	/* Call parent's class dispose method */
1124 	G_OBJECT_CLASS(xfdashboard_application_parent_class)->dispose(inObject);
1125 }
1126 
1127 /* Set/get properties */
_xfdashboard_application_set_property(GObject * inObject,guint inPropID,const GValue * inValue,GParamSpec * inSpec)1128 static void _xfdashboard_application_set_property(GObject *inObject,
1129 													guint inPropID,
1130 													const GValue *inValue,
1131 													GParamSpec *inSpec)
1132 {
1133 	XfdashboardApplication	*self=XFDASHBOARD_APPLICATION(inObject);
1134 
1135 	switch(inPropID)
1136 	{
1137 		case PROP_THEME_NAME:
1138 			_xfdashboard_application_set_theme_name(self, g_value_get_string(inValue));
1139 			break;
1140 
1141 		default:
1142 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
1143 			break;
1144 	}
1145 }
1146 
_xfdashboard_application_get_property(GObject * inObject,guint inPropID,GValue * outValue,GParamSpec * inSpec)1147 static void _xfdashboard_application_get_property(GObject *inObject,
1148 													guint inPropID,
1149 													GValue *outValue,
1150 													GParamSpec *inSpec)
1151 {
1152 	XfdashboardApplication	*self=XFDASHBOARD_APPLICATION(inObject);
1153 
1154 	switch(inPropID)
1155 	{
1156 		case PROP_DAEMONIZED:
1157 			g_value_set_boolean(outValue, self->priv->isDaemon);
1158 			break;
1159 
1160 		case PROP_SUSPENDED:
1161 			g_value_set_boolean(outValue, self->priv->isSuspended);
1162 			break;
1163 
1164 		case PROP_STAGE:
1165 			g_value_set_object(outValue, self->priv->stage);
1166 			break;
1167 
1168 		case PROP_THEME_NAME:
1169 			g_value_set_string(outValue, self->priv->themeName);
1170 			break;
1171 
1172 		default:
1173 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
1174 			break;
1175 	}
1176 }
1177 
1178 /* Class initialization
1179  * Override functions in parent classes and define properties
1180  * and signals
1181  */
xfdashboard_application_class_init(XfdashboardApplicationClass * klass)1182 static void xfdashboard_application_class_init(XfdashboardApplicationClass *klass)
1183 {
1184 	GApplicationClass	*appClass=G_APPLICATION_CLASS(klass);
1185 	GObjectClass		*gobjectClass=G_OBJECT_CLASS(klass);
1186 
1187 	/* Override functions */
1188 	klass->exit=_xfdashboard_application_action_exit;
1189 
1190 	appClass->activate=_xfdashboard_application_activate;
1191 	appClass->command_line=_xfdashboard_application_command_line;
1192 	appClass->local_command_line=_xfdashboard_application_local_command_line;
1193 
1194 	gobjectClass->dispose=_xfdashboard_application_dispose;
1195 	gobjectClass->set_property=_xfdashboard_application_set_property;
1196 	gobjectClass->get_property=_xfdashboard_application_get_property;
1197 
1198 	/* Define properties */
1199 	/**
1200 	 * XfdashboardApplication:is-daemonized:
1201 	 *
1202 	 * A flag indicating if application is running in daemon mode. It is set to
1203 	 * %TRUE if it running in daemon mode otherwise %FALSE as it running in
1204 	 * standalone application.
1205 	 */
1206 	XfdashboardApplicationProperties[PROP_DAEMONIZED]=
1207 		g_param_spec_boolean("is-daemonized",
1208 								"Is daemonized",
1209 								"Flag indicating if application is daemonized",
1210 								FALSE,
1211 								G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1212 
1213 	/**
1214 	 * XfdashboardApplication:is-suspended:
1215 	 *
1216 	 * A flag indicating if application running in daemon mode is suspended.
1217 	 * It is set to %TRUE if application is suspended. If it is %FALSE then
1218 	 * application is resumed, active and visible.
1219 	 */
1220 	XfdashboardApplicationProperties[PROP_SUSPENDED]=
1221 		g_param_spec_boolean("is-suspended",
1222 								"Is suspended",
1223 								"Flag indicating if application is suspended currently",
1224 								FALSE,
1225 								G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1226 
1227 	/**
1228 	 * XfdashboardApplication:stage:
1229 	 *
1230 	 * The #XfdashboardStage of application.
1231 	 */
1232 	XfdashboardApplicationProperties[PROP_STAGE]=
1233 		g_param_spec_object("stage",
1234 								"Stage",
1235 								"The stage object of application",
1236 								XFDASHBOARD_TYPE_STAGE,
1237 								G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1238 
1239 	/**
1240 	 * XfdashboardApplication:theme-name:
1241 	 *
1242 	 * The name of current theme used in application.
1243 	 */
1244 	XfdashboardApplicationProperties[PROP_THEME_NAME]=
1245 		g_param_spec_string("theme-name",
1246 								"Theme name",
1247 								"Name of current theme",
1248 								DEFAULT_THEME_NAME,
1249 								G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1250 
1251 	g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardApplicationProperties);
1252 
1253 	/* Define signals */
1254 	/**
1255 	 * XfdashboardApplication::initialized:
1256 	 * @self: The application's primary instance was fully initialized
1257 	 *
1258 	 * The ::initialized signal is emitted when the primary instance of
1259 	 * application was fully initialized.
1260 	 *
1261 	 * The main purpose of this signal is to give other objects and plugin
1262 	 * a chance to initialize properly after application is fully initialized
1263 	 * and all other components are really available and also initialized.
1264 	 */
1265 	XfdashboardApplicationSignals[SIGNAL_INITIALIZED]=
1266 		g_signal_new("initialized",
1267 						G_TYPE_FROM_CLASS(klass),
1268 						G_SIGNAL_RUN_LAST,
1269 						G_STRUCT_OFFSET(XfdashboardApplicationClass, initialized),
1270 						NULL,
1271 						NULL,
1272 						g_cclosure_marshal_VOID__VOID,
1273 						G_TYPE_NONE,
1274 						0);
1275 
1276 	/**
1277 	 * XfdashboardApplication::quit:
1278 	 * @self: The application going to quit
1279 	 *
1280 	 * The ::quit signal is emitted when the application is going to quit. This
1281 	 * signal is definitely emitted before the stage is destroyed.
1282 	 *
1283 	 * The main purpose of this signal is to give other objects and plugin
1284 	 * a chance to clean-up properly.
1285 	 */
1286 	XfdashboardApplicationSignals[SIGNAL_QUIT]=
1287 		g_signal_new("quit",
1288 						G_TYPE_FROM_CLASS(klass),
1289 						G_SIGNAL_RUN_LAST,
1290 						G_STRUCT_OFFSET(XfdashboardApplicationClass, quit),
1291 						NULL,
1292 						NULL,
1293 						g_cclosure_marshal_VOID__VOID,
1294 						G_TYPE_NONE,
1295 						0);
1296 
1297 	/**
1298 	 * XfdashboardApplication::shutdown-final:
1299 	 * @self: The application being destroyed
1300 	 *
1301 	 * The ::shutdown-final signal is emitted when the application object
1302 	 * is currently destroyed and can be considered as quitted. The stage is
1303 	 * detroyed when this signal is emitted and only the non-visible managers
1304 	 * are still accessible at this time.
1305 	 */
1306 	XfdashboardApplicationSignals[SIGNAL_SHUTDOWN_FINAL]=
1307 		g_signal_new("shutdown-final",
1308 						G_TYPE_FROM_CLASS(klass),
1309 						G_SIGNAL_RUN_LAST,
1310 						G_STRUCT_OFFSET(XfdashboardApplicationClass, shutdown_final),
1311 						NULL,
1312 						NULL,
1313 						g_cclosure_marshal_VOID__VOID,
1314 						G_TYPE_NONE,
1315 						0);
1316 
1317 	/**
1318 	 * XfdashboardApplication::suspend:
1319 	 * @self: The application suspended
1320 	 *
1321 	 * The ::suspend signal is emitted when the application is suspended and
1322 	 * send to background.
1323 	 *
1324 	 * This signal is only emitted if application is running in daemon mode.
1325 	 */
1326 	XfdashboardApplicationSignals[SIGNAL_SUSPEND]=
1327 		g_signal_new("suspend",
1328 						G_TYPE_FROM_CLASS(klass),
1329 						G_SIGNAL_RUN_LAST,
1330 						G_STRUCT_OFFSET(XfdashboardApplicationClass, suspend),
1331 						NULL,
1332 						NULL,
1333 						g_cclosure_marshal_VOID__VOID,
1334 						G_TYPE_NONE,
1335 						0);
1336 
1337 	/**
1338 	 * XfdashboardApplication::resume:
1339 	 * @self: The application resumed
1340 	 *
1341 	 * The ::resume signal is emitted when the application is resumed and
1342 	 * brought to foreground.
1343 	 *
1344 	 * This signal is only emitted if application is running in daemon mode.
1345 	 */
1346 	XfdashboardApplicationSignals[SIGNAL_RESUME]=
1347 		g_signal_new("resume",
1348 						G_TYPE_FROM_CLASS(klass),
1349 						G_SIGNAL_RUN_LAST,
1350 						G_STRUCT_OFFSET(XfdashboardApplicationClass, resume),
1351 						NULL,
1352 						NULL,
1353 						g_cclosure_marshal_VOID__VOID,
1354 						G_TYPE_NONE,
1355 						0);
1356 
1357 	/**
1358 	 * XfdashboardApplication::theme-loading:
1359 	 * @self: The application whose theme is going to change
1360 	 * @inTheme: The new #XfdashboardTheme used
1361 	 *
1362 	 * The ::theme-loading signal is emitted when the theme of application is
1363 	 * going to be loaded. When this signal is received no file was loaded so far
1364 	 * but will be. For example, at this moment it is possible to load a CSS file
1365 	 * by a plugin before the CSS files of the new theme will be loaded to give
1366 	 * the theme a chance to override the default CSS of plugin.
1367 	 */
1368 	XfdashboardApplicationSignals[SIGNAL_THEME_LOADING]=
1369 		g_signal_new("theme-loading",
1370 						G_TYPE_FROM_CLASS(klass),
1371 						G_SIGNAL_RUN_LAST,
1372 						G_STRUCT_OFFSET(XfdashboardApplicationClass, theme_loading),
1373 						NULL,
1374 						NULL,
1375 						g_cclosure_marshal_VOID__OBJECT,
1376 						G_TYPE_NONE,
1377 						1,
1378 						XFDASHBOARD_TYPE_THEME);
1379 
1380 	/**
1381 	 * XfdashboardApplication::theme-loaded:
1382 	 * @self: The application whose theme is going to change
1383 	 * @inTheme: The new #XfdashboardTheme used
1384 	 *
1385 	 * The ::theme-loaded signal is emitted when the new theme of application was
1386 	 * loaded and will soon be applied. When this signal is received it is the
1387 	 * last chance for other components and plugins to load additionally resources
1388 	 * like CSS, e.g. to override CSS of theme.
1389 	 */
1390 	XfdashboardApplicationSignals[SIGNAL_THEME_LOADED]=
1391 		g_signal_new("theme-loaded",
1392 						G_TYPE_FROM_CLASS(klass),
1393 						G_SIGNAL_RUN_LAST,
1394 						G_STRUCT_OFFSET(XfdashboardApplicationClass, theme_loaded),
1395 						NULL,
1396 						NULL,
1397 						g_cclosure_marshal_VOID__OBJECT,
1398 						G_TYPE_NONE,
1399 						1,
1400 						XFDASHBOARD_TYPE_THEME);
1401 
1402 	/**
1403 	 * XfdashboardApplication::theme-changed:
1404 	 * @self: The application whose theme has changed
1405 	 * @inTheme: The new #XfdashboardTheme used
1406 	 *
1407 	 * The ::theme-changed signal is emitted when a new theme of application
1408 	 * has been loaded and applied.
1409 	 */
1410 	XfdashboardApplicationSignals[SIGNAL_THEME_CHANGED]=
1411 		g_signal_new("theme-changed",
1412 						G_TYPE_FROM_CLASS(klass),
1413 						G_SIGNAL_RUN_LAST,
1414 						G_STRUCT_OFFSET(XfdashboardApplicationClass, theme_changed),
1415 						NULL,
1416 						NULL,
1417 						g_cclosure_marshal_VOID__OBJECT,
1418 						G_TYPE_NONE,
1419 						1,
1420 						XFDASHBOARD_TYPE_THEME);
1421 
1422 	/**
1423 	 * XfdashboardApplication::application-launched:
1424 	 * @self: The application
1425 	 * @inAppInfo: The #GAppInfo that was just launched
1426 	 *
1427 	 * The ::application-launched signal is emitted when a #GAppInfo has been
1428 	 * launched successfully, e.g. via any #XfdashboardApplicationButton.
1429 	 */
1430 	XfdashboardApplicationSignals[SIGNAL_APPLICATION_LAUNCHED]=
1431 		g_signal_new("application-launched",
1432 						G_TYPE_FROM_CLASS(klass),
1433 						G_SIGNAL_RUN_LAST,
1434 						G_STRUCT_OFFSET(XfdashboardApplicationClass, application_launched),
1435 						NULL,
1436 						NULL,
1437 						g_cclosure_marshal_VOID__OBJECT,
1438 						G_TYPE_NONE,
1439 						1,
1440 						G_TYPE_APP_INFO);
1441 
1442 	/**
1443 	 * XfdashboardApplication::exit:
1444 	 * @self: The application
1445 	 * @inFocusable: The source #XfdashboardFocusable which send this signal
1446 	 * @inAction: A string containing the action (that is this signal name)
1447 	 * @inEvent: The #ClutterEvent associated to this signal and action
1448 	 *
1449 	 * The ::exit signal is an action and when emitted it causes the application
1450 	 * to quit like calling xfdashboard_application_quit_or_suspend().
1451 	 *
1452 	 * Note: This action can be used at key bindings.
1453 	 */
1454 	XfdashboardApplicationSignals[ACTION_EXIT]=
1455 		g_signal_new("exit",
1456 						G_TYPE_FROM_CLASS(klass),
1457 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1458 						G_STRUCT_OFFSET(XfdashboardApplicationClass, exit),
1459 						g_signal_accumulator_true_handled,
1460 						NULL,
1461 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
1462 						G_TYPE_BOOLEAN,
1463 						3,
1464 						XFDASHBOARD_TYPE_FOCUSABLE,
1465 						G_TYPE_STRING,
1466 						CLUTTER_TYPE_EVENT);
1467 
1468 	/* Register GValue transformation function not provided by any other library */
1469 	xfdashboard_register_gvalue_transformation_funcs();
1470 }
1471 
1472 /* Object initialization
1473  * Create private structure and set up default values
1474  */
xfdashboard_application_init(XfdashboardApplication * self)1475 static void xfdashboard_application_init(XfdashboardApplication *self)
1476 {
1477 	XfdashboardApplicationPrivate	*priv;
1478 	GSimpleAction					*action;
1479 
1480 	priv=self->priv=xfdashboard_application_get_instance_private(self);
1481 
1482 	/* Set default values */
1483 	priv->isDaemon=FALSE;
1484 	priv->isSuspended=FALSE;
1485 	priv->themeName=NULL;
1486 	priv->initialized=FALSE;
1487 	priv->xfconfChannel=NULL;
1488 	priv->stage=NULL;
1489 	priv->viewManager=NULL;
1490 	priv->searchManager=NULL;
1491 	priv->focusManager=NULL;
1492 	priv->theme=NULL;
1493 	priv->xfconfThemeChangedSignalID=0L;
1494 	priv->isQuitting=FALSE;
1495 	priv->sessionManagementClient=NULL;
1496 	priv->pluginManager=NULL;
1497 	priv->forcedNewInstance=FALSE;
1498 	priv->windowTrackerBackend=NULL;
1499 
1500 	/* Add callable DBUS actions for this application */
1501 	action=g_simple_action_new("Quit", NULL);
1502 	g_signal_connect(action, "activate", G_CALLBACK(xfdashboard_application_quit_forced), NULL);
1503 	g_action_map_add_action(G_ACTION_MAP(self), G_ACTION(action));
1504 	g_object_unref(action);
1505 }
1506 
1507 /* IMPLEMENTATION: Public API */
1508 
1509 /**
1510  * xfdashboard_application_has_default:
1511  *
1512  * Determine if the singleton instance of #XfdashboardApplication was created.
1513  * If the singleton instance of #XfdashboardApplication was created, it can be
1514  * retrieved with xfdashboard_application_get_default().
1515  *
1516  * This function is useful if only the availability of the singleton instance
1517  * wants to be checked as xfdashboard_application_get_default() will create this
1518  * singleton instance if not available.
1519  *
1520  * Return value: %TRUE if singleton instance of #XfdashboardApplication was
1521  *   created or %FALSE if not.
1522  */
xfdashboard_application_has_default(void)1523 gboolean xfdashboard_application_has_default(void)
1524 {
1525 	if(G_LIKELY(_xfdashboard_application)) return(TRUE);
1526 
1527 	return(FALSE);
1528 }
1529 
1530 /**
1531  * xfdashboard_application_get_default:
1532  *
1533  * Retrieves the singleton instance of #XfdashboardApplication.
1534  *
1535  * Return value: (transfer none): The instance of #XfdashboardApplication.
1536  *   The returned object is owned by Xfdashboard and it should not be
1537  *   unreferenced directly.
1538  */
xfdashboard_application_get_default(void)1539 XfdashboardApplication* xfdashboard_application_get_default(void)
1540 {
1541 	if(G_UNLIKELY(!_xfdashboard_application))
1542 	{
1543 		gchar			*appID;
1544 		const gchar		*forceNewInstance=NULL;
1545 
1546 #ifdef DEBUG
1547 		/* If a new instance of xfdashboard is forced, e.g. for debugging purposes,
1548 		 * then create a unique application ID.
1549 		 */
1550 		forceNewInstance=g_getenv("XFDASHBOARD_FORCE_NEW_INSTANCE");
1551 #endif
1552 
1553 		if(forceNewInstance)
1554 		{
1555 			appID=g_strdup_printf("%s-%u", XFDASHBOARD_APP_ID, getpid());
1556 			g_message("Forcing new application instance with ID '%s'", appID);
1557 		}
1558 			else appID=g_strdup(XFDASHBOARD_APP_ID);
1559 
1560 		_xfdashboard_application=g_object_new(XFDASHBOARD_TYPE_APPLICATION,
1561 												"application-id", appID,
1562 												"flags", G_APPLICATION_HANDLES_COMMAND_LINE,
1563 												NULL);
1564 
1565 		if(forceNewInstance)
1566 		{
1567 			_xfdashboard_application->priv->forcedNewInstance=TRUE;
1568 		}
1569 
1570 		/* Release allocated resources */
1571 		if(appID) g_free(appID);
1572 	}
1573 
1574 	return(_xfdashboard_application);
1575 }
1576 
1577 /**
1578  * xfdashboard_application_is_daemonized:
1579  * @self: A #XfdashboardApplication
1580  *
1581  * Checks if application is running in background (daemon mode).
1582  *
1583  * Return value: %TRUE if @self is running in background and
1584  *   %FALSE if it is running as standalone application.
1585  */
xfdashboard_application_is_daemonized(XfdashboardApplication * self)1586 gboolean xfdashboard_application_is_daemonized(XfdashboardApplication *self)
1587 {
1588 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), FALSE);
1589 
1590 	return(self->priv->isDaemon);
1591 }
1592 
1593 /**
1594  * xfdashboard_application_is_suspended:
1595  * @self: A #XfdashboardApplication
1596  *
1597  * Checks if application is suspended, that means it is not visible and
1598  * not active.
1599  *
1600  * Note: This state can only be check when @self is running in daemon mode.
1601  *
1602  * Return value: %TRUE if @self is suspended and %FALSE if it is resumed.
1603  */
xfdashboard_application_is_suspended(XfdashboardApplication * self)1604 gboolean xfdashboard_application_is_suspended(XfdashboardApplication *self)
1605 {
1606 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), FALSE);
1607 
1608 	return(self->priv->isSuspended);
1609 }
1610 
1611 /**
1612  * xfdashboard_application_is_quitting:
1613  * @self: A #XfdashboardApplication
1614  *
1615  * Checks if application is in progress to quit.
1616  *
1617  * Return value: %TRUE if @self is going to quit, otherwise %FALSE.
1618  */
xfdashboard_application_is_quitting(XfdashboardApplication * self)1619 gboolean xfdashboard_application_is_quitting(XfdashboardApplication *self)
1620 {
1621 	g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(self), FALSE);
1622 
1623 	return(self->priv->isQuitting);
1624 }
1625 
1626 /**
1627  * xfdashboard_application_resume:
1628  * @self: A #XfdashboardApplication or %NULL
1629  *
1630  * Resumes @self from suspended state, brings it to foreground
1631  * and activates it.
1632  *
1633  * If @self is %NULL the default singleton is used if it was created.
1634  */
xfdashboard_application_resume(XfdashboardApplication * self)1635 void xfdashboard_application_resume(XfdashboardApplication *self)
1636 {
1637 	g_return_if_fail(self==NULL || XFDASHBOARD_IS_APPLICATION(self));
1638 
1639 	/* Get default single instance if NULL is requested */
1640 	if(!self) self=_xfdashboard_application;
1641 
1642 	/* Resume application */
1643 	if(G_LIKELY(self))
1644 	{
1645 		_xfdashboard_application_activate(G_APPLICATION(self));
1646 	}
1647 }
1648 
1649 /**
1650  * xfdashboard_application_suspend_or_quit:
1651  * @self: A #XfdashboardApplication or %NULL
1652  *
1653  * Quits @self if running as standalone application or suspends @self
1654  * if runnning if running in daemon mode. Suspending application means
1655  * to hide application window and send it to background.
1656  *
1657  * If @self is %NULL the default singleton is used if it was created.
1658  */
xfdashboard_application_suspend_or_quit(XfdashboardApplication * self)1659 void xfdashboard_application_suspend_or_quit(XfdashboardApplication *self)
1660 {
1661 	g_return_if_fail(self==NULL || XFDASHBOARD_IS_APPLICATION(self));
1662 
1663 	/* Get default single instance if NULL is requested */
1664 	if(!self) self=_xfdashboard_application;
1665 
1666 	/* Quit application */
1667 	if(G_LIKELY(self))
1668 	{
1669 		_xfdashboard_application_quit(self, FALSE);
1670 	}
1671 }
1672 
1673 /**
1674  * xfdashboard_application_quit_forced:
1675  * @self: A #XfdashboardApplication or %NULL
1676  *
1677  * Quits @self regardless if it is running as standalone application
1678  * or in daemon mode. The application is really quitted after calling
1679  * this function.
1680  *
1681  * If @self is %NULL the default singleton is used if it was created.
1682  */
xfdashboard_application_quit_forced(XfdashboardApplication * self)1683 void xfdashboard_application_quit_forced(XfdashboardApplication *self)
1684 {
1685 	g_return_if_fail(self==NULL || XFDASHBOARD_IS_APPLICATION(self));
1686 
1687 	/* Get default single instance if NULL is requested */
1688 	if(!self) self=_xfdashboard_application;
1689 
1690 	/* Force application to quit */
1691 	if(G_LIKELY(self))
1692 	{
1693 		/* Quit also any other running instance */
1694 		if(g_application_get_is_remote(G_APPLICATION(self))==TRUE)
1695 		{
1696 			g_action_group_activate_action(G_ACTION_GROUP(self), "Quit", NULL);
1697 		}
1698 
1699 		/* Quit this instance */
1700 		_xfdashboard_application_quit(self, TRUE);
1701 	}
1702 }
1703 
1704 /**
1705  * xfdashboard_application_get_stage:
1706  * @self: A #XfdashboardApplication or %NULL
1707  *
1708  * Retrieve #XfdashboardStage of @self.
1709  *
1710  * If @self is %NULL the default singleton is used if it was created.
1711  *
1712  * Return value: (transfer none): The #XfdashboardStage of application at @self.
1713  */
xfdashboard_application_get_stage(XfdashboardApplication * self)1714 XfdashboardStage* xfdashboard_application_get_stage(XfdashboardApplication *self)
1715 {
1716 	XfdashboardStage		*stage;
1717 
1718 	g_return_val_if_fail(self==NULL || XFDASHBOARD_IS_APPLICATION(self), NULL);
1719 
1720 	stage=NULL;
1721 
1722 	/* Get default single instance if NULL is requested */
1723 	if(!self) self=_xfdashboard_application;
1724 
1725 	/* Get xfconf channel */
1726 	if(G_LIKELY(self)) stage=self->priv->stage;
1727 
1728 	return(stage);
1729 }
1730 
1731 /**
1732  * xfdashboard_application_get_theme:
1733  * @self: A #XfdashboardApplication or %NULL
1734  *
1735  * Retrieve the current #XfdashboardTheme of @self.
1736  *
1737  * If @self is %NULL the default singleton is used if it was created.
1738  *
1739  * Return value: (transfer none): The current #XfdashboardTheme of application at @self.
1740  */
xfdashboard_application_get_theme(XfdashboardApplication * self)1741 XfdashboardTheme* xfdashboard_application_get_theme(XfdashboardApplication *self)
1742 {
1743 	XfdashboardTheme		*theme;
1744 
1745 	g_return_val_if_fail(self==NULL || XFDASHBOARD_IS_APPLICATION(self), NULL);
1746 
1747 	theme=NULL;
1748 
1749 	/* Get default single instance if NULL is requested */
1750 	if(!self) self=_xfdashboard_application;
1751 
1752 	/* Get theme */
1753 	if(G_LIKELY(self)) theme=self->priv->theme;
1754 
1755 	return(theme);
1756 }
1757 
1758 /**
1759  * xfdashboard_application_get_xfconf_channel:
1760  * @self: A #XfdashboardApplication or %NULL
1761  *
1762  * Retrieve the #XfconfChannel of @self used to query or modify settings stored
1763  * in Xfconf.
1764  *
1765  * If @self is %NULL the default singleton is used if it was created.
1766  *
1767  * Return value: (transfer none): The current #XfconfChannel of application at @self.
1768  */
xfdashboard_application_get_xfconf_channel(XfdashboardApplication * self)1769 XfconfChannel* xfdashboard_application_get_xfconf_channel(XfdashboardApplication *self)
1770 {
1771 	XfconfChannel			*channel;
1772 
1773 	g_return_val_if_fail(self==NULL || XFDASHBOARD_IS_APPLICATION(self), NULL);
1774 
1775 	channel=NULL;
1776 
1777 	/* Get default single instance if NULL is requested */
1778 	if(!self) self=_xfdashboard_application;
1779 
1780 	/* Get xfconf channel */
1781 	if(G_LIKELY(self)) channel=self->priv->xfconfChannel;
1782 
1783 	return(channel);
1784 }
1785