]> git.sesse.net Git - vlc/blob - modules/misc/screensaver.c
7abfea1e32f461cbbb1840476dcd40c470de1606
[vlc] / modules / misc / screensaver.c
1 /*****************************************************************************
2  * screensaver.c : disable screen savers when VLC is playing
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sam Hocevar <sam@zoy.org>
8  *          Benjamin Pracht <bigben AT videolan DOT org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_input.h>
36 #include <vlc_playlist.h>
37 #include <vlc_interface.h>
38
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 #include <signal.h>
43
44 #ifdef HAVE_DBUS
45
46 #include <dbus/dbus.h>
47
48 #define GS_SERVICE   "org.gnome.ScreenSaver"
49 #define GS_PATH      "/org/gnome/ScreenSaver"
50 #define GS_INTERFACE "org.gnome.ScreenSaver"
51
52 #define FDS_SERVICE   "org.freedesktop.ScreenSaver"
53 #define FDS_PATH      "/ScreenSaver"
54 #define FDS_INTERFACE "org.freedesktop.ScreenSaver"
55
56 #endif
57
58 /*****************************************************************************
59  * Local prototypes
60  *****************************************************************************/
61 static int  Activate     ( vlc_object_t * );
62 static void  Deactivate   ( vlc_object_t * );
63
64 static void Timer( void * );
65
66 #ifdef HAVE_DBUS
67
68 static DBusConnection * dbus_init( intf_thread_t *p_intf );
69 static void poke_screensaver( intf_thread_t *p_intf,
70                               DBusConnection *p_connection );
71 static void screensaver_send_message_void ( intf_thread_t *p_intf,
72                                        DBusConnection *p_connection,
73                                        const char *psz_service,
74                                        const char *psz_path,
75                                        const char *psz_interface,
76                                        const char *psz_name );
77 static bool screensaver_is_running( DBusConnection *p_connection, const char *psz_service );
78 #endif
79
80 struct intf_sys_t
81 {
82 #ifdef HAVE_DBUS
83     DBusConnection *p_connection;
84 #endif
85     vlc_timer_t timer;
86 };
87
88
89 /*****************************************************************************
90  * Module descriptor
91  *****************************************************************************/
92 vlc_module_begin ()
93     set_description( N_("X Screensaver disabler") )
94     set_capability( "interface", 0 )
95     set_callbacks( Activate, Deactivate )
96 vlc_module_end ()
97
98 /*****************************************************************************
99  * Activate: initialize and create stuff
100  *****************************************************************************/
101 static int Activate( vlc_object_t *p_this )
102 {
103     intf_thread_t *p_intf = (intf_thread_t*)p_this;
104     intf_sys_t *p_sys;
105
106     p_sys = p_intf->p_sys = (intf_sys_t *)malloc( sizeof( intf_sys_t ) );
107     if( !p_sys )
108         return VLC_ENOMEM;
109
110     if( vlc_timer_create( &p_sys->timer, Timer, p_intf ) )
111     {
112         free( p_sys );
113         return VLC_ENOMEM;
114     }
115     vlc_timer_schedule( &p_sys->timer, false, 30*CLOCK_FREQ, 30*CLOCK_FREQ );
116
117 #ifdef HAVE_DBUS
118     p_sys->p_connection = dbus_init( p_intf );
119 #endif
120     return VLC_SUCCESS;
121 }
122
123 /*****************************************************************************
124  * Deactivate: uninitialize and cleanup
125  *****************************************************************************/
126 static void Deactivate( vlc_object_t *p_this )
127 {
128     intf_thread_t *p_intf = (intf_thread_t*)p_this;
129     intf_sys_t *p_sys = p_intf->p_sys;
130
131     vlc_timer_destroy( &p_sys->timer );
132 #ifdef HAVE_DBUS
133     if( p_sys->p_connection )
134         dbus_connection_unref( p_sys->p_connection );
135 #endif
136
137     free( p_sys );
138 }
139
140 /*****************************************************************************
141  * Execute: Spawns a process using execv()
142  *****************************************************************************/
143 static void Execute( intf_thread_t *p_this, const char *const *ppsz_args )
144 {
145     pid_t pid = fork();
146     switch( pid )
147     {
148         case 0:     /* we're the child */
149         {
150             sigset_t set;
151             sigemptyset (&set);
152             pthread_sigmask (SIG_SETMASK, &set, NULL);
153
154             /* We don't want output */
155             if( ( freopen( "/dev/null", "w", stdout ) != NULL )
156              && ( freopen( "/dev/null", "w", stderr ) != NULL ) )
157                 execv( ppsz_args[0] , (char *const *)ppsz_args );
158             /* If the file we want to execute doesn't exist we exit() */
159             exit( EXIT_FAILURE );
160         }
161         case -1:    /* we're the error */
162             msg_Dbg( p_this, "Couldn't fork() while launching %s",
163                      ppsz_args[0] );
164             break;
165         default:    /* we're the parent */
166             /* Wait for the child to exit.
167              * We will not deadlock because we ran "/bin/sh &" */
168             while( waitpid( pid, NULL, 0 ) != pid);
169             break;
170     }
171 }
172
173 /*****************************************************************************
174  * Run: main thread
175  *****************************************************************************
176  * This part of the module is in a separate thread so that we do not have
177  * too much system() overhead.
178  *****************************************************************************/
179 static void Timer( void *data )
180 {
181     intf_thread_t *p_intf = data;
182     playlist_t *p_playlist = pl_Hold( p_intf );
183     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
184     pl_Release( p_intf );
185     if( !p_input )
186         return;
187
188     vlc_object_t *p_vout;
189     if( var_GetInteger( p_input, "state" ) == PLAYING_S )
190         p_vout = (vlc_object_t *)input_GetVout( p_input );
191     else
192         p_vout = NULL;
193     vlc_object_release( p_input );
194     if( !p_vout )
195         return;
196     vlc_object_release( p_vout );
197
198     /* If there is a playing video output, disable xscreensaver */
199     /* http://www.jwz.org/xscreensaver/faq.html#dvd */
200     const char *const ppsz_xsargs[] = { "/bin/sh", "-c",
201         "xscreensaver-command -deactivate &", (char*)NULL };
202     Execute( p_intf, ppsz_xsargs );
203
204     /* If we have dbus support, let's communicate directly with
205      * gnome-screensave else, run gnome-screensaver-command */
206 #ifdef HAVE_DBUS
207     poke_screensaver( p_intf, p_intf->p_sys->p_connection );
208 #else
209     const char *const ppsz_gsargs[] = { "/bin/sh", "-c",
210         "gnome-screensaver-command --poke &", (char*)NULL };
211     Execute( p_intf, ppsz_gsargs );
212 #endif
213     /* FIXME: add support for other screensavers */
214 }
215
216 #ifdef HAVE_DBUS
217
218 static DBusConnection * dbus_init( intf_thread_t *p_intf )
219 {
220     DBusError dbus_error;
221
222     dbus_error_init (&dbus_error);
223     DBusConnection * p_connection = dbus_bus_get( DBUS_BUS_SESSION, &dbus_error );
224
225     if ( !p_connection )
226     {
227         msg_Warn( p_intf, "failed to connect to the D-BUS daemon: %s",
228                           dbus_error.message);
229         dbus_error_free( &dbus_error );
230         return NULL;
231     }
232
233     return p_connection;
234 }
235
236 static void poke_screensaver( intf_thread_t *p_intf,
237                               DBusConnection *p_connection )
238 {
239     if( screensaver_is_running( p_connection, GS_SERVICE ) )
240     {
241 #   ifdef SCREENSAVER_DEBUG
242         msg_Dbg( p_intf, "found a running gnome-screensaver instance" );
243 #   endif
244         /* gnome-screensaver changed it's D-Bus interface, so we need both */
245         screensaver_send_message_void( p_intf, p_connection, GS_SERVICE, GS_PATH,
246                                        GS_INTERFACE, "Poke" );
247         screensaver_send_message_void( p_intf, p_connection, GS_SERVICE, GS_PATH,
248                                        GS_INTERFACE, "SimulateUserActivity" );
249     }
250     else if( screensaver_is_running( p_connection, FDS_SERVICE ) )
251     {
252 #   ifdef SCREENSAVER_DEBUG
253         msg_Dbg( p_intf, "found a running freedesktop-screensaver instance" );
254 #   endif
255         screensaver_send_message_void( p_intf, p_connection, FDS_SERVICE, FDS_PATH,
256                                        FDS_INTERFACE, "SimulateUserActivity" );
257     }
258 #   ifdef SCREENSAVER_DEBUG
259     else
260     {
261         msg_Dbg( p_intf, "found no running (gnome|freedesktop)-screensaver instance" );
262     }
263 #   endif
264
265 }
266
267 static void screensaver_send_message_void ( intf_thread_t *p_intf,
268                                        DBusConnection *p_connection,
269                                        const char *psz_service,
270                                        const char *psz_path,
271                                        const char *psz_interface,
272                                        const char *psz_name )
273 {
274     DBusMessage *p_message;
275
276     if( !p_connection || !psz_name ) return;
277
278     p_message = dbus_message_new_method_call( psz_service, psz_path,
279                                               psz_interface, psz_name );
280     if( p_message == NULL )
281     {
282         msg_Err( p_intf, "DBUS initialization failed: message initialization" );
283         return;
284     }
285
286     if( !dbus_connection_send( p_connection, p_message, NULL ) )
287     {
288         msg_Err( p_intf, "DBUS communication failed" );
289     }
290
291     dbus_connection_flush( p_connection );
292
293     dbus_message_unref( p_message );
294 }
295
296 static bool screensaver_is_running( DBusConnection *p_connection, const char *psz_service )
297 {
298     DBusError error;
299     bool b_return;
300
301     if( !p_connection ) return false;
302
303     dbus_error_init( &error );
304     b_return = dbus_bus_name_has_owner( p_connection, psz_service, &error );
305     if( dbus_error_is_set( &error ) ) dbus_error_free (&error);
306
307     return b_return;
308 }
309
310 #endif
311