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