Some style fixes + documentation
[headphoned] / src / headphoned.c
1 /**
2  * headphoned for the Nokia N8x0
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  * Copyright (c) 2009-10-21 Thomas Perl <thpinfo.com>
13  **/
14
15 #include <stdio.h>
16 #include <assert.h>
17 #include <glib.h>
18 #include <gconf/gconf-client.h>
19 #include <libosso.h>
20
21 #define STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state"
22 #define STATE_CONNECTED_STR "connected"
23 #define STATE_DISCONNECTED_STR "disconnected"
24
25 #define GCONF_VOLUME_CONTROL "/apps/osso/sound/master_volume"
26
27 #define MEDIA_SERVER_SRVC "com.nokia.osso_media_server"
28 #define MEDIA_SERVER_PATH "/com/nokia/osso_media_server"
29 #define MEDIA_SERVER_INTF "com.nokia.osso_media_server.music"
30
31 // Volume control is currently broken, as something is messing
32 // with the controls from outside this process in GConf..
33 //#define ENABLE_VOLUME_CONTROL
34 #define ENABLE_PAUSE_ON_DISCONNECT
35
36
37 enum { STATE_UNKNOWN, STATE_CONNECTED, STATE_DISCONNECTED, STATE_COUNT };
38
39 typedef struct {
40         GConfClient* client;
41         osso_context_t* osso;
42         guint state;
43         gint volume[STATE_COUNT];
44         gboolean initial;
45 } Headphoned;
46
47 void
48 on_volume_changed(GConfClient* client, guint gnxn_id, GConfEntry* entry,
49                 gpointer data)
50 {
51         Headphoned* headphoned = (Headphoned*)data;
52         headphoned->volume[headphoned->state] =
53                 gconf_value_get_int(entry->value);
54 }
55
56 Headphoned*
57 headphoned_new()
58 {
59         Headphoned* this = g_new0(Headphoned, 1);
60         assert(this != NULL);
61
62         this->osso = osso_initialize("headphoned", "1.0", FALSE, NULL);
63         assert(this->osso != NULL);
64
65 #ifdef ENABLE_VOLUME_CONTROL
66         this->client = gconf_client_get_default();
67         gconf_client_add_dir(this->client, GCONF_VOLUME_CONTROL,
68                         GCONF_CLIENT_PRELOAD_NONE, NULL);
69         gconf_client_notify_add(this->client, GCONF_VOLUME_CONTROL,
70                         on_volume_changed, this, NULL, NULL);
71 #endif
72         this->initial = TRUE;
73
74         return this;
75 }
76
77 gboolean
78 on_file_changed(GIOChannel* source, GIOCondition condition, gpointer data)
79 {
80         Headphoned* headphoned = (Headphoned*)data;
81 #ifdef ENABLE_VOLUME_CONTROL
82         gint volume = headphoned->volume[headphoned->state];
83 #endif
84         gchar* result;
85
86         g_io_channel_seek_position(source, 0, G_SEEK_SET, NULL);
87         g_io_channel_read_line(source, &result, NULL, NULL, NULL);
88         g_strstrip(result);
89         
90         if (g_ascii_strcasecmp(result, STATE_CONNECTED_STR) == 0) {
91                 headphoned->state = STATE_CONNECTED;
92         } else {
93                 headphoned->state = STATE_DISCONNECTED;
94 #ifdef ENABLE_PAUSE_ON_DISCONNECT
95                 if (headphoned->initial == FALSE) {
96                         osso_rpc_run(headphoned->osso,
97                                         MEDIA_SERVER_SRVC,
98                                         MEDIA_SERVER_PATH,
99                                         MEDIA_SERVER_INTF,
100                                         "pause",
101                                         NULL,
102                                         DBUS_TYPE_INVALID);
103                 }
104 #endif
105         }
106
107 #ifdef ENABLE_VOLUME_CONTROL
108         gint new_volume = headphoned->volume[headphoned->state];
109         if (new_volume != volume) {
110                 gconf_client_set_int(headphoned->client, GCONF_VOLUME_CONTROL,
111                                 new_volume, NULL);
112                 /*gconf_client_suggest_sync(headphoned->client, NULL);
113                 gconf_client_clear_cache(headphoned->client);*/
114         }
115 #endif
116         headphoned->initial = FALSE;
117
118         g_free(result);
119         return TRUE;
120 }
121
122 int
123 main(int argc, char* argv[])
124 {
125         g_type_init();
126         GMainLoop* loop = g_main_loop_new(NULL, FALSE);
127
128         GIOChannel* state = g_io_channel_new_file(STATE_FILE, "r", NULL);
129         g_io_add_watch(state, G_IO_PRI, on_file_changed, headphoned_new());
130
131         g_main_loop_run(loop);
132         return 0;
133 }
134