153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdio.h> 2553a5a1b3Sopenharmony_ci 2653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <pulsecore/llist.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/shared.h> 3153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3253a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include "x11wrap.h" 3553a5a1b3Sopenharmony_ci 3653a5a1b3Sopenharmony_ci#include <X11/Xlib.h> 3753a5a1b3Sopenharmony_ci 3853a5a1b3Sopenharmony_citypedef struct pa_x11_internal pa_x11_internal; 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_cistruct pa_x11_internal { 4153a5a1b3Sopenharmony_ci PA_LLIST_FIELDS(pa_x11_internal); 4253a5a1b3Sopenharmony_ci pa_x11_wrapper *wrapper; 4353a5a1b3Sopenharmony_ci pa_io_event* io_event; 4453a5a1b3Sopenharmony_ci int fd; 4553a5a1b3Sopenharmony_ci}; 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_cistruct pa_x11_wrapper { 4853a5a1b3Sopenharmony_ci PA_REFCNT_DECLARE; 4953a5a1b3Sopenharmony_ci pa_core *core; 5053a5a1b3Sopenharmony_ci 5153a5a1b3Sopenharmony_ci char *property_name; 5253a5a1b3Sopenharmony_ci Display *display; 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ci pa_defer_event* defer_event; 5553a5a1b3Sopenharmony_ci pa_io_event* io_event; 5653a5a1b3Sopenharmony_ci pa_defer_event* cleanup_event; 5753a5a1b3Sopenharmony_ci 5853a5a1b3Sopenharmony_ci PA_LLIST_HEAD(pa_x11_client, clients); 5953a5a1b3Sopenharmony_ci PA_LLIST_HEAD(pa_x11_internal, internals); 6053a5a1b3Sopenharmony_ci}; 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_cistruct pa_x11_client { 6353a5a1b3Sopenharmony_ci PA_LLIST_FIELDS(pa_x11_client); 6453a5a1b3Sopenharmony_ci pa_x11_wrapper *wrapper; 6553a5a1b3Sopenharmony_ci pa_x11_event_cb_t event_cb; 6653a5a1b3Sopenharmony_ci pa_x11_kill_cb_t kill_cb; 6753a5a1b3Sopenharmony_ci void *userdata; 6853a5a1b3Sopenharmony_ci}; 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_cistatic void x11_wrapper_kill(pa_x11_wrapper *w); 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci/* Dispatch all pending X11 events */ 7353a5a1b3Sopenharmony_cistatic void work(pa_x11_wrapper *w) { 7453a5a1b3Sopenharmony_ci pa_assert(w); 7553a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_ci pa_x11_wrapper_ref(w); 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_ci while (XPending(w->display)) { 8053a5a1b3Sopenharmony_ci pa_x11_client *c, *n; 8153a5a1b3Sopenharmony_ci XEvent e; 8253a5a1b3Sopenharmony_ci XNextEvent(w->display, &e); 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_ci for (c = w->clients; c; c = n) { 8553a5a1b3Sopenharmony_ci n = c->next; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci if (c->event_cb) 8853a5a1b3Sopenharmony_ci if (c->event_cb(w, &e, c->userdata) != 0) 8953a5a1b3Sopenharmony_ci break; 9053a5a1b3Sopenharmony_ci } 9153a5a1b3Sopenharmony_ci } 9253a5a1b3Sopenharmony_ci 9353a5a1b3Sopenharmony_ci pa_x11_wrapper_unref(w); 9453a5a1b3Sopenharmony_ci} 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_ci/* IO notification event for the X11 display connection */ 9753a5a1b3Sopenharmony_cistatic void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { 9853a5a1b3Sopenharmony_ci pa_x11_wrapper *w = userdata; 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_ci pa_assert(m); 10153a5a1b3Sopenharmony_ci pa_assert(e); 10253a5a1b3Sopenharmony_ci pa_assert(fd >= 0); 10353a5a1b3Sopenharmony_ci pa_assert(w); 10453a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 10553a5a1b3Sopenharmony_ci 10653a5a1b3Sopenharmony_ci work(w); 10753a5a1b3Sopenharmony_ci} 10853a5a1b3Sopenharmony_ci 10953a5a1b3Sopenharmony_ci/* Deferred notification event. Called once each main loop iteration */ 11053a5a1b3Sopenharmony_cistatic void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { 11153a5a1b3Sopenharmony_ci pa_x11_wrapper *w = userdata; 11253a5a1b3Sopenharmony_ci 11353a5a1b3Sopenharmony_ci pa_assert(m); 11453a5a1b3Sopenharmony_ci pa_assert(e); 11553a5a1b3Sopenharmony_ci pa_assert(w); 11653a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 11753a5a1b3Sopenharmony_ci 11853a5a1b3Sopenharmony_ci m->defer_enable(e, 0); 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci work(w); 12153a5a1b3Sopenharmony_ci} 12253a5a1b3Sopenharmony_ci 12353a5a1b3Sopenharmony_ci/* IO notification event for X11 internal connections */ 12453a5a1b3Sopenharmony_cistatic void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { 12553a5a1b3Sopenharmony_ci pa_x11_wrapper *w = userdata; 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci pa_assert(m); 12853a5a1b3Sopenharmony_ci pa_assert(e); 12953a5a1b3Sopenharmony_ci pa_assert(fd >= 0); 13053a5a1b3Sopenharmony_ci pa_assert(w); 13153a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 13253a5a1b3Sopenharmony_ci 13353a5a1b3Sopenharmony_ci XProcessInternalConnection(w->display, fd); 13453a5a1b3Sopenharmony_ci 13553a5a1b3Sopenharmony_ci work(w); 13653a5a1b3Sopenharmony_ci} 13753a5a1b3Sopenharmony_ci 13853a5a1b3Sopenharmony_ci/* Add a new IO source for the specified X11 internal connection */ 13953a5a1b3Sopenharmony_cistatic pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) { 14053a5a1b3Sopenharmony_ci pa_x11_internal *i; 14153a5a1b3Sopenharmony_ci pa_assert(fd >= 0); 14253a5a1b3Sopenharmony_ci 14353a5a1b3Sopenharmony_ci i = pa_xnew(pa_x11_internal, 1); 14453a5a1b3Sopenharmony_ci i->wrapper = w; 14553a5a1b3Sopenharmony_ci i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w); 14653a5a1b3Sopenharmony_ci i->fd = fd; 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(pa_x11_internal, w->internals, i); 14953a5a1b3Sopenharmony_ci return i; 15053a5a1b3Sopenharmony_ci} 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_ci/* Remove an IO source for an X11 internal connection */ 15353a5a1b3Sopenharmony_cistatic void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) { 15453a5a1b3Sopenharmony_ci pa_assert(i); 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(pa_x11_internal, w->internals, i); 15753a5a1b3Sopenharmony_ci w->core->mainloop->io_free(i->io_event); 15853a5a1b3Sopenharmony_ci pa_xfree(i); 15953a5a1b3Sopenharmony_ci} 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci/* Implementation of XConnectionWatchProc */ 16253a5a1b3Sopenharmony_cistatic void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) { 16353a5a1b3Sopenharmony_ci pa_x11_wrapper *w = (pa_x11_wrapper*) userdata; 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci pa_assert(display); 16653a5a1b3Sopenharmony_ci pa_assert(w); 16753a5a1b3Sopenharmony_ci pa_assert(fd >= 0); 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci if (opening) 17053a5a1b3Sopenharmony_ci *watch_data = (XPointer) x11_internal_add(w, fd); 17153a5a1b3Sopenharmony_ci else 17253a5a1b3Sopenharmony_ci x11_internal_remove(w, (pa_x11_internal*) *watch_data); 17353a5a1b3Sopenharmony_ci} 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_cistatic int x11_error_handler(Display* display, XErrorEvent* error_event) { 17653a5a1b3Sopenharmony_ci pa_log_warn("X11 error handler called"); 17753a5a1b3Sopenharmony_ci return 0; 17853a5a1b3Sopenharmony_ci} 17953a5a1b3Sopenharmony_ci 18053a5a1b3Sopenharmony_cistatic int x11_io_error_handler(Display* display) { 18153a5a1b3Sopenharmony_ci pa_log_warn("X11 I/O error handler called"); 18253a5a1b3Sopenharmony_ci return 0; 18353a5a1b3Sopenharmony_ci} 18453a5a1b3Sopenharmony_ci 18553a5a1b3Sopenharmony_cistatic void deferred_x11_teardown(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { 18653a5a1b3Sopenharmony_ci pa_x11_wrapper *w = userdata; 18753a5a1b3Sopenharmony_ci 18853a5a1b3Sopenharmony_ci m->defer_enable(e, 0); 18953a5a1b3Sopenharmony_ci 19053a5a1b3Sopenharmony_ci pa_log_debug("Start tearing down X11 modules after X11 I/O error"); 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci x11_wrapper_kill(w); 19353a5a1b3Sopenharmony_ci 19453a5a1b3Sopenharmony_ci pa_log_debug("Done tearing down X11 modules after X11 I/O error"); 19553a5a1b3Sopenharmony_ci} 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_ci#ifdef HAVE_XSETIOERROREXITHANDLER 19853a5a1b3Sopenharmony_cistatic void x11_io_error_exit_handler(Display* display, void *userdata) { 19953a5a1b3Sopenharmony_ci pa_x11_wrapper *w = userdata; 20053a5a1b3Sopenharmony_ci 20153a5a1b3Sopenharmony_ci pa_log_warn("X11 I/O error exit handler called, preparing to tear down X11 modules"); 20253a5a1b3Sopenharmony_ci 20353a5a1b3Sopenharmony_ci pa_x11_wrapper_kill_deferred(w); 20453a5a1b3Sopenharmony_ci} 20553a5a1b3Sopenharmony_ci#endif 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_cistatic pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) { 20853a5a1b3Sopenharmony_ci pa_x11_wrapper*w; 20953a5a1b3Sopenharmony_ci Display *d; 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci if (!(d = XOpenDisplay(name))) { 21253a5a1b3Sopenharmony_ci pa_log("XOpenDisplay() failed"); 21353a5a1b3Sopenharmony_ci return NULL; 21453a5a1b3Sopenharmony_ci } 21553a5a1b3Sopenharmony_ci 21653a5a1b3Sopenharmony_ci w = pa_xnew(pa_x11_wrapper, 1); 21753a5a1b3Sopenharmony_ci PA_REFCNT_INIT(w); 21853a5a1b3Sopenharmony_ci w->core = c; 21953a5a1b3Sopenharmony_ci w->property_name = pa_xstrdup(t); 22053a5a1b3Sopenharmony_ci w->display = d; 22153a5a1b3Sopenharmony_ci 22253a5a1b3Sopenharmony_ci PA_LLIST_HEAD_INIT(pa_x11_client, w->clients); 22353a5a1b3Sopenharmony_ci PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals); 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w); 22653a5a1b3Sopenharmony_ci w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w); 22753a5a1b3Sopenharmony_ci w->cleanup_event = c->mainloop->defer_new(c->mainloop, deferred_x11_teardown, w); 22853a5a1b3Sopenharmony_ci w->core->mainloop->defer_enable(w->cleanup_event, 0); 22953a5a1b3Sopenharmony_ci 23053a5a1b3Sopenharmony_ci XSetErrorHandler(x11_error_handler); 23153a5a1b3Sopenharmony_ci XSetIOErrorHandler(x11_io_error_handler); 23253a5a1b3Sopenharmony_ci#ifdef HAVE_XSETIOERROREXITHANDLER 23353a5a1b3Sopenharmony_ci XSetIOErrorExitHandler(d, x11_io_error_exit_handler, w); 23453a5a1b3Sopenharmony_ci#endif 23553a5a1b3Sopenharmony_ci XAddConnectionWatch(d, x11_watch, (XPointer) w); 23653a5a1b3Sopenharmony_ci 23753a5a1b3Sopenharmony_ci pa_assert_se(pa_shared_set(c, w->property_name, w) >= 0); 23853a5a1b3Sopenharmony_ci 23953a5a1b3Sopenharmony_ci pa_log_debug("Created X11 connection wrapper '%s'", w->property_name); 24053a5a1b3Sopenharmony_ci 24153a5a1b3Sopenharmony_ci return w; 24253a5a1b3Sopenharmony_ci} 24353a5a1b3Sopenharmony_ci 24453a5a1b3Sopenharmony_cistatic void x11_wrapper_free(pa_x11_wrapper*w) { 24553a5a1b3Sopenharmony_ci pa_assert(w); 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci pa_assert_se(pa_shared_remove(w->core, w->property_name) >= 0); 24853a5a1b3Sopenharmony_ci 24953a5a1b3Sopenharmony_ci pa_assert(!w->clients); 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_ci pa_log_debug("Destroying X11 connection wrapper '%s'", w->property_name); 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w); 25453a5a1b3Sopenharmony_ci XCloseDisplay(w->display); 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci w->core->mainloop->defer_free(w->cleanup_event); 25753a5a1b3Sopenharmony_ci w->core->mainloop->io_free(w->io_event); 25853a5a1b3Sopenharmony_ci w->core->mainloop->defer_free(w->defer_event); 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci while (w->internals) 26153a5a1b3Sopenharmony_ci x11_internal_remove(w, w->internals); 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci pa_xfree(w->property_name); 26453a5a1b3Sopenharmony_ci pa_xfree(w); 26553a5a1b3Sopenharmony_ci} 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_cipa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) { 26853a5a1b3Sopenharmony_ci char t[256]; 26953a5a1b3Sopenharmony_ci pa_x11_wrapper *w; 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci pa_core_assert_ref(c); 27253a5a1b3Sopenharmony_ci 27353a5a1b3Sopenharmony_ci pa_snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "@" : "", name ? name : ""); 27453a5a1b3Sopenharmony_ci 27553a5a1b3Sopenharmony_ci if ((w = pa_shared_get(c, t))) 27653a5a1b3Sopenharmony_ci return pa_x11_wrapper_ref(w); 27753a5a1b3Sopenharmony_ci 27853a5a1b3Sopenharmony_ci return x11_wrapper_new(c, name, t); 27953a5a1b3Sopenharmony_ci} 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_cipa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) { 28253a5a1b3Sopenharmony_ci pa_assert(w); 28353a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci PA_REFCNT_INC(w); 28653a5a1b3Sopenharmony_ci return w; 28753a5a1b3Sopenharmony_ci} 28853a5a1b3Sopenharmony_ci 28953a5a1b3Sopenharmony_civoid pa_x11_wrapper_unref(pa_x11_wrapper* w) { 29053a5a1b3Sopenharmony_ci pa_assert(w); 29153a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 29253a5a1b3Sopenharmony_ci 29353a5a1b3Sopenharmony_ci if (PA_REFCNT_DEC(w) > 0) 29453a5a1b3Sopenharmony_ci return; 29553a5a1b3Sopenharmony_ci 29653a5a1b3Sopenharmony_ci x11_wrapper_free(w); 29753a5a1b3Sopenharmony_ci} 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ciDisplay *pa_x11_wrapper_get_display(pa_x11_wrapper *w) { 30053a5a1b3Sopenharmony_ci pa_assert(w); 30153a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 30253a5a1b3Sopenharmony_ci 30353a5a1b3Sopenharmony_ci /* Somebody is using us, schedule a output buffer flush */ 30453a5a1b3Sopenharmony_ci w->core->mainloop->defer_enable(w->defer_event, 1); 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_ci return w->display; 30753a5a1b3Sopenharmony_ci} 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_cixcb_connection_t *pa_x11_wrapper_get_xcb_connection(pa_x11_wrapper *w) { 31053a5a1b3Sopenharmony_ci return XGetXCBConnection(pa_x11_wrapper_get_display(w)); 31153a5a1b3Sopenharmony_ci} 31253a5a1b3Sopenharmony_ci 31353a5a1b3Sopenharmony_civoid pa_x11_wrapper_kill_deferred(pa_x11_wrapper *w) { 31453a5a1b3Sopenharmony_ci pa_assert(w); 31553a5a1b3Sopenharmony_ci 31653a5a1b3Sopenharmony_ci /* schedule X11 display teardown */ 31753a5a1b3Sopenharmony_ci w->core->mainloop->defer_enable(w->cleanup_event, 1); 31853a5a1b3Sopenharmony_ci} 31953a5a1b3Sopenharmony_ci 32053a5a1b3Sopenharmony_ci/* Kill the connection to the X11 display */ 32153a5a1b3Sopenharmony_cistatic void x11_wrapper_kill(pa_x11_wrapper *w) { 32253a5a1b3Sopenharmony_ci pa_x11_client *c, *n; 32353a5a1b3Sopenharmony_ci 32453a5a1b3Sopenharmony_ci pa_assert(w); 32553a5a1b3Sopenharmony_ci 32653a5a1b3Sopenharmony_ci pa_x11_wrapper_ref(w); 32753a5a1b3Sopenharmony_ci 32853a5a1b3Sopenharmony_ci for (c = w->clients; c; c = n) { 32953a5a1b3Sopenharmony_ci n = c->next; 33053a5a1b3Sopenharmony_ci 33153a5a1b3Sopenharmony_ci if (c->kill_cb) 33253a5a1b3Sopenharmony_ci c->kill_cb(w, c->userdata); 33353a5a1b3Sopenharmony_ci } 33453a5a1b3Sopenharmony_ci 33553a5a1b3Sopenharmony_ci pa_x11_wrapper_unref(w); 33653a5a1b3Sopenharmony_ci} 33753a5a1b3Sopenharmony_ci 33853a5a1b3Sopenharmony_cipa_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) { 33953a5a1b3Sopenharmony_ci pa_x11_client *c; 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci pa_assert(w); 34253a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(w) >= 1); 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci c = pa_xnew(pa_x11_client, 1); 34553a5a1b3Sopenharmony_ci c->wrapper = w; 34653a5a1b3Sopenharmony_ci c->event_cb = event_cb; 34753a5a1b3Sopenharmony_ci c->kill_cb = kill_cb; 34853a5a1b3Sopenharmony_ci c->userdata = userdata; 34953a5a1b3Sopenharmony_ci 35053a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(pa_x11_client, w->clients, c); 35153a5a1b3Sopenharmony_ci 35253a5a1b3Sopenharmony_ci return c; 35353a5a1b3Sopenharmony_ci} 35453a5a1b3Sopenharmony_ci 35553a5a1b3Sopenharmony_civoid pa_x11_client_free(pa_x11_client *c) { 35653a5a1b3Sopenharmony_ci pa_assert(c); 35753a5a1b3Sopenharmony_ci pa_assert(c->wrapper); 35853a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(c->wrapper) >= 1); 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c); 36153a5a1b3Sopenharmony_ci pa_xfree(c); 36253a5a1b3Sopenharmony_ci} 363