]> git.sesse.net Git - vlc/blob - modules/misc/inhibit.c
MP4: fix a typo
[vlc] / modules / misc / inhibit.c
1 /*****************************************************************************
2  * inhibit.c : prevents the computer from suspending when VLC is playing
3  *****************************************************************************
4  * Copyright © 2007 Rafaël Carré
5  * $Id$
6  *
7  * Author: Rafaël Carré <funman@videolanorg>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program 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
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*
25  * Based on freedesktop Power Management Specification version 0.2
26  * http://people.freedesktop.org/~hughsient/temp/power-management-spec-0.2.html
27  */
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_input.h>
40 #include <vlc_interface.h>
41 #include <vlc_playlist.h>
42
43 #include <dbus/dbus.h>
44
45 enum {
46     FREEDESKTOP = 0, /* as used by KDE and gnome <= 2.26 */
47     GNOME       = 1, /* as used by gnome > 2.26 */
48 };
49
50 static const char *dbus_service[] = {
51     [FREEDESKTOP]   = "org.freedesktop.PowerManagement",
52     [GNOME]         = "org.gnome.SessionManager",
53 };
54
55 static const char *dbus_path[] = {
56     [FREEDESKTOP]   = "/org/freedesktop/PowerManagement",
57     [GNOME]         = "/org/gnome/SessionManager",
58 };
59
60 static const char *dbus_interface[] = {
61     [FREEDESKTOP]   = "org.freedesktop.PowerManagement.Inhibit",
62     [GNOME]         = "org.gnome.SessionManager",
63 };
64
65
66 /*****************************************************************************
67  * Local prototypes
68  !*****************************************************************************/
69 static int  Activate     ( vlc_object_t * );
70 static void Deactivate   ( vlc_object_t * );
71
72 static void UnInhibit( intf_thread_t *p_intf, int type );
73
74 static int InputChange( vlc_object_t *, const char *,
75                         vlc_value_t, vlc_value_t, void * );
76 static int StateChange( vlc_object_t *, const char *,
77                         vlc_value_t, vlc_value_t, void * );
78
79 struct intf_sys_t
80 {
81     playlist_t      *p_playlist;
82     vlc_object_t    *p_input;
83     DBusConnection  *p_conn;
84     dbus_uint32_t   i_cookie[2];
85 };
86
87 /*****************************************************************************
88  * Module descriptor
89  *****************************************************************************/
90 vlc_module_begin ()
91     set_description( N_("Power Management Inhibitor") )
92     set_capability( "interface", 0 )
93     set_callbacks( Activate, Deactivate )
94 vlc_module_end ()
95
96 /*****************************************************************************
97  * Activate: initialize and create stuff
98  *****************************************************************************/
99 static int Activate( vlc_object_t *p_this )
100 {
101     intf_thread_t *p_intf = (intf_thread_t*)p_this;
102     intf_sys_t *p_sys;
103     DBusError     error;
104
105     p_sys = p_intf->p_sys = (intf_sys_t *) calloc( 1, sizeof( intf_sys_t ) );
106     if( !p_sys )
107         return VLC_ENOMEM;
108
109     p_sys->i_cookie[FREEDESKTOP] = 0;
110     p_sys->i_cookie[GNOME] = 0;
111     p_sys->p_input = NULL;
112
113     dbus_error_init( &error );
114
115     /* connect privately to the session bus
116      * the connection will not be shared with other vlc modules which use dbus,
117      * thus avoiding a whole class of concurrency issues */
118     p_sys->p_conn = dbus_bus_get_private( DBUS_BUS_SESSION, &error );
119     if( !p_sys->p_conn )
120     {
121         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
122                 error.message );
123         dbus_error_free( &error );
124         free( p_sys );
125         return VLC_EGENERIC;
126     }
127
128     p_sys->p_playlist = pl_Get( p_intf );
129     var_AddCallback( p_sys->p_playlist, "item-current", InputChange, p_intf );
130     return VLC_SUCCESS;
131 }
132
133 /*****************************************************************************
134  * Deactivate: uninitialize and cleanup
135  *****************************************************************************/
136 static void Deactivate( vlc_object_t *p_this )
137 {
138     intf_thread_t *p_intf = (intf_thread_t*)p_this;
139     intf_sys_t *p_sys = p_intf->p_sys;
140
141     var_DelCallback( p_sys->p_playlist, "item-current", InputChange, p_intf );
142
143     if( p_sys->p_input ) /* Do delete "state" after "item-changed"! */
144     {
145         var_DelCallback( p_sys->p_input, "state", StateChange, p_intf );
146         vlc_object_release( p_sys->p_input );
147     }
148
149     if( p_sys->i_cookie[FREEDESKTOP] )
150         UnInhibit( p_intf, FREEDESKTOP );
151     if( p_sys->i_cookie[GNOME] )
152         UnInhibit( p_intf, GNOME );
153
154     /* The dbus connection is private,
155      * so we are responsible for closing it */
156     dbus_connection_close( p_sys->p_conn );
157     dbus_connection_unref( p_sys->p_conn );
158
159     free( p_sys );
160 }
161
162 /*****************************************************************************
163  * Inhibit: Notify the power management daemon that it shouldn't suspend
164  * the computer because of inactivity
165  *****************************************************************************/
166 static void Inhibit( intf_thread_t *p_intf, int type )
167 {
168     intf_sys_t *p_sys = p_intf->p_sys;
169
170     DBusMessage *msg = dbus_message_new_method_call(
171         dbus_service[type], dbus_path[type], dbus_interface[type], "Inhibit" );
172     if( unlikely(msg == NULL) )
173         return;
174
175     const char *app = PACKAGE;
176     const char *reason = _("Playing some media.");
177
178     p_sys->i_cookie[type] = 0;
179
180     dbus_bool_t ret;
181     dbus_uint32_t xid = 0; // FIXME?
182     dbus_uint32_t flags = 8 /* Inhibit suspending the session or computer */
183                         | 4;/* Inhibit the session being marked as idle */
184     switch( type ) {
185     case FREEDESKTOP:
186         ret = dbus_message_append_args( msg, DBUS_TYPE_STRING, &app,
187                                         DBUS_TYPE_STRING, &reason,
188                                         DBUS_TYPE_INVALID );
189         break;
190     case GNOME:
191     default:
192         ret = dbus_message_append_args( msg, DBUS_TYPE_STRING, &app,
193                                         DBUS_TYPE_UINT32, &xid,
194                                         DBUS_TYPE_STRING, &reason,
195                                         DBUS_TYPE_UINT32, &flags,
196                                         DBUS_TYPE_INVALID );
197         break;
198     }
199
200     if( !ret )
201     {
202         dbus_message_unref( msg );
203         return;
204     }
205
206     /* blocks 50ms maximum */
207     DBusMessage *reply;
208
209     reply = dbus_connection_send_with_reply_and_block( p_sys->p_conn, msg,
210                                                        50, NULL );
211     dbus_message_unref( msg );
212     if( reply == NULL )
213         /* g-p-m is not active, or too slow. Better luck next time? */
214         return;
215
216     /* extract the cookie from the reply */
217     dbus_uint32_t i_cookie;
218
219     if( dbus_message_get_args( reply, NULL,
220                                DBUS_TYPE_UINT32, &i_cookie,
221                                DBUS_TYPE_INVALID ) )
222         p_sys->i_cookie[type] = i_cookie;
223
224     dbus_message_unref( reply );
225 }
226
227 /*****************************************************************************
228  * UnInhibit: Notify the power management daemon that we aren't active anymore
229  *****************************************************************************/
230 static void UnInhibit( intf_thread_t *p_intf, int type )
231 {
232     intf_sys_t *p_sys = p_intf->p_sys;
233
234     DBusMessage *msg = dbus_message_new_method_call( dbus_service[type],
235             dbus_path[type], dbus_interface[type], "UnInhibit" );
236     if( unlikely(msg == NULL) )
237         return;
238
239     dbus_uint32_t i_cookie = p_sys->i_cookie[type];
240     if( dbus_message_append_args( msg, DBUS_TYPE_UINT32, &i_cookie,
241                                        DBUS_TYPE_INVALID )
242      && dbus_connection_send( p_sys->p_conn, msg, NULL ) )
243     {
244         dbus_connection_flush( p_sys->p_conn );
245         p_sys->i_cookie[type] = 0;
246     }
247     dbus_message_unref( msg );
248 }
249
250
251 static int StateChange( vlc_object_t *p_input, const char *var,
252                         vlc_value_t prev, vlc_value_t value, void *data )
253 {
254     intf_thread_t *p_intf = data;
255     intf_sys_t *p_sys = p_intf->p_sys;
256     const int old = prev.i_int, cur = value.i_int;
257
258     if( ( old == PLAYING_S ) == ( cur == PLAYING_S ) )
259         return VLC_SUCCESS; /* No interesting change */
260
261     if( cur == PLAYING_S ) {
262         if (p_sys->i_cookie[FREEDESKTOP] == 0)
263             Inhibit( p_intf, FREEDESKTOP );
264         if (p_sys->i_cookie[GNOME] == 0)
265             Inhibit( p_intf, GNOME );
266     }
267     else {
268         if (p_sys->i_cookie[FREEDESKTOP] != 0)
269             UnInhibit( p_intf, FREEDESKTOP );
270         if (p_sys->i_cookie[GNOME] != 0)
271             UnInhibit( p_intf, GNOME );
272     }
273
274     (void)p_input; (void)var; (void)prev;
275     return VLC_SUCCESS;
276 }
277
278 static int InputChange( vlc_object_t *p_playlist, const char *var,
279                         vlc_value_t prev, vlc_value_t value, void *data )
280 {
281     intf_thread_t *p_intf = data;
282     intf_sys_t *p_sys = p_intf->p_sys;
283
284     if( p_sys->p_input )
285     {
286         var_DelCallback( p_sys->p_input, "state", StateChange, p_intf );
287         vlc_object_release( p_sys->p_input );
288     }
289     p_sys->p_input = VLC_OBJECT(playlist_CurrentInput( p_sys->p_playlist ));
290     if( p_sys->p_input )
291     {
292         Inhibit( p_intf, FREEDESKTOP );
293         Inhibit( p_intf, GNOME );
294
295         var_AddCallback( p_sys->p_input, "state", StateChange, p_intf );
296     }
297
298     (void)var; (void)prev; (void)value; (void)p_playlist;
299     return VLC_SUCCESS;
300 }