Minor cleanups (memory, D-Bus, signals)
[headphoned] / src / headphoned.c
1 /**
2  * headphoned for the Nokia N8x0 and the N900
3  *
4  * The headphone daemon watches the state of the headphone
5  * plug (connected, disconnected) and carries out actions
6  * based on these events.
7  *
8  * Currently supported:
9  *   * Send "pause" to the media player on disconnect
10  *   * Maintain different volume settings for each state
11  *
12  * Initial working version: 2009-10-21
13  *
14  * Copyright (c) 2009-2010 Thomas Perl <thpinfo.com>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this package; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
29  **/
30
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <assert.h>
34 #include <glib.h>
35 #include <gconf/gconf-client.h>
36 #include <libosso.h>
37 #include <dbus/dbus.h>
38 #include <fcntl.h>
39
40 #include "config.h"
41
42 #define STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state"
43 #define STATE_CONNECTED_STR "connected"
44 #define STATE_DISCONNECTED_STR "disconnected"
45
46 #define GCONF_VOLUME_CONTROL "/apps/osso/sound/master_volume"
47
48 #ifdef DIABLO
49 #    define MEDIA_SERVER_SRVC "com.nokia.osso_media_server"
50 #    define MEDIA_SERVER_PATH "/com/nokia/osso_media_server"
51 #    define MEDIA_SERVER_INTF "com.nokia.osso_media_server.music"
52 #else
53 #    define MEDIA_SERVER_SRVC "com.nokia.mafw.renderer.Mafw-Gst-Renderer-Plugin.gstrenderer"
54 #    define MEDIA_SERVER_PATH "/com/nokia/mafw/renderer/gstrenderer"
55 #    define MEDIA_SERVER_INTF "com.nokia.mafw.renderer"
56 #endif
57
58 #define PANUCCI_SRVC "org.panucci.panucciInterface"
59 #define PANUCCI_PATH "/panucciInterface"
60 #define PANUCCI_INTF "org.panucci.panucciInterface"
61
62 #define MPLAYER_FIFO "/etc/headphoned/mplayer-input"
63
64 enum { STATE_UNKNOWN, STATE_CONNECTED, STATE_DISCONNECTED, STATE_COUNT };
65
66 typedef struct {
67         GConfClient* client;
68         DBusConnection* session_bus;
69         osso_context_t* osso;
70         guint state;
71         gint volume[STATE_COUNT];
72         gboolean initial;
73 } Headphoned;
74
75 static
76 GMainLoop* loop = NULL;
77
78 static void
79 sig_handler (int sig G_GNUC_UNUSED)
80 {
81         if (loop && g_main_loop_is_running (loop)) {
82                 g_main_loop_quit (loop);
83         }
84 }
85
86 void
87 on_volume_changed(GConfClient* client, guint gnxn_id, GConfEntry* entry,
88                 gpointer data)
89 {
90         Headphoned* headphoned = (Headphoned*)data;
91         headphoned->volume[headphoned->state] =
92                 gconf_value_get_int(entry->value);
93 }
94
95 Headphoned*
96 headphoned_new()
97 {
98         Headphoned* this = g_new0(Headphoned, 1);
99
100         this->osso = osso_initialize("headphoned", "1.0", FALSE, NULL);
101         assert(this->osso != NULL);
102
103 #ifdef ENABLE_VOLUME_CONTROL
104         this->client = gconf_client_get_default();
105         gconf_client_add_dir(this->client, GCONF_VOLUME_CONTROL,
106                         GCONF_CLIENT_PRELOAD_NONE, NULL);
107         gconf_client_notify_add(this->client, GCONF_VOLUME_CONTROL,
108                         on_volume_changed, this, NULL, NULL);
109 #endif
110
111         this->session_bus = (DBusConnection*)osso_get_dbus_connection(this->osso);
112         this->initial = TRUE;
113
114         return this;
115 }
116
117 gboolean
118 on_file_changed(GIOChannel* source, GIOCondition condition, gpointer data)
119 {
120         Headphoned* headphoned = (Headphoned*)data;
121 #ifdef ENABLE_VOLUME_CONTROL
122         gint volume = headphoned->volume[headphoned->state];
123 #endif
124         gchar* result;
125         int mplayer_fifo;
126
127         g_io_channel_seek_position(source, 0, G_SEEK_SET, NULL);
128         g_io_channel_read_line(source, &result, NULL, NULL, NULL);
129         g_strstrip(result);
130         
131         if (g_ascii_strcasecmp(result, STATE_CONNECTED_STR) == 0) {
132                 headphoned->state = STATE_CONNECTED;
133         } else {
134                 headphoned->state = STATE_DISCONNECTED;
135 #ifdef ENABLE_PAUSE_ON_DISCONNECT
136                 if (headphoned->initial == FALSE) {
137                         /* Nokia Media Player */
138                         osso_rpc_run(headphoned->osso,
139                                         MEDIA_SERVER_SRVC,
140                                         MEDIA_SERVER_PATH,
141                                         MEDIA_SERVER_INTF,
142                                         "pause",
143                                         NULL,
144                                         DBUS_TYPE_INVALID);
145
146                         /* Panucci */
147                         if (dbus_bus_name_has_owner(headphoned->session_bus,
148                                                         PANUCCI_SRVC,
149                                                         NULL)) {
150                                 osso_rpc_run(headphoned->osso,
151                                                 PANUCCI_SRVC,
152                                                 PANUCCI_PATH,
153                                                 PANUCCI_INTF,
154                                                 "pause",
155                                                 NULL,
156                                                 DBUS_TYPE_INVALID);
157                         }
158
159                         /* MPlayer */
160                         if ((mplayer_fifo = open(MPLAYER_FIFO,
161                                                 O_WRONLY | O_NONBLOCK)) != -1) {
162                                 write(mplayer_fifo, "pause\n", 6);
163                                 close(mplayer_fifo);
164                         }
165                 }
166 #endif
167         }
168
169 #ifdef ENABLE_VOLUME_CONTROL
170         gint new_volume = headphoned->volume[headphoned->state];
171         if (new_volume != volume) {
172                 gconf_client_set_int(headphoned->client, GCONF_VOLUME_CONTROL,
173                                 new_volume, NULL);
174                 /*gconf_client_suggest_sync(headphoned->client, NULL);
175                 gconf_client_clear_cache(headphoned->client);*/
176         }
177 #endif
178         headphoned->initial = FALSE;
179
180         g_free(result);
181         return TRUE;
182 }
183
184 int
185 main(int argc, char* argv[])
186 {
187         Headphoned *headphoned;
188         g_type_init();
189
190         signal(SIGINT, sig_handler);
191         signal(SIGQUIT, sig_handler);
192         signal(SIGTERM, sig_handler);
193
194         loop = g_main_loop_new(NULL, FALSE);
195         headphoned = headphoned_new();
196
197         GIOChannel* state = g_io_channel_new_file(STATE_FILE, "r", NULL);
198         g_io_add_watch(state, G_IO_PRI, on_file_changed, headphoned);
199
200         g_main_loop_run(loop);
201
202 #ifdef ENABLE_VOLUME_CONTROL
203         if (headphoned->client) {
204                 gconf_client_remove_dir(headphoned->client, GCONF_VOLUME_CONTROL, NULL);
205                 g_object_unref(headphoned->client);
206         }
207 #endif
208
209         if (state) {
210                 g_io_channel_unref(state);
211         }
212
213         osso_deinitialize(headphoned->osso);
214         g_free(headphoned);
215         return 0;
216 }
217