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