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