lirc: fix logic behind mute buttons
[profile/ivi/pulseaudio-panda.git] / src / modules / module-suspend-on-idle.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2006 Lennart Poettering
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulse/xmalloc.h>
27 #include <pulse/timeval.h>
28
29 #include <pulsecore/core.h>
30 #include <pulsecore/sink-input.h>
31 #include <pulsecore/source-output.h>
32 #include <pulsecore/modargs.h>
33 #include <pulsecore/log.h>
34 #include <pulsecore/namereg.h>
35
36 #include "module-suspend-on-idle-symdef.h"
37
38 PA_MODULE_AUTHOR("Lennart Poettering");
39 PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
40 PA_MODULE_VERSION(PACKAGE_VERSION);
41 PA_MODULE_LOAD_ONCE(TRUE);
42
43 static const char* const valid_modargs[] = {
44     "timeout",
45     NULL,
46 };
47
48 struct userdata {
49     pa_core *core;
50     pa_usec_t timeout;
51     pa_hashmap *device_infos;
52     pa_hook_slot
53         *sink_new_slot,
54         *source_new_slot,
55         *sink_unlink_slot,
56         *source_unlink_slot,
57         *sink_state_changed_slot,
58         *source_state_changed_slot;
59
60     pa_hook_slot
61         *sink_input_new_slot,
62         *source_output_new_slot,
63         *sink_input_unlink_slot,
64         *source_output_unlink_slot,
65         *sink_input_move_start_slot,
66         *source_output_move_start_slot,
67         *sink_input_move_finish_slot,
68         *source_output_move_finish_slot,
69         *sink_input_state_changed_slot,
70         *source_output_state_changed_slot;
71 };
72
73 struct device_info {
74     struct userdata *userdata;
75     pa_sink *sink;
76     pa_source *source;
77     struct timeval last_use;
78     pa_time_event *time_event;
79 };
80
81 static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
82     struct device_info *d = userdata;
83
84     pa_assert(d);
85
86     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
87
88     if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
89         pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
90         pa_sink_suspend(d->sink, TRUE);
91     }
92
93     if (d->source && pa_source_check_suspend(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
94         pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
95         pa_source_suspend(d->source, TRUE);
96     }
97 }
98
99 static void restart(struct device_info *d) {
100     struct timeval tv;
101     pa_assert(d);
102
103     pa_gettimeofday(&tv);
104     d->last_use = tv;
105     pa_timeval_add(&tv, d->userdata->timeout*1000000);
106     d->userdata->core->mainloop->time_restart(d->time_event, &tv);
107
108     if (d->sink)
109         pa_log_debug("Sink %s becomes idle.", d->sink->name);
110     if (d->source)
111         pa_log_debug("Source %s becomes idle.", d->source->name);
112 }
113
114 static void resume(struct device_info *d) {
115     pa_assert(d);
116
117     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
118
119     if (d->sink) {
120         pa_sink_suspend(d->sink, FALSE);
121
122         pa_log_debug("Sink %s becomes busy.", d->sink->name);
123     }
124
125     if (d->source) {
126         pa_source_suspend(d->source, FALSE);
127
128         pa_log_debug("Source %s becomes busy.", d->source->name);
129     }
130 }
131
132 static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
133     struct device_info *d;
134
135     pa_assert(c);
136     pa_assert(data);
137     pa_assert(u);
138
139     if ((d = pa_hashmap_get(u->device_infos, data->sink)))
140         resume(d);
141
142     return PA_HOOK_OK;
143 }
144
145 static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
146     struct device_info *d;
147
148     pa_assert(c);
149     pa_assert(data);
150     pa_assert(u);
151
152     if (data->source->monitor_of)
153         d = pa_hashmap_get(u->device_infos, data->source->monitor_of);
154     else
155         d = pa_hashmap_get(u->device_infos, data->source);
156
157     if (d)
158         resume(d);
159
160     return PA_HOOK_OK;
161 }
162
163 static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
164     pa_assert(c);
165     pa_sink_input_assert_ref(s);
166     pa_assert(u);
167
168     if (!s->sink)
169         return PA_HOOK_OK;
170
171     if (pa_sink_check_suspend(s->sink) <= 0) {
172         struct device_info *d;
173         if ((d = pa_hashmap_get(u->device_infos, s->sink)))
174             restart(d);
175     }
176
177     return PA_HOOK_OK;
178 }
179
180 static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
181     struct device_info *d = NULL;
182
183     pa_assert(c);
184     pa_source_output_assert_ref(s);
185     pa_assert(u);
186
187     if (!s->source)
188         return PA_HOOK_OK;
189
190     if (s->source->monitor_of) {
191         if (pa_sink_check_suspend(s->source->monitor_of) <= 0)
192             d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
193     } else {
194         if (pa_source_check_suspend(s->source) <= 0)
195             d = pa_hashmap_get(u->device_infos, s->source);
196     }
197
198     if (d)
199         restart(d);
200
201     return PA_HOOK_OK;
202 }
203
204 static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
205     struct device_info *d;
206
207     pa_assert(c);
208     pa_sink_input_assert_ref(s);
209     pa_assert(u);
210
211     if (pa_sink_check_suspend(s->sink) <= 1)
212         if ((d = pa_hashmap_get(u->device_infos, s->sink)))
213             restart(d);
214
215     return PA_HOOK_OK;
216 }
217
218 static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
219     struct device_info *d;
220
221     pa_assert(c);
222     pa_sink_input_assert_ref(s);
223     pa_assert(u);
224
225     if ((d = pa_hashmap_get(u->device_infos, s->sink)))
226         resume(d);
227
228     return PA_HOOK_OK;
229 }
230
231 static pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
232     struct device_info *d = NULL;
233
234     pa_assert(c);
235     pa_source_output_assert_ref(s);
236     pa_assert(u);
237
238     if (s->source->monitor_of) {
239         if (pa_sink_check_suspend(s->source->monitor_of) <= 1)
240             d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
241     } else {
242         if (pa_source_check_suspend(s->source) <= 1)
243             d = pa_hashmap_get(u->device_infos, s->source);
244     }
245
246     if (d)
247         restart(d);
248
249     return PA_HOOK_OK;
250 }
251
252 static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
253     struct device_info *d;
254
255     pa_assert(c);
256     pa_source_output_assert_ref(s);
257     pa_assert(u);
258
259     if (s->source->monitor_of)
260         d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
261     else
262         d = pa_hashmap_get(u->device_infos, s->source);
263
264     if (d)
265         resume(d);
266
267     return PA_HOOK_OK;
268 }
269
270 static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
271     struct device_info *d;
272     pa_sink_input_state_t state;
273     pa_assert(c);
274     pa_sink_input_assert_ref(s);
275     pa_assert(u);
276
277     state = pa_sink_input_get_state(s);
278     if (state == PA_SINK_INPUT_RUNNING || state == PA_SINK_INPUT_DRAINED)
279         if ((d = pa_hashmap_get(u->device_infos, s->sink)))
280             resume(d);
281
282     return PA_HOOK_OK;
283 }
284
285 static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
286     pa_source_output_state_t state;
287
288     pa_assert(c);
289     pa_source_output_assert_ref(s);
290     pa_assert(u);
291
292     state = pa_source_output_get_state(s);
293
294     if (state == PA_SOURCE_OUTPUT_RUNNING) {
295         struct device_info *d;
296
297         if (s->source->monitor_of)
298             d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
299         else
300             d = pa_hashmap_get(u->device_infos, s->source);
301
302         if (d)
303             resume(d);
304     }
305
306     return PA_HOOK_OK;
307 }
308
309 static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
310     struct device_info *d;
311     pa_source *source;
312     pa_sink *sink;
313
314     pa_assert(c);
315     pa_object_assert_ref(o);
316     pa_assert(u);
317
318     source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL;
319     sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL;
320
321     /* Never suspend monitors */
322     if (source && source->monitor_of)
323         return PA_HOOK_OK;
324
325     pa_assert(source || sink);
326
327     d = pa_xnew(struct device_info, 1);
328     d->userdata = u;
329     d->source = source ? pa_source_ref(source) : NULL;
330     d->sink = sink ? pa_sink_ref(sink) : NULL;
331     d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
332     pa_hashmap_put(u->device_infos, o, d);
333
334     if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) ||
335         (d->source && pa_source_check_suspend(d->source) <= 0))
336         restart(d);
337
338     return PA_HOOK_OK;
339 }
340
341 static void device_info_free(struct device_info *d) {
342     pa_assert(d);
343
344     if (d->source)
345         pa_source_unref(d->source);
346     if (d->sink)
347         pa_sink_unref(d->sink);
348
349     d->userdata->core->mainloop->time_free(d->time_event);
350
351     pa_xfree(d);
352 }
353
354 static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
355     struct device_info *d;
356
357     pa_assert(c);
358     pa_object_assert_ref(o);
359     pa_assert(u);
360
361     if ((d = pa_hashmap_remove(u->device_infos, o)))
362         device_info_free(d);
363
364     return PA_HOOK_OK;
365 }
366
367 static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
368     struct device_info *d;
369
370     pa_assert(c);
371     pa_object_assert_ref(o);
372     pa_assert(u);
373
374     if (!(d = pa_hashmap_get(u->device_infos, o)))
375         return PA_HOOK_OK;
376
377     if (pa_sink_isinstance(o)) {
378         pa_sink *s = PA_SINK(o);
379         pa_sink_state_t state = pa_sink_get_state(s);
380
381         if (pa_sink_check_suspend(s) <= 0) {
382
383             if (PA_SINK_IS_OPENED(state))
384                 restart(d);
385
386         }
387
388     } else if (pa_source_isinstance(o)) {
389         pa_source *s = PA_SOURCE(o);
390         pa_source_state_t state = pa_source_get_state(s);
391
392         if (pa_source_check_suspend(s) <= 0) {
393
394             if (PA_SOURCE_IS_OPENED(state))
395                 restart(d);
396         }
397     }
398
399     return PA_HOOK_OK;
400 }
401
402 int pa__init(pa_module*m) {
403     pa_modargs *ma = NULL;
404     struct userdata *u;
405     uint32_t timeout = 5;
406     uint32_t idx;
407     pa_sink *sink;
408     pa_source *source;
409
410     pa_assert(m);
411
412     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
413         pa_log("Failed to parse module arguments.");
414         goto fail;
415     }
416
417     if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) {
418         pa_log("Failed to parse timeout value.");
419         goto fail;
420     }
421
422     m->userdata = u = pa_xnew(struct userdata, 1);
423     u->core = m->core;
424     u->timeout = timeout;
425     u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
426
427     for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
428         device_new_hook_cb(m->core, PA_OBJECT(sink), u);
429
430     for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
431         device_new_hook_cb(m->core, PA_OBJECT(source), u);
432
433     u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
434     u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
435     u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
436     u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
437     u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
438     u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
439
440     u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
441     u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
442     u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
443     u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
444     u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u);
445     u->source_output_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u);
446     u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u);
447     u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u);
448     u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
449     u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
450
451     pa_modargs_free(ma);
452     return 0;
453
454 fail:
455
456     if (ma)
457         pa_modargs_free(ma);
458
459     return -1;
460 }
461
462 void pa__done(pa_module*m) {
463     struct userdata *u;
464     struct device_info *d;
465
466     pa_assert(m);
467
468     if (!m->userdata)
469         return;
470
471     u = m->userdata;
472
473     if (u->sink_new_slot)
474         pa_hook_slot_free(u->sink_new_slot);
475     if (u->sink_unlink_slot)
476         pa_hook_slot_free(u->sink_unlink_slot);
477     if (u->sink_state_changed_slot)
478         pa_hook_slot_free(u->sink_state_changed_slot);
479
480     if (u->source_new_slot)
481         pa_hook_slot_free(u->source_new_slot);
482     if (u->source_unlink_slot)
483         pa_hook_slot_free(u->source_unlink_slot);
484     if (u->source_state_changed_slot)
485         pa_hook_slot_free(u->source_state_changed_slot);
486
487     if (u->sink_input_new_slot)
488         pa_hook_slot_free(u->sink_input_new_slot);
489     if (u->sink_input_unlink_slot)
490         pa_hook_slot_free(u->sink_input_unlink_slot);
491     if (u->sink_input_move_start_slot)
492         pa_hook_slot_free(u->sink_input_move_start_slot);
493     if (u->sink_input_move_finish_slot)
494         pa_hook_slot_free(u->sink_input_move_finish_slot);
495     if (u->sink_input_state_changed_slot)
496         pa_hook_slot_free(u->sink_input_state_changed_slot);
497
498     if (u->source_output_new_slot)
499         pa_hook_slot_free(u->source_output_new_slot);
500     if (u->source_output_unlink_slot)
501         pa_hook_slot_free(u->source_output_unlink_slot);
502     if (u->source_output_move_start_slot)
503         pa_hook_slot_free(u->source_output_move_start_slot);
504     if (u->source_output_move_finish_slot)
505         pa_hook_slot_free(u->source_output_move_finish_slot);
506     if (u->source_output_state_changed_slot)
507         pa_hook_slot_free(u->source_output_state_changed_slot);
508
509     while ((d = pa_hashmap_steal_first(u->device_infos)))
510         device_info_free(d);
511
512     pa_hashmap_free(u->device_infos, NULL, NULL);
513
514     pa_xfree(u);
515 }