]> code.delx.au - pulseaudio/blob - src/pulsecore/core-subscribe.c
Remove unnecessary #includes
[pulseaudio] / src / pulsecore / core-subscribe.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-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 <stdio.h>
27
28 #include <pulse/xmalloc.h>
29
30 #include <pulsecore/log.h>
31 #include <pulsecore/macro.h>
32
33 #include "core-subscribe.h"
34
35 /* The subscription subsystem may be used to be notified whenever an
36 * entity (sink, source, ...) is created or deleted. Modules may
37 * register a callback function that is called whenever an event
38 * matching a subscription mask happens. The execution of the callback
39 * function is postponed to the next main loop iteration, i.e. is not
40 * called from within the stack frame the entity was created in. */
41
42 struct pa_subscription {
43 pa_core *core;
44 pa_bool_t dead;
45
46 pa_subscription_cb_t callback;
47 void *userdata;
48 pa_subscription_mask_t mask;
49
50 PA_LLIST_FIELDS(pa_subscription);
51 };
52
53 struct pa_subscription_event {
54 pa_core *core;
55
56 pa_subscription_event_type_t type;
57 uint32_t index;
58
59 PA_LLIST_FIELDS(pa_subscription_event);
60 };
61
62 static void sched_event(pa_core *c);
63
64 /* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
65 pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_subscription_cb_t callback, void *userdata) {
66 pa_subscription *s;
67
68 pa_assert(c);
69 pa_assert(m);
70 pa_assert(callback);
71
72 s = pa_xnew(pa_subscription, 1);
73 s->core = c;
74 s->dead = FALSE;
75 s->callback = callback;
76 s->userdata = userdata;
77 s->mask = m;
78
79 PA_LLIST_PREPEND(pa_subscription, c->subscriptions, s);
80 return s;
81 }
82
83 /* Free a subscription object, effectively marking it for deletion */
84 void pa_subscription_free(pa_subscription*s) {
85 pa_assert(s);
86 pa_assert(!s->dead);
87
88 s->dead = TRUE;
89 sched_event(s->core);
90 }
91
92 static void free_subscription(pa_subscription *s) {
93 pa_assert(s);
94 pa_assert(s->core);
95
96 PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);
97 pa_xfree(s);
98 }
99
100 static void free_event(pa_subscription_event *s) {
101 pa_assert(s);
102 pa_assert(s->core);
103
104 if (!s->next)
105 s->core->subscription_event_last = s->prev;
106
107 PA_LLIST_REMOVE(pa_subscription_event, s->core->subscription_event_queue, s);
108 pa_xfree(s);
109 }
110
111 /* Free all subscription objects */
112 void pa_subscription_free_all(pa_core *c) {
113 pa_assert(c);
114
115 while (c->subscriptions)
116 free_subscription(c->subscriptions);
117
118 while (c->subscription_event_queue)
119 free_event(c->subscription_event_queue);
120
121 if (c->subscription_defer_event) {
122 c->mainloop->defer_free(c->subscription_defer_event);
123 c->subscription_defer_event = NULL;
124 }
125 }
126
127 #ifdef DEBUG
128 static void dump_event(const char * prefix, pa_subscription_event*e) {
129 const char * const fac_table[] = {
130 [PA_SUBSCRIPTION_EVENT_SINK] = "SINK",
131 [PA_SUBSCRIPTION_EVENT_SOURCE] = "SOURCE",
132 [PA_SUBSCRIPTION_EVENT_SINK_INPUT] = "SINK_INPUT",
133 [PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT] = "SOURCE_OUTPUT",
134 [PA_SUBSCRIPTION_EVENT_MODULE] = "MODULE",
135 [PA_SUBSCRIPTION_EVENT_CLIENT] = "CLIENT",
136 [PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE] = "SAMPLE_CACHE",
137 [PA_SUBSCRIPTION_EVENT_SERVER] = "SERVER",
138 [PA_SUBSCRIPTION_EVENT_AUTOLOAD] = "AUTOLOAD"
139 };
140
141 const char * const type_table[] = {
142 [PA_SUBSCRIPTION_EVENT_NEW] = "NEW",
143 [PA_SUBSCRIPTION_EVENT_CHANGE] = "CHANGE",
144 [PA_SUBSCRIPTION_EVENT_REMOVE] = "REMOVE"
145 };
146
147 pa_log_debug("%s event (%s|%s|%u)",
148 prefix,
149 fac_table[e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK],
150 type_table[e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK],
151 e->index);
152 }
153 #endif
154
155 /* Deferred callback for dispatching subscirption events */
156 static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
157 pa_core *c = userdata;
158 pa_subscription *s;
159
160 pa_assert(c->mainloop == m);
161 pa_assert(c);
162 pa_assert(c->subscription_defer_event == de);
163
164 c->mainloop->defer_enable(c->subscription_defer_event, 0);
165
166 /* Dispatch queued events */
167
168 while (c->subscription_event_queue) {
169 pa_subscription_event *e = c->subscription_event_queue;
170
171 for (s = c->subscriptions; s; s = s->next) {
172
173 if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
174 s->callback(c, e->type, e->index, s->userdata);
175 }
176
177 #ifdef DEBUG
178 dump_event("Dispatched", e);
179 #endif
180 free_event(e);
181 }
182
183 /* Remove dead subscriptions */
184
185 s = c->subscriptions;
186 while (s) {
187 pa_subscription *n = s->next;
188 if (s->dead)
189 free_subscription(s);
190 s = n;
191 }
192 }
193
194 /* Schedule an mainloop event so that a pending subscription event is dispatched */
195 static void sched_event(pa_core *c) {
196 pa_assert(c);
197
198 if (!c->subscription_defer_event) {
199 c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
200 pa_assert(c->subscription_defer_event);
201 }
202
203 c->mainloop->defer_enable(c->subscription_defer_event, 1);
204 }
205
206 /* Append a new subscription event to the subscription event queue and schedule a main loop event */
207 void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {
208 pa_subscription_event *e;
209 pa_assert(c);
210
211 /* No need for queuing subscriptions of noone is listening */
212 if (!c->subscriptions)
213 return;
214
215 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_NEW) {
216 pa_subscription_event *i, *n;
217
218 /* Check for duplicates */
219 for (i = c->subscription_event_last; i; i = n) {
220 n = i->prev;
221
222 /* not the same object type */
223 if (((t ^ i->type) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))
224 continue;
225
226 /* not the same object */
227 if (i->index != idx)
228 continue;
229
230 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
231 /* This object is being removed, hence there is no
232 * point in keeping the old events regarding this
233 * entry in the queue. */
234
235 free_event(i);
236 pa_log_debug("Dropped redundant event due to remove event.");
237 continue;
238 }
239
240 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
241 /* This object has changed. If a "new" or "change" event for
242 * this object is still in the queue we can exit. */
243
244 pa_log_debug("Dropped redundant event due to change event.");
245 return;
246 }
247 }
248 }
249
250 e = pa_xnew(pa_subscription_event, 1);
251 e->core = c;
252 e->type = t;
253 e->index = idx;
254
255 PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
256 c->subscription_event_last = e;
257
258 #ifdef DEBUG
259 dump_event("Queued", e);
260 #endif
261
262 sched_event(c);
263 }