Branch data Line data Source code
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 : 0 : 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 [ # # ]: 0 : pa_assert(c);
69 [ # # ]: 0 : pa_assert(m);
70 [ # # ]: 0 : pa_assert(callback);
71 : :
72 : 0 : s = pa_xnew(pa_subscription, 1);
73 : 0 : s->core = c;
74 : 0 : s->dead = FALSE;
75 : 0 : s->callback = callback;
76 : 0 : s->userdata = userdata;
77 : 0 : s->mask = m;
78 : :
79 [ # # ][ # # ]: 0 : PA_LLIST_PREPEND(pa_subscription, c->subscriptions, s);
80 : 0 : return s;
81 : : }
82 : :
83 : : /* Free a subscription object, effectively marking it for deletion */
84 : 0 : void pa_subscription_free(pa_subscription*s) {
85 [ # # ]: 0 : pa_assert(s);
86 [ # # ]: 0 : pa_assert(!s->dead);
87 : :
88 : 0 : s->dead = TRUE;
89 : 0 : sched_event(s->core);
90 : 0 : }
91 : :
92 : 0 : static void free_subscription(pa_subscription *s) {
93 [ # # ]: 0 : pa_assert(s);
94 [ # # ]: 0 : pa_assert(s->core);
95 : :
96 [ # # ][ # # ]: 0 : PA_LLIST_REMOVE(pa_subscription, s->core->subscriptions, s);
[ # # ][ # # ]
97 : 0 : pa_xfree(s);
98 : 0 : }
99 : :
100 : 0 : static void free_event(pa_subscription_event *s) {
101 [ # # ]: 0 : pa_assert(s);
102 [ # # ]: 0 : pa_assert(s->core);
103 : :
104 [ # # ]: 0 : if (!s->next)
105 : 0 : s->core->subscription_event_last = s->prev;
106 : :
107 [ # # ][ # # ]: 0 : PA_LLIST_REMOVE(pa_subscription_event, s->core->subscription_event_queue, s);
[ # # ][ # # ]
108 : 0 : pa_xfree(s);
109 : 0 : }
110 : :
111 : : /* Free all subscription objects */
112 : 0 : void pa_subscription_free_all(pa_core *c) {
113 [ # # ]: 0 : pa_assert(c);
114 : :
115 [ # # ]: 0 : while (c->subscriptions)
116 : 0 : free_subscription(c->subscriptions);
117 : :
118 [ # # ]: 0 : while (c->subscription_event_queue)
119 : 0 : free_event(c->subscription_event_queue);
120 : :
121 [ # # ]: 0 : if (c->subscription_defer_event) {
122 : 0 : c->mainloop->defer_free(c->subscription_defer_event);
123 : 0 : c->subscription_defer_event = NULL;
124 : : }
125 : 0 : }
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 subscription events */
156 : 0 : static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
157 : 0 : pa_core *c = userdata;
158 : : pa_subscription *s;
159 : :
160 [ # # ]: 0 : pa_assert(c->mainloop == m);
161 [ # # ]: 0 : pa_assert(c);
162 [ # # ]: 0 : pa_assert(c->subscription_defer_event == de);
163 : :
164 : 0 : c->mainloop->defer_enable(c->subscription_defer_event, 0);
165 : :
166 : : /* Dispatch queued events */
167 : :
168 [ # # ]: 0 : while (c->subscription_event_queue) {
169 : 0 : pa_subscription_event *e = c->subscription_event_queue;
170 : :
171 [ # # ]: 0 : for (s = c->subscriptions; s; s = s->next) {
172 : :
173 [ # # ][ # # ]: 0 : if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
174 : 0 : s->callback(c, e->type, e->index, s->userdata);
175 : : }
176 : :
177 : : #ifdef DEBUG
178 : : dump_event("Dispatched", e);
179 : : #endif
180 : 0 : free_event(e);
181 : : }
182 : :
183 : : /* Remove dead subscriptions */
184 : :
185 : 0 : s = c->subscriptions;
186 [ # # ]: 0 : while (s) {
187 : 0 : pa_subscription *n = s->next;
188 [ # # ]: 0 : if (s->dead)
189 : 0 : free_subscription(s);
190 : 0 : s = n;
191 : : }
192 : 0 : }
193 : :
194 : : /* Schedule an mainloop event so that a pending subscription event is dispatched */
195 : 0 : static void sched_event(pa_core *c) {
196 [ # # ]: 0 : pa_assert(c);
197 : :
198 [ # # ]: 0 : if (!c->subscription_defer_event) {
199 : 0 : c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
200 [ # # ]: 0 : pa_assert(c->subscription_defer_event);
201 : : }
202 : :
203 : 0 : c->mainloop->defer_enable(c->subscription_defer_event, 1);
204 : 0 : }
205 : :
206 : : /* Append a new subscription event to the subscription event queue and schedule a main loop event */
207 : 0 : void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx) {
208 : : pa_subscription_event *e;
209 [ # # ]: 0 : pa_assert(c);
210 : :
211 : : /* No need for queuing subscriptions of no one is listening */
212 [ # # ]: 0 : if (!c->subscriptions)
213 : : return;
214 : :
215 [ # # ]: 0 : if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_NEW) {
216 : : pa_subscription_event *i, *n;
217 : :
218 : : /* Check for duplicates */
219 [ # # ]: 0 : for (i = c->subscription_event_last; i; i = n) {
220 : 0 : n = i->prev;
221 : :
222 : : /* not the same object type */
223 [ # # ]: 0 : if (((t ^ i->type) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))
224 : 0 : continue;
225 : :
226 : : /* not the same object */
227 [ # # ]: 0 : if (i->index != idx)
228 : 0 : continue;
229 : :
230 [ # # ]: 0 : 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 : 0 : free_event(i);
236 : 0 : pa_log_debug("Dropped redundant event due to remove event.");
237 : 0 : continue;
238 : : }
239 : :
240 [ # # ]: 0 : 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 : 0 : pa_log_debug("Dropped redundant event due to change event.");
245 : 0 : return;
246 : : }
247 : : }
248 : : }
249 : :
250 : 0 : e = pa_xnew(pa_subscription_event, 1);
251 : 0 : e->core = c;
252 : 0 : e->type = t;
253 : 0 : e->index = idx;
254 : :
255 [ # # ][ # # ]: 0 : PA_LLIST_INSERT_AFTER(pa_subscription_event, c->subscription_event_queue, c->subscription_event_last, e);
[ # # ][ # # ]
256 : 0 : c->subscription_event_last = e;
257 : :
258 : : #ifdef DEBUG
259 : : dump_event("Queued", e);
260 : : #endif
261 : :
262 : 0 : sched_event(c);
263 : : }
|