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, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdio.h> 25 26#include <pulse/xmalloc.h> 27 28#include <pulsecore/llist.h> 29#include <pulsecore/log.h> 30#include <pulsecore/shared.h> 31#include <pulsecore/core-util.h> 32#include <pulsecore/macro.h> 33 34#include "x11wrap.h" 35 36#include <X11/Xlib.h> 37 38typedef struct pa_x11_internal pa_x11_internal; 39 40struct 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 47struct 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 pa_defer_event* cleanup_event; 57 58 PA_LLIST_HEAD(pa_x11_client, clients); 59 PA_LLIST_HEAD(pa_x11_internal, internals); 60}; 61 62struct pa_x11_client { 63 PA_LLIST_FIELDS(pa_x11_client); 64 pa_x11_wrapper *wrapper; 65 pa_x11_event_cb_t event_cb; 66 pa_x11_kill_cb_t kill_cb; 67 void *userdata; 68}; 69 70static void x11_wrapper_kill(pa_x11_wrapper *w); 71 72/* Dispatch all pending X11 events */ 73static void work(pa_x11_wrapper *w) { 74 pa_assert(w); 75 pa_assert(PA_REFCNT_VALUE(w) >= 1); 76 77 pa_x11_wrapper_ref(w); 78 79 while (XPending(w->display)) { 80 pa_x11_client *c, *n; 81 XEvent e; 82 XNextEvent(w->display, &e); 83 84 for (c = w->clients; c; c = n) { 85 n = c->next; 86 87 if (c->event_cb) 88 if (c->event_cb(w, &e, c->userdata) != 0) 89 break; 90 } 91 } 92 93 pa_x11_wrapper_unref(w); 94} 95 96/* IO notification event for the X11 display connection */ 97static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { 98 pa_x11_wrapper *w = userdata; 99 100 pa_assert(m); 101 pa_assert(e); 102 pa_assert(fd >= 0); 103 pa_assert(w); 104 pa_assert(PA_REFCNT_VALUE(w) >= 1); 105 106 work(w); 107} 108 109/* Deferred notification event. Called once each main loop iteration */ 110static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { 111 pa_x11_wrapper *w = userdata; 112 113 pa_assert(m); 114 pa_assert(e); 115 pa_assert(w); 116 pa_assert(PA_REFCNT_VALUE(w) >= 1); 117 118 m->defer_enable(e, 0); 119 120 work(w); 121} 122 123/* IO notification event for X11 internal connections */ 124static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { 125 pa_x11_wrapper *w = userdata; 126 127 pa_assert(m); 128 pa_assert(e); 129 pa_assert(fd >= 0); 130 pa_assert(w); 131 pa_assert(PA_REFCNT_VALUE(w) >= 1); 132 133 XProcessInternalConnection(w->display, fd); 134 135 work(w); 136} 137 138/* Add a new IO source for the specified X11 internal connection */ 139static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) { 140 pa_x11_internal *i; 141 pa_assert(fd >= 0); 142 143 i = pa_xnew(pa_x11_internal, 1); 144 i->wrapper = w; 145 i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w); 146 i->fd = fd; 147 148 PA_LLIST_PREPEND(pa_x11_internal, w->internals, i); 149 return i; 150} 151 152/* Remove an IO source for an X11 internal connection */ 153static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) { 154 pa_assert(i); 155 156 PA_LLIST_REMOVE(pa_x11_internal, w->internals, i); 157 w->core->mainloop->io_free(i->io_event); 158 pa_xfree(i); 159} 160 161/* Implementation of XConnectionWatchProc */ 162static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) { 163 pa_x11_wrapper *w = (pa_x11_wrapper*) userdata; 164 165 pa_assert(display); 166 pa_assert(w); 167 pa_assert(fd >= 0); 168 169 if (opening) 170 *watch_data = (XPointer) x11_internal_add(w, fd); 171 else 172 x11_internal_remove(w, (pa_x11_internal*) *watch_data); 173} 174 175static int x11_error_handler(Display* display, XErrorEvent* error_event) { 176 pa_log_warn("X11 error handler called"); 177 return 0; 178} 179 180static int x11_io_error_handler(Display* display) { 181 pa_log_warn("X11 I/O error handler called"); 182 return 0; 183} 184 185static void deferred_x11_teardown(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { 186 pa_x11_wrapper *w = userdata; 187 188 m->defer_enable(e, 0); 189 190 pa_log_debug("Start tearing down X11 modules after X11 I/O error"); 191 192 x11_wrapper_kill(w); 193 194 pa_log_debug("Done tearing down X11 modules after X11 I/O error"); 195} 196 197#ifdef HAVE_XSETIOERROREXITHANDLER 198static void x11_io_error_exit_handler(Display* display, void *userdata) { 199 pa_x11_wrapper *w = userdata; 200 201 pa_log_warn("X11 I/O error exit handler called, preparing to tear down X11 modules"); 202 203 pa_x11_wrapper_kill_deferred(w); 204} 205#endif 206 207static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) { 208 pa_x11_wrapper*w; 209 Display *d; 210 211 if (!(d = XOpenDisplay(name))) { 212 pa_log("XOpenDisplay() failed"); 213 return NULL; 214 } 215 216 w = pa_xnew(pa_x11_wrapper, 1); 217 PA_REFCNT_INIT(w); 218 w->core = c; 219 w->property_name = pa_xstrdup(t); 220 w->display = d; 221 222 PA_LLIST_HEAD_INIT(pa_x11_client, w->clients); 223 PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals); 224 225 w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w); 226 w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w); 227 w->cleanup_event = c->mainloop->defer_new(c->mainloop, deferred_x11_teardown, w); 228 w->core->mainloop->defer_enable(w->cleanup_event, 0); 229 230 XSetErrorHandler(x11_error_handler); 231 XSetIOErrorHandler(x11_io_error_handler); 232#ifdef HAVE_XSETIOERROREXITHANDLER 233 XSetIOErrorExitHandler(d, x11_io_error_exit_handler, w); 234#endif 235 XAddConnectionWatch(d, x11_watch, (XPointer) w); 236 237 pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0); 238 239 pa_log_debug("Created X11 connection wrapper '%s'", w->property_name); 240 241 return w; 242} 243 244static void x11_wrapper_free(pa_x11_wrapper*w) { 245 pa_assert(w); 246 247 pa_assert_se(pa_shared_remove(w->core, w->property_name) >= 0); 248 249 pa_assert(!w->clients); 250 251 pa_log_debug("Destroying X11 connection wrapper '%s'", w->property_name); 252 253 XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w); 254 XCloseDisplay(w->display); 255 256 w->core->mainloop->defer_free(w->cleanup_event); 257 w->core->mainloop->io_free(w->io_event); 258 w->core->mainloop->defer_free(w->defer_event); 259 260 while (w->internals) 261 x11_internal_remove(w, w->internals); 262 263 pa_xfree(w->property_name); 264 pa_xfree(w); 265} 266 267pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) { 268 char t[256]; 269 pa_x11_wrapper *w; 270 271 pa_core_assert_ref(c); 272 273 pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "@" : "", name ? name : ""); 274 275 if ((w = pa_shared_get(c, t))) 276 return pa_x11_wrapper_ref(w); 277 278 return x11_wrapper_new(c, name, t); 279} 280 281pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) { 282 pa_assert(w); 283 pa_assert(PA_REFCNT_VALUE(w) >= 1); 284 285 PA_REFCNT_INC(w); 286 return w; 287} 288 289void pa_x11_wrapper_unref(pa_x11_wrapper* w) { 290 pa_assert(w); 291 pa_assert(PA_REFCNT_VALUE(w) >= 1); 292 293 if (PA_REFCNT_DEC(w) > 0) 294 return; 295 296 x11_wrapper_free(w); 297} 298 299Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) { 300 pa_assert(w); 301 pa_assert(PA_REFCNT_VALUE(w) >= 1); 302 303 /* Somebody is using us, schedule a output buffer flush */ 304 w->core->mainloop->defer_enable(w->defer_event, 1); 305 306 return w->display; 307} 308 309xcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w) { 310 return XGetXCBConnection(pa_x11_wrapper_get_display(w)); 311} 312 313void pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w) { 314 pa_assert(w); 315 316 /* schedule X11 display teardown */ 317 w->core->mainloop->defer_enable(w->cleanup_event, 1); 318} 319 320/* Kill the connection to the X11 display */ 321static void x11_wrapper_kill(pa_x11_wrapper *w) { 322 pa_x11_client *c, *n; 323 324 pa_assert(w); 325 326 pa_x11_wrapper_ref(w); 327 328 for (c = w->clients; c; c = n) { 329 n = c->next; 330 331 if (c->kill_cb) 332 c->kill_cb(w, c->userdata); 333 } 334 335 pa_x11_wrapper_unref(w); 336} 337 338pa_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) { 339 pa_x11_client *c; 340 341 pa_assert(w); 342 pa_assert(PA_REFCNT_VALUE(w) >= 1); 343 344 c = pa_xnew(pa_x11_client, 1); 345 c->wrapper = w; 346 c->event_cb = event_cb; 347 c->kill_cb = kill_cb; 348 c->userdata = userdata; 349 350 PA_LLIST_PREPEND(pa_x11_client, w->clients, c); 351 352 return c; 353} 354 355void pa_x11_client_free(pa_x11_client *c) { 356 pa_assert(c); 357 pa_assert(c->wrapper); 358 pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1); 359 360 PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c); 361 pa_xfree(c); 362} 363