debian: release version 0.5.1
[espeaktime] / src / daemon.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <time.h>
5 #include <glib.h>
6 #include <dbus/dbus-glib.h>
7 #include <dbus/dbus-glib-lowlevel.h>
8 #include <hal/libhal.h>
9 #include "config.h"
10
11 #include <mce/dbus-names.h>
12 #include <mce/mode-names.h>
13
14 #define HAL_COND_BUTTONPRESSED  "ButtonPressed"
15 #define HAL_BUTTON_POWER        "power"
16 #define HAL_INPUTDEV_PATH       "/org/freedesktop/Hal/devices/computer_logicaldev_input"
17
18 struct app_data {
19         DBusGConnection *bus;
20         LibHalContext *hal;
21         GConfClient *gconf;
22
23         gboolean mode_locked;
24         gboolean display_on;
25         time_t last_press;
26         unsigned press_count;
27 };
28
29 static void speak_time(struct app_data *app)
30 {
31         struct espeaktime_settings cfg;
32         cfg_read(app->gconf, &cfg);
33         cfg_speak(&cfg, FALSE);
34         cfg_free(&cfg);
35 }
36
37 static void sig_tklock_mode(DBusGProxy *proxy, const char *mode, gpointer user_data)
38 {
39         struct app_data *app = user_data;
40         g_debug("sig_tklock_mode [%s]", mode);
41         app->mode_locked = !strcmp(mode, MCE_TK_LOCKED);
42 }
43
44 static void sig_display_status(DBusGProxy *proxy, const char *status, gpointer user_data)
45 {
46         struct app_data *app = user_data;
47         g_debug("sig_display_status [%s]", status);
48         app->display_on = !strcmp(status, MCE_DISPLAY_ON_STRING);
49
50         /* Double-pressing the button at a normal rate will usually result
51          * in the two ButtonPress events being received before the display_status==on
52          * event.  Check here if that's the case.
53          */
54         if (app->mode_locked && app->display_on && app->press_count > 1 &&
55                         time(NULL) - app->last_press <= 1)
56                 speak_time(app);
57 }
58
59 static void debug_log(const gchar *log_domain,
60         GLogLevelFlags log_level, const gchar *message, gpointer unused_data)
61 {
62         g_print("# %s\n", message);
63 }
64
65 static void power_button(struct app_data *app)
66 {
67         time_t now;
68
69         time(&now);
70         if (now - app->last_press > 1)
71                 app->press_count = 0;
72         app->press_count++;
73         app->last_press = now;
74
75         g_debug("power button: count=%d", app->press_count);
76         if (app->mode_locked && app->display_on)
77                 speak_time(app);
78 }
79
80 /**
81  * Call a method with no input arguments and one string output argument.
82  * Return the allocaed result string on success, NULL on failure.
83  */
84 static char *mce_call_getstr(DBusGProxy *proxy, const char *method)
85 {
86         GError *err = NULL;
87         char *s = NULL;
88
89         if (dbus_g_proxy_call(proxy, method, &err,
90                         G_TYPE_INVALID,
91                         G_TYPE_STRING, &s, G_TYPE_INVALID))
92                 return s;
93
94         g_error("Couldn't call MCE (%s): %s\n", method, err->message);
95         g_error_free(err);
96         return NULL;
97 }
98
99 static gboolean prefill_status(struct app_data *app)
100 {
101         DBusGProxy *mce_req;
102         char *mode, *status;
103
104         mce_req = dbus_g_proxy_new_for_name(app->bus, MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF);
105         g_assert(mce_req);
106
107         mode = mce_call_getstr(mce_req, MCE_TKLOCK_MODE_GET);
108         status = mce_call_getstr(mce_req, MCE_DISPLAY_STATUS_GET);
109         if (mode)
110                 sig_tklock_mode(NULL, mode, app);
111         if (status)
112                 sig_display_status(NULL, status, app);
113         g_free(mode);
114         g_free(status);
115         g_object_unref(mce_req);
116
117         return mode && status;
118 }
119
120 static void connect_signals(struct app_data *app)
121 {
122         DBusGProxy *mce_sig;
123
124         mce_sig = dbus_g_proxy_new_for_name(app->bus, MCE_SERVICE, MCE_SIGNAL_PATH, MCE_SIGNAL_IF);
125         g_assert(mce_sig);
126         dbus_g_proxy_add_signal(mce_sig, MCE_TKLOCK_MODE_SIG, G_TYPE_STRING, G_TYPE_INVALID);
127         dbus_g_proxy_add_signal(mce_sig, MCE_DISPLAY_SIG, G_TYPE_STRING, G_TYPE_INVALID);
128         dbus_g_proxy_connect_signal(mce_sig, MCE_TKLOCK_MODE_SIG, G_CALLBACK(sig_tklock_mode), app, NULL);
129         dbus_g_proxy_connect_signal(mce_sig, MCE_DISPLAY_SIG, G_CALLBACK(sig_display_status), app, NULL);
130 }
131
132
133 static void device_condition(LibHalContext *ctx,
134         const char *udi,
135         const char *condition_name,
136         const char *condition_detail)
137 {
138         g_debug("device_condition: name [%s] detail [%s]",
139                 condition_name, condition_detail);
140         if (!strcmp(condition_name, HAL_COND_BUTTONPRESSED) && !strcmp(condition_detail, HAL_BUTTON_POWER)) {
141                 struct app_data *app = libhal_ctx_get_user_data(ctx);
142                 power_button(app);
143         }
144 }
145
146
147 static gboolean init_hal(struct app_data *app)
148 {
149         DBusError err;
150
151         app->hal = libhal_ctx_new();
152         if (!app->hal) {
153                 g_error("Couldn't get a HAL context");
154                 return FALSE;
155         }
156         libhal_ctx_set_dbus_connection(app->hal,
157                 dbus_g_connection_get_connection(app->bus));
158         libhal_ctx_set_device_condition(app->hal, device_condition);
159         libhal_ctx_set_user_data(app->hal, app);
160
161         dbus_error_init(&err);
162         if (!libhal_ctx_init(app->hal, &err)) {
163                 g_error("Couldn't initialize HAL context: %s", err.message);
164                 dbus_error_free(&err);
165                 libhal_ctx_free(app->hal);
166                 return FALSE;
167         }
168
169         if (!libhal_device_add_property_watch(app->hal, HAL_INPUTDEV_PATH, &err)) {
170                 g_error("Couldn't add HAL watch: %s", err.message);
171                 dbus_error_free(&err);
172                 libhal_ctx_free(app->hal);
173         }
174         return TRUE;
175 }
176
177 int main(int argc, char *argv[])
178 {
179         GMainLoop *loop;
180         GError *err = NULL;
181         struct app_data app;
182
183         if (argc > 1 && !strcmp(argv[1], "-v"))
184                 g_log_set_handler(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, debug_log, NULL);
185
186         g_debug("init");
187         memset(&app, 0, sizeof(app));
188         signal(SIGCHLD, SIG_IGN);
189
190         g_type_init();
191         loop = g_main_loop_new(NULL, FALSE);
192
193         app.bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &err);
194         if (!app.bus) {
195                 g_error("Couldn't get DBUS connection: %s\n", err->message);
196                 g_error_free(err);
197                 return 1;
198         }
199
200         app.gconf = gconf_client_get_default();
201         g_assert(app.gconf);
202
203         if (!init_hal(&app))
204                 return 1;
205
206         if (!prefill_status(&app))
207                 return 1;
208         connect_signals(&app);
209
210         g_debug("running");
211         g_main_loop_run(loop);
212         dbus_g_connection_unref(app.bus);
213         return 0;
214 }