First version for the N900 (headphoned 1.5)
[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 <assert.h>
33 #include <glib.h>
34 #include <gconf/gconf-client.h>
35 #include <libosso.h>
36 #include <dbus/dbus.h>
37 #include <fcntl.h>
38
39 #include "config.h"
40
41 #define STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state"
42 #define STATE_CONNECTED_STR "connected"
43 #define STATE_DISCONNECTED_STR "disconnected"
44
45 #define GCONF_VOLUME_CONTROL "/apps/osso/sound/master_volume"
46
47 #ifdef DIABLO
48 #    define MEDIA_SERVER_SRVC "com.nokia.osso_media_server"
49 #    define MEDIA_SERVER_PATH "/com/nokia/osso_media_server"
50 #    define MEDIA_SERVER_INTF "com.nokia.osso_media_server.music"
51 #else
52 #    define MEDIA_SERVER_SRVC "com.nokia.mafw.renderer.Mafw-Gst-Renderer-Plugin.gstrenderer"
53 #    define MEDIA_SERVER_PATH "/com/nokia/mafw/renderer/gstrenderer"
54 #    define MEDIA_SERVER_INTF "com.nokia.mafw.renderer"
55 #endif
56
57 #define PANUCCI_SRVC "org.panucci.panucciInterface"
58 #define PANUCCI_PATH "/panucciInterface"
59 #define PANUCCI_INTF "org.panucci.panucciInterface"
60
61 #define MPLAYER_FIFO "/etc/headphoned/mplayer-input"
62
63 enum { STATE_UNKNOWN, STATE_CONNECTED, STATE_DISCONNECTED, STATE_COUNT };
64
65 typedef struct {
66         GConfClient* client;
67         DBusConnection* session_bus;
68         osso_context_t* osso;
69         guint state;
70         gint volume[STATE_COUNT];
71         gboolean initial;
72 } Headphoned;
73
74 void
75 on_volume_changed(GConfClient* client, guint gnxn_id, GConfEntry* entry,
76                 gpointer data)
77 {
78         Headphoned* headphoned = (Headphoned*)data;
79         headphoned->volume[headphoned->state] =
80                 gconf_value_get_int(entry->value);
81 }
82
83 Headphoned*
84 headphoned_new()
85 {
86         Headphoned* this = g_new0(Headphoned, 1);
87         assert(this != NULL);
88
89         this->osso = osso_initialize("headphoned", "1.0", FALSE, NULL);
90         assert(this->osso != NULL);
91
92 #ifdef ENABLE_VOLUME_CONTROL
93         this->client = gconf_client_get_default();
94         gconf_client_add_dir(this->client, GCONF_VOLUME_CONTROL,
95                         GCONF_CLIENT_PRELOAD_NONE, NULL);
96         gconf_client_notify_add(this->client, GCONF_VOLUME_CONTROL,
97                         on_volume_changed, this, NULL, NULL);
98 #endif
99
100         this->session_bus = dbus_bus_get(DBUS_BUS_SESSION, NULL);
101         this->initial = TRUE;
102
103         return this;
104 }
105
106 gboolean
107 on_file_changed(GIOChannel* source, GIOCondition condition, gpointer data)
108 {
109         Headphoned* headphoned = (Headphoned*)data;
110 #ifdef ENABLE_VOLUME_CONTROL
111         gint volume = headphoned->volume[headphoned->state];
112 #endif
113         gchar* result;
114         int mplayer_fifo;
115
116         g_io_channel_seek_position(source, 0, G_SEEK_SET, NULL);
117         g_io_channel_read_line(source, &result, NULL, NULL, NULL);
118         g_strstrip(result);
119         
120         if (g_ascii_strcasecmp(result, STATE_CONNECTED_STR) == 0) {
121                 headphoned->state = STATE_CONNECTED;
122         } else {
123                 headphoned->state = STATE_DISCONNECTED;
124 #ifdef ENABLE_PAUSE_ON_DISCONNECT
125                 if (headphoned->initial == FALSE) {
126                         /* Nokia Media Player */
127                         osso_rpc_run(headphoned->osso,
128                                         MEDIA_SERVER_SRVC,
129                                         MEDIA_SERVER_PATH,
130                                         MEDIA_SERVER_INTF,
131                                         "pause",
132                                         NULL,
133                                         DBUS_TYPE_INVALID);
134
135                         /* Panucci */
136                         if (dbus_bus_name_has_owner(headphoned->session_bus,
137                                                         PANUCCI_SRVC,
138                                                         NULL)) {
139                                 osso_rpc_run(headphoned->osso,
140                                                 PANUCCI_SRVC,
141                                                 PANUCCI_PATH,
142                                                 PANUCCI_INTF,
143                                                 "pause",
144                                                 NULL,
145                                                 DBUS_TYPE_INVALID);
146                         }
147
148                         /* MPlayer */
149                         if ((mplayer_fifo = open(MPLAYER_FIFO,
150                                                 O_WRONLY | O_NONBLOCK)) != -1) {
151                                 write(mplayer_fifo, "pause\n", 6);
152                                 close(mplayer_fifo);
153                         }
154                 }
155 #endif
156         }
157
158 #ifdef ENABLE_VOLUME_CONTROL
159         gint new_volume = headphoned->volume[headphoned->state];
160         if (new_volume != volume) {
161                 gconf_client_set_int(headphoned->client, GCONF_VOLUME_CONTROL,
162                                 new_volume, NULL);
163                 /*gconf_client_suggest_sync(headphoned->client, NULL);
164                 gconf_client_clear_cache(headphoned->client);*/
165         }
166 #endif
167         headphoned->initial = FALSE;
168
169         g_free(result);
170         return TRUE;
171 }
172
173 int
174 main(int argc, char* argv[])
175 {
176         g_type_init();
177         GMainLoop* loop = g_main_loop_new(NULL, FALSE);
178
179         GIOChannel* state = g_io_channel_new_file(STATE_FILE, "r", NULL);
180         g_io_add_watch(state, G_IO_PRI, on_file_changed, headphoned_new());
181
182         g_main_loop_run(loop);
183         return 0;
184 }
185