1 /*
2  * Nautilus-Actions
3  * A Nautilus extension which offers configurable context menu actions.
4  *
5  * Copyright (C) 2005 The GNOME Foundation
6  * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7  * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8  *
9  * Nautilus-Actions is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * Nautilus-Actions is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Nautilus-Actions; see the file COPYING. If not, see
21  * <http://www.gnu.org/licenses/>.
22  *
23  * Authors:
24  *   Frederic Ruaudel <grumz@grumz.net>
25  *   Rodrigo Moya <rodrigo@gnome-db.org>
26  *   Pierre Wieser <pwieser@trychlos.org>
27  *   ... and many others (see AUTHORS)
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include "base-isession.h"
35 #include "base-marshal.h"
36 #include "egg-sm-client.h"
37 
38 /* private interface data
39  */
40 struct _BaseISessionInterfacePrivate {
41 	void *empty;						/* so that gcc -pedantic is happy */
42 };
43 
44 /* pseudo-properties, set against the instance
45  */
46 typedef struct {
47 	EggSMClient *sm_client;
48 	gulong       sm_client_quit_handler_id;
49 	gulong       sm_client_quit_requested_handler_id;
50 }
51 	ISessionData;
52 
53 #define BASE_PROP_ISESSION_DATA			"base-prop-isession-data"
54 
55 /* signals defined by the BaseISession interface
56  */
57 enum {
58 	QUIT_REQUESTED,
59 	QUIT,
60 	LAST_SIGNAL
61 };
62 
63 static guint st_initializations = 0;	/* interface initialisation count */
64 static gint  st_signals[ LAST_SIGNAL ] = { 0 };
65 
66 static GType         register_type( void );
67 static void          interface_base_init( BaseISessionInterface *klass );
68 static void          interface_base_finalize( BaseISessionInterface *klass );
69 
70 static ISessionData *get_isession_data( BaseISession *instance );
71 static void          on_instance_finalized( gpointer user_data, BaseISession *instance );
72 static void          client_quit_requested_cb( EggSMClient *client, BaseISession *instance );
73 static gboolean      on_quit_requested_class_handler( BaseISession *instance, void *user_data );
74 static gboolean      signal_accumulator_false_handled( GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer dummy);
75 static void          client_quit_cb( EggSMClient *client, BaseISession *instance );
76 static void          on_quit_class_handler( BaseISession *instance, void *user_data );
77 
78 GType
base_isession_get_type(void)79 base_isession_get_type( void )
80 {
81 	static GType iface_type = 0;
82 
83 	if( !iface_type ){
84 		iface_type = register_type();
85 	}
86 
87 	return( iface_type );
88 }
89 
90 static GType
register_type(void)91 register_type( void )
92 {
93 	static const gchar *thisfn = "base_isession_register_type";
94 	GType type;
95 
96 	static const GTypeInfo info = {
97 		sizeof( BaseISessionInterface ),
98 		( GBaseInitFunc ) interface_base_init,
99 		( GBaseFinalizeFunc ) interface_base_finalize,
100 		NULL,
101 		NULL,
102 		NULL,
103 		0,
104 		0,
105 		NULL
106 	};
107 
108 	g_debug( "%s", thisfn );
109 
110 	type = g_type_register_static( G_TYPE_INTERFACE, "BaseISession", &info, 0 );
111 
112 	g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
113 
114 	return( type );
115 }
116 
117 static void
interface_base_init(BaseISessionInterface * klass)118 interface_base_init( BaseISessionInterface *klass )
119 {
120 	static const gchar *thisfn = "base_isession_interface_base_init";
121 
122 	if( !st_initializations ){
123 
124 		g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
125 
126 		/**
127 		 * base-signal-isession-quit-requested:
128 		 *
129 		 * The signal is emitted when the session is about to terminate,
130 		 * to determine if all implementations are actually willing to quit.
131 		 *
132 		 * If the implementation is not willing to quit, then the user handler
133 		 * should return %FALSE to stop the signal emission.
134 		 *
135 		 * Returning %TRUE will let the signal be emitted to other connected
136 		 * handlers, eventually authorizing the application to terminate.
137 		 *
138 		 * Notes about GLib signals.
139 		 *
140 		 * If the signal is defined as G_SIGNAL_RUN_CLEANUP, then the object
141 		 * handler does not participate to the return value of the signal
142 		 * (accumulator is not called). More this object handler is triggered
143 		 * unconditionnally, event if a user handler has returned %FALSE to
144 		 * stop the emission.
145 		 *
146 		 * Contrarily, if the signal is defined as G_SIGNAL_RUN_LAST, then the
147 		 * object handler returned value is taken into account by the accumulator,
148 		 * and can participate to the return value for the signal. If a user
149 		 * handler returns FALSE to stop the emission, then the object handler
150 		 * will not be triggered.
151 		 */
152 		st_signals[ QUIT_REQUESTED ] =
153 			g_signal_new_class_handler(
154 					BASE_SIGNAL_QUIT_REQUESTED,
155 					G_TYPE_FROM_CLASS( klass ),
156 					G_SIGNAL_RUN_LAST,
157 					G_CALLBACK( on_quit_requested_class_handler ),
158 					( GSignalAccumulator ) signal_accumulator_false_handled,
159 					NULL,
160 					base_cclosure_marshal_BOOLEAN__VOID,
161 					G_TYPE_BOOLEAN,
162 					0 );
163 
164 		/**
165 		 * base-signal-isession-quit:
166 		 *
167 		 * The signal is emitted when the session is about to terminate,
168 		 * and no application has refused to abort then end of session
169 		 * (all have accepted the end). It is time for the application
170 		 * to terminate cleanly.
171 		 */
172 		st_signals[ QUIT ] =
173 			g_signal_new_class_handler(
174 					BASE_SIGNAL_QUIT,
175 					G_TYPE_FROM_CLASS( klass ),
176 					G_SIGNAL_RUN_LAST,
177 					G_CALLBACK( on_quit_class_handler ),
178 					NULL,
179 					NULL,
180 					g_cclosure_marshal_VOID__VOID,
181 					G_TYPE_NONE,
182 					0 );
183 
184 		klass->private = g_new0( BaseISessionInterfacePrivate, 1 );
185 	}
186 
187 	st_initializations += 1;
188 }
189 
190 static void
interface_base_finalize(BaseISessionInterface * klass)191 interface_base_finalize( BaseISessionInterface *klass )
192 {
193 	static const gchar *thisfn = "base_isession_interface_base_finalize";
194 
195 	st_initializations -= 1;
196 
197 	if( !st_initializations ){
198 
199 		g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
200 
201 		g_free( klass->private );
202 	}
203 }
204 
205 static ISessionData *
get_isession_data(BaseISession * instance)206 get_isession_data( BaseISession *instance )
207 {
208 	ISessionData *data;
209 
210 	data = ( ISessionData * ) g_object_get_data( G_OBJECT( instance ), BASE_PROP_ISESSION_DATA );
211 
212 	if( !data ){
213 		data = g_new0( ISessionData, 1 );
214 		g_object_set_data( G_OBJECT( instance ), BASE_PROP_ISESSION_DATA, data );
215 		g_object_weak_ref( G_OBJECT( instance ), ( GWeakNotify ) on_instance_finalized, NULL );
216 	}
217 
218 	return( data );
219 }
220 
221 static void
on_instance_finalized(gpointer user_data,BaseISession * instance)222 on_instance_finalized( gpointer user_data, BaseISession *instance )
223 {
224 	static const gchar *thisfn = "base_isession_on_instance_finalized";
225 	ISessionData *data;
226 
227 	g_debug( "%s: instance=%p, user_data=%p", thisfn, ( void * ) instance, ( void * ) user_data );
228 
229 	data = get_isession_data( instance );
230 
231 	if( data->sm_client_quit_handler_id &&
232 		g_signal_handler_is_connected( data->sm_client, data->sm_client_quit_handler_id )){
233 			g_signal_handler_disconnect( data->sm_client, data->sm_client_quit_handler_id  );
234 	}
235 
236 	if( data->sm_client_quit_requested_handler_id &&
237 		g_signal_handler_is_connected( data->sm_client, data->sm_client_quit_requested_handler_id )){
238 			g_signal_handler_disconnect( data->sm_client, data->sm_client_quit_requested_handler_id  );
239 	}
240 
241 	if( data->sm_client ){
242 		g_object_unref( data->sm_client );
243 	}
244 
245 	g_free( data );
246 }
247 
248 void
base_isession_init(BaseISession * instance)249 base_isession_init( BaseISession *instance )
250 {
251 	static const gchar *thisfn = "base_isession_init";
252 	ISessionData *data;
253 
254 	g_return_if_fail( BASE_IS_ISESSION( instance ));
255 
256 	g_debug( "%s: instance=%p", thisfn, ( void * ) instance );
257 
258 	data = get_isession_data( instance );
259 
260 	/* initialize the session manager
261 	 */
262 	egg_sm_client_set_mode( EGG_SM_CLIENT_MODE_NO_RESTART );
263 	data->sm_client = egg_sm_client_get();
264 	egg_sm_client_startup();
265 	g_debug( "%s: sm_client=%p", thisfn, ( void * ) data->sm_client );
266 
267 	data->sm_client_quit_handler_id =
268 			g_signal_connect(
269 					data->sm_client,
270 					"quit-requested",
271 					G_CALLBACK( client_quit_requested_cb ),
272 					instance );
273 
274 	data->sm_client_quit_requested_handler_id =
275 			g_signal_connect(
276 					data->sm_client,
277 					"quit",
278 					G_CALLBACK( client_quit_cb ),
279 					instance );
280 }
281 
282 /*
283  * base_isession_is_willing_to_quit:
284  * @instance: this #BaseISession instance.
285  *
286  * Returns: %TRUE if the implementation is willing to quit, %FALSE else.
287  *
288  * From the session management strict point of view, this function may be
289  * just static and reserved for the own internal use of the interface.
290  * We make it public because it is also useful for the application itself,
291  * and we are so able to reuse both the signal and its handler mechanisms.
292  */
293 gboolean
base_isession_is_willing_to_quit(const BaseISession * instance)294 base_isession_is_willing_to_quit( const BaseISession *instance )
295 {
296 	static const gchar *thisfn = "base_isession_is_willing_to_quit";
297 	GValue instance_params = {0};
298 	GValue return_value = {0};
299 
300 	g_return_val_if_fail( BASE_IS_ISESSION( instance ), TRUE );
301 
302 	g_debug( "%s: instance=%p (%s)", thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
303 
304 	g_value_init( &instance_params, G_TYPE_FROM_INSTANCE( instance ));
305 	g_value_set_instance( &instance_params, ( gpointer ) instance );
306 
307 	g_value_init( &return_value, G_TYPE_BOOLEAN );
308 	g_value_set_boolean( &return_value, TRUE );
309 
310 	g_signal_emitv( &instance_params, st_signals[ QUIT_REQUESTED ], 0, &return_value );
311 
312 	return( g_value_get_boolean( &return_value ));
313 }
314 
315 /*
316  * the session manager advertises us that the session is about to exit
317  *
318  * Returns: %TRUE if the application is willing to quit, %FALSE else.
319  *
320  * This function is called when the session manager detects the end of
321  * session and thus asks its clients if they are willing to quit.
322  */
323 static void
client_quit_requested_cb(EggSMClient * client,BaseISession * instance)324 client_quit_requested_cb( EggSMClient *client, BaseISession *instance )
325 {
326 	static const gchar *thisfn = "base_isession_client_quit_requested_cb";
327 	gboolean willing_to;
328 
329 	g_debug( "%s: client=%p, instance=%p (%s)",
330 			thisfn,
331 			( void * ) client,
332 			( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
333 
334 	willing_to = base_isession_is_willing_to_quit( instance );
335 
336 	egg_sm_client_will_quit( client, willing_to );
337 }
338 
339 /*
340  * Handler of BASE_SIGNAL_QUIT_REQUESTED message
341  *
342  * Application should handle this signal in order to trap application
343  * termination, returning %FALSE if it is not willing to quit.
344  */
345 static gboolean
on_quit_requested_class_handler(BaseISession * instance,void * user_data)346 on_quit_requested_class_handler( BaseISession *instance, void *user_data )
347 {
348 	static const gchar *thisfn = "base_isession_on_quit_requested_class_handler";
349 
350 	g_return_val_if_fail( BASE_IS_ISESSION( instance ), TRUE );
351 
352 	g_debug( "%s: instance=%p (%s), user_data=%p",
353 			thisfn,
354 			( void * ) instance, G_OBJECT_TYPE_NAME( instance ),
355 			( void * ) user_data );
356 
357 	return( TRUE );
358 }
359 
360 /*
361  * the first handler which returns FALSE stops the emission
362  * this is used on BASE_SIGNAL_QUIT_REQUESTED signal
363  */
364 static gboolean
signal_accumulator_false_handled(GSignalInvocationHint * hint,GValue * return_accu,const GValue * handler_return,gpointer dummy)365 signal_accumulator_false_handled(
366 		GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer dummy)
367 {
368 	static const gchar *thisfn = "base_isession_signal_accumulator_false_handled";
369 	gboolean continue_emission;
370 	gboolean willing_to_quit;
371 
372 	willing_to_quit = g_value_get_boolean( handler_return );
373 	g_value_set_boolean( return_accu, willing_to_quit );
374 	continue_emission = willing_to_quit;
375 
376 	g_debug( "%s: willing_to handler returns %s", thisfn, willing_to_quit ? "True":"False" );
377 	return( continue_emission );
378 }
379 
380 /*
381  * cleanly terminate the application when exiting the session
382  * -> triggers the implementation
383  */
384 static void
client_quit_cb(EggSMClient * client,BaseISession * instance)385 client_quit_cb( EggSMClient *client, BaseISession *instance )
386 {
387 	static const gchar *thisfn = "base_isession_client_quit_cb";
388 
389 	g_return_if_fail( BASE_IS_ISESSION( instance ));
390 
391 	g_debug( "%s: client=%p, instance=%p", thisfn, ( void * ) client, ( void * ) instance );
392 
393 	g_signal_emit_by_name( G_OBJECT( instance ), BASE_SIGNAL_QUIT, NULL );
394 }
395 
396 static void
on_quit_class_handler(BaseISession * instance,void * user_data)397 on_quit_class_handler( BaseISession *instance, void *user_data )
398 {
399 	static const gchar *thisfn = "base_isession_on_quit_class_handler";
400 
401 	g_return_if_fail( BASE_IS_ISESSION( instance ));
402 
403 	g_debug( "%s: instance=%p (%s), user_data=%p",
404 			thisfn,
405 			( void * ) instance, G_OBJECT_TYPE_NAME( instance ),
406 			( void * ) user_data );
407 }
408