1 /*
2 * main: Common functions, shared data and main entry point of application
3 *
4 * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 *
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <glib/gi18n-lib.h>
29 #include <gtk/gtk.h>
30 #include <clutter/clutter.h>
31 #ifdef CLUTTER_WINDOWING_X11
32 #include <clutter/x11/clutter-x11.h>
33 #endif
34 #include <libxfce4util/libxfce4util.h>
35 #include <libxfdashboard/application.h>
36 #include <libxfdashboard/window-tracker-backend.h>
37
38
39 typedef struct _RestartData RestartData;
40 struct _RestartData
41 {
42 GMainLoop *mainLoop;
43 XfdashboardApplication *application;
44 gboolean appHasQuitted;
45 };
46
47 #define DEFAULT_RESTART_WAIT_TIMEOUT 5000 /* Timeout in milliseconds */
48
49
50 /* Timeout to wait for application to disappear has been reached */
_on_quit_timeout(gpointer inUserData)51 static gboolean _on_quit_timeout(gpointer inUserData)
52 {
53 RestartData *restartData;
54
55 g_return_val_if_fail(inUserData, G_SOURCE_REMOVE);
56
57 restartData=(RestartData*)inUserData;
58
59 /* Enforce flag that application has been disappeared to be unset */
60 restartData->appHasQuitted=FALSE;
61
62 /* Return from main loop */
63 g_main_loop_quit(restartData->mainLoop);
64 g_debug("Timeout for waiting the application has been reached!");
65
66 /* Remove this source from main loop and prevent getting it called again */
67 return(G_SOURCE_REMOVE);
68 }
69
70 /* Idle callback for quitting application has been called */
_on_quit_idle(gpointer inUserData)71 static gboolean _on_quit_idle(gpointer inUserData)
72 {
73 /* Quit application */
74 xfdashboard_application_quit_forced(NULL);
75
76 /* Remove this source from main loop and prevent getting it called again */
77 return(G_SOURCE_REMOVE);
78 }
79
80 /* Requested name appeared at DBUS */
_on_dbus_watcher_name_appeared(GDBusConnection * inConnection,const gchar * inName,const gchar * inOwner,gpointer inUserData)81 static void _on_dbus_watcher_name_appeared(GDBusConnection *inConnection,
82 const gchar *inName,
83 const gchar *inOwner,
84 gpointer inUserData)
85 {
86 RestartData *restartData;
87 GSource *quitIdleSource;
88
89 g_return_if_fail(inUserData);
90
91 restartData=(RestartData*)inUserData;
92
93 /* Add an idle source to main loop to quit running application _after_
94 * the main loop is running and the DBUS watcher was set up.
95 */
96 quitIdleSource=g_idle_source_new();
97 g_source_set_callback(quitIdleSource, _on_quit_idle, NULL, NULL);
98 g_source_attach(quitIdleSource, g_main_loop_get_context(restartData->mainLoop));
99 g_source_unref(quitIdleSource);
100
101 g_debug("Name %s appeared at DBUS and is owned by %s", inName, inOwner);
102 }
103
104 /* Requested name vanished from DBUS */
_on_dbus_watcher_name_vanished(GDBusConnection * inConnection,const gchar * inName,gpointer inUserData)105 static void _on_dbus_watcher_name_vanished(GDBusConnection *inConnection,
106 const gchar *inName,
107 gpointer inUserData)
108 {
109 RestartData *restartData;
110
111 g_return_if_fail(inUserData);
112
113 restartData=(RestartData*)inUserData;
114
115 /* Set flag that application disappeared */
116 restartData->appHasQuitted=TRUE;
117
118 /* Now the application is gone and we can safely restart application.
119 * So return from main loop now.
120 */
121 g_main_loop_quit(restartData->mainLoop);
122
123 g_debug("Name %s does not exist at DBUS or vanished", inName);
124 }
125
126 /* Quit running application and restart application */
_restart(XfdashboardApplication * inApplication)127 static gboolean _restart(XfdashboardApplication *inApplication)
128 {
129 GMainLoop *mainLoop;
130 guint dbusWatcherID;
131 GSource *timeoutSource;
132 RestartData restartData;
133
134 g_return_val_if_fail(XFDASHBOARD_IS_APPLICATION(inApplication), FALSE);
135
136 /* Create main loop for watching DBUS for application to disappear */
137 mainLoop=g_main_loop_new(NULL, FALSE);
138
139 /* Set up user data for callbacks */
140 restartData.mainLoop=mainLoop;
141 restartData.application=inApplication;
142 restartData.appHasQuitted=FALSE;
143
144 /* Set up DBUS watcher to get noticed when application disappears
145 * which means it is safe to start a new instance. But if it takes
146 * too long assume that either application did not quit and is still
147 * running or we did get notified although we set up DBUS watcher
148 * before quitting application.
149 */
150 dbusWatcherID=g_bus_watch_name(G_BUS_TYPE_SESSION,
151 g_application_get_application_id(G_APPLICATION(inApplication)),
152 G_BUS_NAME_WATCHER_FLAGS_NONE,
153 _on_dbus_watcher_name_appeared,
154 _on_dbus_watcher_name_vanished,
155 &restartData,
156 NULL);
157
158 /* Add an idle source to main loop to quit running application _after_
159 * the main loop is running and the DBUS watcher was set up.
160 */
161 timeoutSource=g_timeout_source_new(DEFAULT_RESTART_WAIT_TIMEOUT);
162 g_source_set_callback(timeoutSource, _on_quit_timeout, &restartData, NULL);
163 g_source_attach(timeoutSource, g_main_loop_get_context(mainLoop));
164 g_source_unref(timeoutSource);
165
166 /* Run main loop */
167 g_debug("Starting main loop for waiting the application to quit");
168 g_main_loop_run(mainLoop);
169 g_debug("Returned from main loop for waiting the application to quit");
170
171 /* Show warning if timeout had been reached */
172 if(!restartData.appHasQuitted)
173 {
174 g_warning("Cannot restart application: Failed to quit running instance");
175 }
176
177 /* Destroy DBUS watcher */
178 g_bus_unwatch_name(dbusWatcherID);
179
180 /* Return TRUE if application was quitted successfully
181 * otherwise FALSE.
182 */
183 return(restartData.appHasQuitted);
184 }
185
186 /* Main entry point */
main(int argc,char ** argv)187 int main(int argc, char **argv)
188 {
189 XfdashboardApplication *app=NULL;
190 gint status;
191 #if CLUTTER_CHECK_VERSION(1, 16, 0)
192 const gchar *backend;
193 #endif
194
195 #ifdef ENABLE_NLS
196 /* Set up localization */
197 xfce_textdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
198 #endif
199
200 #if !GLIB_CHECK_VERSION(2, 36, 0)
201 /* Initialize GObject type system */
202 g_type_init();
203 #endif
204
205 #if CLUTTER_CHECK_VERSION(1, 16, 0)
206 /* Enforce X11 backend in Clutter if no specific backend was requested via
207 * the XFDASHBOARD_BACKEND environment variable. If this environment variable
208 * is set, enforce this backend.
209 *
210 * This function must be called before any API function call at libxfdashboard
211 * or other library API function like the one of Clutter, GTK+ etc.
212 */
213 backend=g_getenv("XFDASHBOARD_BACKEND");
214 if(backend)
215 {
216 xfdashboard_window_tracker_backend_set_backend(backend);
217 g_debug("Setting backend to '%s'", backend);
218 }
219 else
220 {
221 clutter_set_windowing_backend("x11");
222 g_debug("Enforcing X11 backend");
223 }
224 #endif
225
226 #ifdef CLUTTER_WINDOWING_X11
227 /* Tell clutter to try to initialize an RGBA visual if the X11 backend is
228 * used before fallback to default and use RGB visual without alpha channel.
229 */
230 clutter_x11_set_use_argb_visual(TRUE);
231 #endif
232
233 /* Initialize GTK+ and Clutter */
234 gtk_init(&argc, &argv);
235 if(!clutter_init(&argc, &argv))
236 {
237 g_error("Initializing clutter failed!");
238 return(1);
239 }
240
241 /* Notify that application has started and main loop will be entered */
242 gdk_notify_startup_complete();
243
244 /* Start application as primary or remote instace */
245 app=xfdashboard_application_get_default();
246 if(!app)
247 {
248 g_warning("Failed to create application instance");
249 return(XFDASHBOARD_APPLICATION_ERROR_FAILED);
250 }
251
252 status=g_application_run(G_APPLICATION(app), argc, argv);
253 if(status==XFDASHBOARD_APPLICATION_ERROR_RESTART &&
254 g_application_get_is_remote(G_APPLICATION(app)))
255 {
256 /* Wait for existing primary instance to quit */
257 if(_restart(app))
258 {
259 g_debug("Reached clean state to restart application");
260
261 /* Destroy remote instance application object for restart */
262 g_object_unref(app);
263 app=NULL;
264
265 /* Create new application instance which should become
266 * the new primary instance.
267 */
268 app=xfdashboard_application_get_default();
269 if(!app)
270 {
271 g_warning("Failed to create application instance");
272 return(XFDASHBOARD_APPLICATION_ERROR_FAILED);
273 }
274
275 g_debug("Starting new primary instance");
276 status=g_application_run(G_APPLICATION(app), argc, argv);
277 }
278 else
279 {
280 g_warning("Could not restart application because existing instance seems still to be running.");
281 }
282 }
283
284 /* Clean up, release allocated resources */
285 g_object_unref(app);
286
287 return(status);
288 }
289