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