Merge branch 'master' of ssh://rootserver/home/lennart/git/public/pulseaudio
[platform/upstream/pulseaudio.git] / src / pulsecore / x11wrap.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/llist.h>
31 #include <pulsecore/log.h>
32 #include <pulsecore/shared.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/macro.h>
35
36 #include "x11wrap.h"
37
38 typedef struct pa_x11_internal pa_x11_internal;
39
40 struct pa_x11_internal {
41     PA_LLIST_FIELDS(pa_x11_internal);
42     pa_x11_wrapper *wrapper;
43     pa_io_event* io_event;
44     int fd;
45 };
46
47 struct pa_x11_wrapper {
48     PA_REFCNT_DECLARE;
49     pa_core *core;
50
51     char *property_name;
52     Display *display;
53
54     pa_defer_event* defer_event;
55     pa_io_event* io_event;
56
57     PA_LLIST_HEAD(pa_x11_client, clients);
58     PA_LLIST_HEAD(pa_x11_internal, internals);
59 };
60
61 struct pa_x11_client {
62     PA_LLIST_FIELDS(pa_x11_client);
63     pa_x11_wrapper *wrapper;
64     pa_x11_event_cb_t event_cb;
65     pa_x11_kill_cb_t kill_cb;
66     void *userdata;
67 };
68
69 /* Dispatch all pending X11 events */
70 static void work(pa_x11_wrapper *w) {
71     pa_assert(w);
72     pa_assert(PA_REFCNT_VALUE(w) >= 1);
73
74     pa_x11_wrapper_ref(w);
75
76     while (XPending(w->display)) {
77         pa_x11_client *c, *n;
78         XEvent e;
79         XNextEvent(w->display, &e);
80
81         for (c = w->clients; c; c = n) {
82             n = c->next;
83
84             if (c->event_cb)
85                 if (c->event_cb(w, &e, c->userdata) != 0)
86                     break;
87         }
88     }
89
90     pa_x11_wrapper_unref(w);
91 }
92
93 /* IO notification event for the X11 display connection */
94 static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
95     pa_x11_wrapper *w = userdata;
96
97     pa_assert(m);
98     pa_assert(e);
99     pa_assert(fd >= 0);
100     pa_assert(w);
101     pa_assert(PA_REFCNT_VALUE(w) >= 1);
102
103     work(w);
104 }
105
106 /* Deferred notification event. Called once each main loop iteration */
107 static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) {
108     pa_x11_wrapper *w = userdata;
109
110     pa_assert(m);
111     pa_assert(e);
112     pa_assert(w);
113     pa_assert(PA_REFCNT_VALUE(w) >= 1);
114
115     m->defer_enable(e, 0);
116
117     work(w);
118 }
119
120 /* IO notification event for X11 internal connections */
121 static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
122     pa_x11_wrapper *w = userdata;
123
124     pa_assert(m);
125     pa_assert(e);
126     pa_assert(fd >= 0);
127     pa_assert(w);
128     pa_assert(PA_REFCNT_VALUE(w) >= 1);
129
130     XProcessInternalConnection(w->display, fd);
131
132     work(w);
133 }
134
135 /* Add a new IO source for the specified X11 internal connection */
136 static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) {
137     pa_x11_internal *i;
138     pa_assert(fd >= 0);
139
140     i = pa_xnew(pa_x11_internal, 1);
141     i->wrapper = w;
142     i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w);
143     i->fd = fd;
144
145     PA_LLIST_PREPEND(pa_x11_internal, w->internals, i);
146     return i;
147 }
148
149 /* Remove an IO source for an X11 internal connection */
150 static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) {
151     pa_assert(i);
152
153     PA_LLIST_REMOVE(pa_x11_internal, w->internals, i);
154     w->core->mainloop->io_free(i->io_event);
155     pa_xfree(i);
156 }
157
158 /* Implementation of XConnectionWatchProc */
159 static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) {
160     pa_x11_wrapper *w = (pa_x11_wrapper*) userdata;
161
162     pa_assert(display);
163     pa_assert(w);
164     pa_assert(fd >= 0);
165
166     if (opening)
167         *watch_data = (XPointer) x11_internal_add(w, fd);
168     else
169         x11_internal_remove(w, (pa_x11_internal*) *watch_data);
170 }
171
172 static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) {
173     pa_x11_wrapper*w;
174     Display *d;
175
176     if (!(d = XOpenDisplay(name))) {
177         pa_log("XOpenDisplay() failed");
178         return NULL;
179     }
180
181     w = pa_xnew(pa_x11_wrapper, 1);
182     PA_REFCNT_INIT(w);
183     w->core = c;
184     w->property_name = pa_xstrdup(t);
185     w->display = d;
186
187     PA_LLIST_HEAD_INIT(pa_x11_client, w->clients);
188     PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals);
189
190     w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w);
191     w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w);
192
193     XAddConnectionWatch(d, x11_watch, (XPointer) w);
194
195     pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0);
196
197     return w;
198 }
199
200 static void x11_wrapper_free(pa_x11_wrapper*w) {
201     pa_assert(w);
202
203     pa_assert_se(pa_shared_remove(w->core, w->property_name) >= 0);
204
205     pa_assert(!w->clients);
206
207     XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w);
208     XCloseDisplay(w->display);
209
210     w->core->mainloop->io_free(w->io_event);
211     w->core->mainloop->defer_free(w->defer_event);
212
213     while (w->internals)
214         x11_internal_remove(w, w->internals);
215
216     pa_xfree(w->property_name);
217     pa_xfree(w);
218 }
219
220 pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) {
221     char t[256];
222     pa_x11_wrapper *w;
223
224     pa_core_assert_ref(c);
225
226     pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "@" : "", name ? name : "");
227
228     if ((w = pa_shared_get(c, t)))
229         return pa_x11_wrapper_ref(w);
230
231     return x11_wrapper_new(c, name, t);
232 }
233
234 pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) {
235     pa_assert(w);
236     pa_assert(PA_REFCNT_VALUE(w) >= 1);
237
238     PA_REFCNT_INC(w);
239     return w;
240 }
241
242 void pa_x11_wrapper_unref(pa_x11_wrapper* w) {
243     pa_assert(w);
244     pa_assert(PA_REFCNT_VALUE(w) >= 1);
245
246     if (PA_REFCNT_DEC(w) > 0)
247         return;
248
249     x11_wrapper_free(w);
250 }
251
252 Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) {
253     pa_assert(w);
254     pa_assert(PA_REFCNT_VALUE(w) >= 1);
255
256     /* Somebody is using us, schedule a output buffer flush */
257     w->core->mainloop->defer_enable(w->defer_event, 1);
258
259     return w->display;
260 }
261
262 void pa_x11_wrapper_kill(pa_x11_wrapper *w) {
263     pa_x11_client *c, *n;
264
265     pa_assert(w);
266
267     pa_x11_wrapper_ref(w);
268
269     for (c = w->clients; c; c = n) {
270         n = c->next;
271
272         if (c->kill_cb)
273             c->kill_cb(w, c->userdata);
274     }
275
276     pa_x11_wrapper_unref(w);
277 }
278
279 pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, pa_x11_event_cb_t event_cb, pa_x11_kill_cb_t kill_cb, void *userdata) {
280     pa_x11_client *c;
281
282     pa_assert(w);
283     pa_assert(PA_REFCNT_VALUE(w) >= 1);
284
285     c = pa_xnew(pa_x11_client, 1);
286     c->wrapper = w;
287     c->event_cb = event_cb;
288     c->kill_cb = kill_cb;
289     c->userdata = userdata;
290
291     PA_LLIST_PREPEND(pa_x11_client, w->clients, c);
292
293     return c;
294 }
295
296 void pa_x11_client_free(pa_x11_client *c) {
297     pa_assert(c);
298     pa_assert(c->wrapper);
299     pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1);
300
301     PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c);
302     pa_xfree(c);
303 }