From 0368d6e22b9d7e6fec2474d158eaec16c90054f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 28 Apr 2009 02:29:01 +0200 Subject: build-system: move x11 and jack modules into subdirectories --- src/modules/jack/module-jack-sink.c | 497 ++++++++++++++++++++++++++++++ src/modules/jack/module-jack-source.c | 447 +++++++++++++++++++++++++++ src/modules/module-jack-sink.c | 497 ------------------------------ src/modules/module-jack-source.c | 447 --------------------------- src/modules/module-x11-bell.c | 190 ------------ src/modules/module-x11-cork-request.c | 189 ------------ src/modules/module-x11-publish.c | 245 --------------- src/modules/module-x11-xsmp.c | 254 --------------- src/modules/x11/module-x11-bell.c | 190 ++++++++++++ src/modules/x11/module-x11-cork-request.c | 189 ++++++++++++ src/modules/x11/module-x11-publish.c | 245 +++++++++++++++ src/modules/x11/module-x11-xsmp.c | 254 +++++++++++++++ 12 files changed, 1822 insertions(+), 1822 deletions(-) create mode 100644 src/modules/jack/module-jack-sink.c create mode 100644 src/modules/jack/module-jack-source.c delete mode 100644 src/modules/module-jack-sink.c delete mode 100644 src/modules/module-jack-source.c delete mode 100644 src/modules/module-x11-bell.c delete mode 100644 src/modules/module-x11-cork-request.c delete mode 100644 src/modules/module-x11-publish.c delete mode 100644 src/modules/module-x11-xsmp.c create mode 100644 src/modules/x11/module-x11-bell.c create mode 100644 src/modules/x11/module-x11-cork-request.c create mode 100644 src/modules/x11/module-x11-publish.c create mode 100644 src/modules/x11/module-x11-xsmp.c (limited to 'src/modules') diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c new file mode 100644 index 00000000..31b8a96e --- /dev/null +++ b/src/modules/jack/module-jack-sink.c @@ -0,0 +1,497 @@ +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-jack-sink-symdef.h" + +/* General overview: + * + * Because JACK has a very unflexible event loop management which + * doesn't allow us to add our own event sources to the event thread + * we cannot use the JACK real-time thread for dispatching our PA + * work. Instead, we run an additional RT thread which does most of + * the PA handling, and have the JACK RT thread request data from it + * via pa_asyncmsgq. The cost is an additional context switch which + * should hopefully not be that expensive if RT scheduling is + * enabled. A better fix would only be possible with additional event + * source support in JACK. + */ + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("JACK Sink"); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_USAGE( + "sink_name= " + "server_name= " + "client_name= " + "channels= " + "connect= " + "channel_map="); + +#define DEFAULT_SINK_NAME "jack_out" + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink; + + unsigned channels; + + jack_port_t* port[PA_CHANNELS_MAX]; + jack_client_t *client; + + void *buffer[PA_CHANNELS_MAX]; + + pa_thread_mq thread_mq; + pa_asyncmsgq *jack_msgq; + pa_rtpoll *rtpoll; + pa_rtpoll_item *rtpoll_item; + + pa_thread *thread; + + jack_nframes_t frames_in_buffer; + jack_nframes_t saved_frame_time; + pa_bool_t saved_frame_time_valid; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "server_name", + "client_name", + "channels", + "connect", + "channel_map", + NULL +}; + +enum { + SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX, + SINK_MESSAGE_BUFFER_SIZE, + SINK_MESSAGE_ON_SHUTDOWN +}; + +static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) { + struct userdata *u = PA_SINK(o)->userdata; + + switch (code) { + + case SINK_MESSAGE_RENDER: + + /* Handle the request from the JACK thread */ + + if (u->sink->thread_info.state == PA_SINK_RUNNING) { + pa_memchunk chunk; + size_t nbytes; + void *p; + + pa_assert(offset > 0); + nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec); + + pa_sink_render_full(u->sink, nbytes, &chunk); + + p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index; + pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset); + pa_memblock_release(chunk.memblock); + + pa_memblock_unref(chunk.memblock); + } else { + unsigned c; + pa_sample_spec ss; + + /* Humm, we're not RUNNING, hence let's write some silence */ + + ss = u->sink->sample_spec; + ss.channels = 1; + + for (c = 0; c < u->channels; c++) + pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss); + } + + u->frames_in_buffer = (jack_nframes_t) offset; + u->saved_frame_time = * (jack_nframes_t*) data; + u->saved_frame_time_valid = TRUE; + + return 0; + + case SINK_MESSAGE_BUFFER_SIZE: + pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec)); + return 0; + + case SINK_MESSAGE_ON_SHUTDOWN: + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + return 0; + + case PA_SINK_MESSAGE_GET_LATENCY: { + jack_nframes_t l, ft, d; + size_t n; + + /* This is the "worst-case" latency */ + l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer; + + if (u->saved_frame_time_valid) { + /* Adjust the worst case latency by the time that + * passed since we last handed data to JACK */ + + ft = jack_frame_time(u->client); + d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; + l = l > d ? l - d : 0; + } + + /* Convert it to usec */ + n = l * pa_frame_size(&u->sink->sample_spec); + *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); + + return 0; + } + + } + + return pa_sink_process_msg(o, code, data, offset, memchunk); +} + +static int jack_process(jack_nframes_t nframes, void *arg) { + struct userdata *u = arg; + unsigned c; + jack_nframes_t frame_time; + pa_assert(u); + + /* We just forward the request to our other RT thread */ + + for (c = 0; c < u->channels; c++) + pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes)); + + frame_time = jack_frame_time(u->client); + + pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0); + return 0; +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + if (u->core->realtime_scheduling) + pa_make_realtime(u->core->realtime_priority); + + pa_thread_mq_install(&u->thread_mq); + pa_rtpoll_install(u->rtpoll); + + for (;;) { + int ret; + + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) + if (u->sink->thread_info.rewind_requested) + pa_sink_process_rewind(u->sink, 0); + + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) + goto fail; + + if (ret == 0) + goto finish; + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +static void jack_error_func(const char*t) { + char *s; + + s = pa_xstrndup(t, strcspn(t, "\n\r")); + pa_log_warn("JACK error >%s<", s); + pa_xfree(s); +} + +static void jack_init(void *arg) { + struct userdata *u = arg; + + pa_log_info("JACK thread starting up."); + + if (u->core->realtime_scheduling) + pa_make_realtime(u->core->realtime_priority+4); +} + +static void jack_shutdown(void* arg) { + struct userdata *u = arg; + + pa_log_info("JACK thread shutting down."); + pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL); +} + +static int jack_buffer_size(jack_nframes_t nframes, void *arg) { + struct userdata *u = arg; + + pa_log_info("JACK buffer size changed."); + pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_BUFFER_SIZE, NULL, nframes, NULL, NULL); + return 0; +} + +int pa__init(pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma = NULL; + jack_status_t status; + const char *server_name, *client_name; + uint32_t channels = 0; + pa_bool_t do_connect = TRUE; + unsigned i; + const char **ports = NULL, **p; + pa_sink_new_data data; + + pa_assert(m); + + jack_set_error_function(jack_error_func); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { + pa_log("Failed to parse connect= argument."); + goto fail; + } + + server_name = pa_modargs_get_value(ma, "server_name", NULL); + client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink"); + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->saved_frame_time_valid = FALSE; + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + + /* The queue linking the JACK thread and our RT thread */ + u->jack_msgq = pa_asyncmsgq_new(0); + + /* The msgq from the JACK RT thread should have an even higher + * priority than the normal message queues, to match the guarantee + * all other drivers make: supplying the audio device with data is + * the top priority -- and as long as that is possible we don't do + * anything else */ + u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); + + if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { + pa_log("jack_client_open() failed."); + goto fail; + } + + ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); + + channels = 0; + for (p = ports; *p; p++) + channels++; + + if (!channels) + channels = m->core->default_sample_spec.channels; + + if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || + channels <= 0 || + channels > PA_CHANNELS_MAX) { + pa_log("Failed to parse channels= argument."); + goto fail; + } + + if (channels == m->core->default_channel_map.channels) + map = m->core->default_channel_map; + else + pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); + + if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { + pa_log("Failed to parse channel_map= argument."); + goto fail; + } + + pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); + + u->channels = ss.channels = (uint8_t) channels; + ss.rate = jack_get_sample_rate(u->client); + ss.format = PA_SAMPLE_FLOAT32NE; + + pa_assert(pa_sample_spec_valid(&ss)); + + for (i = 0; i < ss.channels; i++) { + if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) { + pa_log("jack_port_register() failed."); + goto fail; + } + } + + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); + if (server_name) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client)); + pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); + pa_sink_new_data_done(&data); + + if (!u->sink) { + pa_log("Failed to create sink."); + goto fail; + } + + u->sink->parent.process_msg = sink_process_msg; + u->sink->userdata = u; + + pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); + pa_sink_set_rtpoll(u->sink, u->rtpoll); + pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec)); + + jack_set_process_callback(u->client, jack_process, u); + jack_on_shutdown(u->client, jack_shutdown, u); + jack_set_thread_init_callback(u->client, jack_init, u); + jack_set_buffer_size_callback(u->client, jack_buffer_size, u); + + if (!(u->thread = pa_thread_new(thread_func, u))) { + pa_log("Failed to create thread."); + goto fail; + } + + if (jack_activate(u->client)) { + pa_log("jack_activate() failed"); + goto fail; + } + + if (do_connect) { + for (i = 0, p = ports; i < ss.channels; i++, p++) { + + if (!*p) { + pa_log("Not enough physical output ports, leaving unconnected."); + break; + } + + pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); + + if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) { + pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); + break; + } + } + } + + pa_sink_put(u->sink); + + free(ports); + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + free(ports); + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_sink_linked_by(u->sink); +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->client) + jack_client_close(u->client); + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->sink) + pa_sink_unref(u->sink); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + + if (u->jack_msgq) + pa_asyncmsgq_unref(u->jack_msgq); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + pa_xfree(u); +} diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c new file mode 100644 index 00000000..9a215c54 --- /dev/null +++ b/src/modules/jack/module-jack-source.c @@ -0,0 +1,447 @@ +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-jack-source-symdef.h" + +/* See module-jack-sink for a few comments how this module basically + * works */ + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("JACK Source"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE( + "source_name= " + "server_name= " + "client_name= " + "channels= " + "connect=" + "channel_map="); + +#define DEFAULT_SOURCE_NAME "jack_in" + +struct userdata { + pa_core *core; + pa_module *module; + pa_source *source; + + unsigned channels; + + jack_port_t* port[PA_CHANNELS_MAX]; + jack_client_t *client; + + pa_thread_mq thread_mq; + pa_asyncmsgq *jack_msgq; + pa_rtpoll *rtpoll; + pa_rtpoll_item *rtpoll_item; + + pa_thread *thread; + + jack_nframes_t saved_frame_time; + pa_bool_t saved_frame_time_valid; +}; + +static const char* const valid_modargs[] = { + "source_name", + "server_name", + "client_name", + "channels", + "connect", + "channel_map", + NULL +}; + +enum { + SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX, + SOURCE_MESSAGE_ON_SHUTDOWN +}; + +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { + struct userdata *u = PA_SOURCE(o)->userdata; + + switch (code) { + + case SOURCE_MESSAGE_POST: + + /* Handle the new block from the JACK thread */ + pa_assert(chunk); + pa_assert(chunk->length > 0); + + if (u->source->thread_info.state == PA_SOURCE_RUNNING) + pa_source_post(u->source, chunk); + + u->saved_frame_time = (jack_nframes_t) offset; + u->saved_frame_time_valid = TRUE; + + return 0; + + case SOURCE_MESSAGE_ON_SHUTDOWN: + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + return 0; + + case PA_SOURCE_MESSAGE_GET_LATENCY: { + jack_nframes_t l, ft, d; + size_t n; + + /* This is the "worst-case" latency */ + l = jack_port_get_total_latency(u->client, u->port[0]); + + if (u->saved_frame_time_valid) { + /* Adjust the worst case latency by the time that + * passed since we last handed data to JACK */ + + ft = jack_frame_time(u->client); + d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; + l += d; + } + + /* Convert it to usec */ + n = l * pa_frame_size(&u->source->sample_spec); + *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec); + + return 0; + } + } + + return pa_source_process_msg(o, code, data, offset, chunk); +} + +static int jack_process(jack_nframes_t nframes, void *arg) { + unsigned c; + struct userdata *u = arg; + const void *buffer[PA_CHANNELS_MAX]; + void *p; + jack_nframes_t frame_time; + pa_memchunk chunk; + + pa_assert(u); + + for (c = 0; c < u->channels; c++) + pa_assert_se(buffer[c] = jack_port_get_buffer(u->port[c], nframes)); + + /* We interleave the data and pass it on to the other RT thread */ + + pa_memchunk_reset(&chunk); + chunk.length = nframes * pa_frame_size(&u->source->sample_spec); + chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); + p = pa_memblock_acquire(chunk.memblock); + pa_interleave(buffer, u->channels, p, sizeof(float), nframes); + pa_memblock_release(chunk.memblock); + + frame_time = jack_frame_time(u->client); + + pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL); + + pa_memblock_unref(chunk.memblock); + + return 0; +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + if (u->core->realtime_scheduling) + pa_make_realtime(u->core->realtime_priority); + + pa_thread_mq_install(&u->thread_mq); + pa_rtpoll_install(u->rtpoll); + + for (;;) { + int ret; + + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) + goto fail; + + if (ret == 0) + goto finish; + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +static void jack_error_func(const char*t) { + char *s; + + s = pa_xstrndup(t, strcspn(t, "\n\r")); + pa_log_warn("JACK error >%s<", s); + pa_xfree(s); +} + +static void jack_init(void *arg) { + struct userdata *u = arg; + + pa_log_info("JACK thread starting up."); + + if (u->core->realtime_scheduling) + pa_make_realtime(u->core->realtime_priority+4); +} + +static void jack_shutdown(void* arg) { + struct userdata *u = arg; + + pa_log_info("JACK thread shutting down.."); + pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL); +} + +int pa__init(pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma = NULL; + jack_status_t status; + const char *server_name, *client_name; + uint32_t channels = 0; + pa_bool_t do_connect = TRUE; + unsigned i; + const char **ports = NULL, **p; + pa_source_new_data data; + + pa_assert(m); + + jack_set_error_function(jack_error_func); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { + pa_log("Failed to parse connect= argument."); + goto fail; + } + + server_name = pa_modargs_get_value(ma, "server_name", NULL); + client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source"); + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->saved_frame_time_valid = FALSE; + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + + u->jack_msgq = pa_asyncmsgq_new(0); + u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); + + if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { + pa_log("jack_client_open() failed."); + goto fail; + } + + ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); + + channels = 0; + for (p = ports; *p; p++) + channels++; + + if (!channels) + channels = m->core->default_sample_spec.channels; + + if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || + channels <= 0 || + channels >= PA_CHANNELS_MAX) { + pa_log("failed to parse channels= argument."); + goto fail; + } + + if (channels == m->core->default_channel_map.channels) + map = m->core->default_channel_map; + else + pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); + + if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { + pa_log("failed to parse channel_map= argument."); + goto fail; + } + + pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); + + u->channels = ss.channels = (uint8_t) channels; + ss.rate = jack_get_sample_rate(u->client); + ss.format = PA_SAMPLE_FLOAT32NE; + + pa_assert(pa_sample_spec_valid(&ss)); + + for (i = 0; i < ss.channels; i++) { + if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) { + pa_log("jack_port_register() failed."); + goto fail; + } + } + + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); + if (server_name) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client)); + pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); + + u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); + + if (!u->source) { + pa_log("Failed to create source."); + goto fail; + } + + u->source->parent.process_msg = source_process_msg; + u->source->userdata = u; + + pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); + pa_source_set_rtpoll(u->source, u->rtpoll); + + jack_set_process_callback(u->client, jack_process, u); + jack_on_shutdown(u->client, jack_shutdown, u); + jack_set_thread_init_callback(u->client, jack_init, u); + + if (!(u->thread = pa_thread_new(thread_func, u))) { + pa_log("Failed to create thread."); + goto fail; + } + + if (jack_activate(u->client)) { + pa_log("jack_activate() failed"); + goto fail; + } + + if (do_connect) { + for (i = 0, p = ports; i < ss.channels; i++, p++) { + + if (!*p) { + pa_log("Not enough physical output ports, leaving unconnected."); + break; + } + + pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); + + if (jack_connect(u->client, *p, jack_port_name(u->port[i]))) { + pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); + break; + } + } + + } + + pa_source_put(u->source); + + free(ports); + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + free(ports); + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_source_linked_by(u->source); +} + +void pa__done(pa_module*m) { + struct userdata *u; + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->client) + jack_client_close(u->client); + + if (u->source) + pa_source_unlink(u->source); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->source) + pa_source_unref(u->source); + + if (u->rtpoll_item) + pa_rtpoll_item_free(u->rtpoll_item); + + if (u->jack_msgq) + pa_asyncmsgq_unref(u->jack_msgq); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + pa_xfree(u); +} diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c deleted file mode 100644 index 31b8a96e..00000000 --- a/src/modules/module-jack-sink.c +++ /dev/null @@ -1,497 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2006 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module-jack-sink-symdef.h" - -/* General overview: - * - * Because JACK has a very unflexible event loop management which - * doesn't allow us to add our own event sources to the event thread - * we cannot use the JACK real-time thread for dispatching our PA - * work. Instead, we run an additional RT thread which does most of - * the PA handling, and have the JACK RT thread request data from it - * via pa_asyncmsgq. The cost is an additional context switch which - * should hopefully not be that expensive if RT scheduling is - * enabled. A better fix would only be possible with additional event - * source support in JACK. - */ - -PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("JACK Sink"); -PA_MODULE_LOAD_ONCE(TRUE); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_USAGE( - "sink_name= " - "server_name= " - "client_name= " - "channels= " - "connect= " - "channel_map="); - -#define DEFAULT_SINK_NAME "jack_out" - -struct userdata { - pa_core *core; - pa_module *module; - pa_sink *sink; - - unsigned channels; - - jack_port_t* port[PA_CHANNELS_MAX]; - jack_client_t *client; - - void *buffer[PA_CHANNELS_MAX]; - - pa_thread_mq thread_mq; - pa_asyncmsgq *jack_msgq; - pa_rtpoll *rtpoll; - pa_rtpoll_item *rtpoll_item; - - pa_thread *thread; - - jack_nframes_t frames_in_buffer; - jack_nframes_t saved_frame_time; - pa_bool_t saved_frame_time_valid; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "server_name", - "client_name", - "channels", - "connect", - "channel_map", - NULL -}; - -enum { - SINK_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX, - SINK_MESSAGE_BUFFER_SIZE, - SINK_MESSAGE_ON_SHUTDOWN -}; - -static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) { - struct userdata *u = PA_SINK(o)->userdata; - - switch (code) { - - case SINK_MESSAGE_RENDER: - - /* Handle the request from the JACK thread */ - - if (u->sink->thread_info.state == PA_SINK_RUNNING) { - pa_memchunk chunk; - size_t nbytes; - void *p; - - pa_assert(offset > 0); - nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec); - - pa_sink_render_full(u->sink, nbytes, &chunk); - - p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index; - pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset); - pa_memblock_release(chunk.memblock); - - pa_memblock_unref(chunk.memblock); - } else { - unsigned c; - pa_sample_spec ss; - - /* Humm, we're not RUNNING, hence let's write some silence */ - - ss = u->sink->sample_spec; - ss.channels = 1; - - for (c = 0; c < u->channels; c++) - pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss); - } - - u->frames_in_buffer = (jack_nframes_t) offset; - u->saved_frame_time = * (jack_nframes_t*) data; - u->saved_frame_time_valid = TRUE; - - return 0; - - case SINK_MESSAGE_BUFFER_SIZE: - pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec)); - return 0; - - case SINK_MESSAGE_ON_SHUTDOWN: - pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); - return 0; - - case PA_SINK_MESSAGE_GET_LATENCY: { - jack_nframes_t l, ft, d; - size_t n; - - /* This is the "worst-case" latency */ - l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer; - - if (u->saved_frame_time_valid) { - /* Adjust the worst case latency by the time that - * passed since we last handed data to JACK */ - - ft = jack_frame_time(u->client); - d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; - l = l > d ? l - d : 0; - } - - /* Convert it to usec */ - n = l * pa_frame_size(&u->sink->sample_spec); - *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); - - return 0; - } - - } - - return pa_sink_process_msg(o, code, data, offset, memchunk); -} - -static int jack_process(jack_nframes_t nframes, void *arg) { - struct userdata *u = arg; - unsigned c; - jack_nframes_t frame_time; - pa_assert(u); - - /* We just forward the request to our other RT thread */ - - for (c = 0; c < u->channels; c++) - pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes)); - - frame_time = jack_frame_time(u->client); - - pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0); - return 0; -} - -static void thread_func(void *userdata) { - struct userdata *u = userdata; - - pa_assert(u); - - pa_log_debug("Thread starting up"); - - if (u->core->realtime_scheduling) - pa_make_realtime(u->core->realtime_priority); - - pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); - - for (;;) { - int ret; - - if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) - if (u->sink->thread_info.rewind_requested) - pa_sink_process_rewind(u->sink, 0); - - if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) - goto fail; - - if (ret == 0) - goto finish; - } - -fail: - /* If this was no regular exit from the loop we have to continue - * processing messages until we received PA_MESSAGE_SHUTDOWN */ - pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); - pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); - -finish: - pa_log_debug("Thread shutting down"); -} - -static void jack_error_func(const char*t) { - char *s; - - s = pa_xstrndup(t, strcspn(t, "\n\r")); - pa_log_warn("JACK error >%s<", s); - pa_xfree(s); -} - -static void jack_init(void *arg) { - struct userdata *u = arg; - - pa_log_info("JACK thread starting up."); - - if (u->core->realtime_scheduling) - pa_make_realtime(u->core->realtime_priority+4); -} - -static void jack_shutdown(void* arg) { - struct userdata *u = arg; - - pa_log_info("JACK thread shutting down."); - pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL); -} - -static int jack_buffer_size(jack_nframes_t nframes, void *arg) { - struct userdata *u = arg; - - pa_log_info("JACK buffer size changed."); - pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_BUFFER_SIZE, NULL, nframes, NULL, NULL); - return 0; -} - -int pa__init(pa_module*m) { - struct userdata *u = NULL; - pa_sample_spec ss; - pa_channel_map map; - pa_modargs *ma = NULL; - jack_status_t status; - const char *server_name, *client_name; - uint32_t channels = 0; - pa_bool_t do_connect = TRUE; - unsigned i; - const char **ports = NULL, **p; - pa_sink_new_data data; - - pa_assert(m); - - jack_set_error_function(jack_error_func); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { - pa_log("Failed to parse connect= argument."); - goto fail; - } - - server_name = pa_modargs_get_value(ma, "server_name", NULL); - client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink"); - - m->userdata = u = pa_xnew0(struct userdata, 1); - u->core = m->core; - u->module = m; - u->saved_frame_time_valid = FALSE; - u->rtpoll = pa_rtpoll_new(); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - - /* The queue linking the JACK thread and our RT thread */ - u->jack_msgq = pa_asyncmsgq_new(0); - - /* The msgq from the JACK RT thread should have an even higher - * priority than the normal message queues, to match the guarantee - * all other drivers make: supplying the audio device with data is - * the top priority -- and as long as that is possible we don't do - * anything else */ - u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); - - if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { - pa_log("jack_client_open() failed."); - goto fail; - } - - ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); - - channels = 0; - for (p = ports; *p; p++) - channels++; - - if (!channels) - channels = m->core->default_sample_spec.channels; - - if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || - channels <= 0 || - channels > PA_CHANNELS_MAX) { - pa_log("Failed to parse channels= argument."); - goto fail; - } - - if (channels == m->core->default_channel_map.channels) - map = m->core->default_channel_map; - else - pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); - - if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { - pa_log("Failed to parse channel_map= argument."); - goto fail; - } - - pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); - - u->channels = ss.channels = (uint8_t) channels; - ss.rate = jack_get_sample_rate(u->client); - ss.format = PA_SAMPLE_FLOAT32NE; - - pa_assert(pa_sample_spec_valid(&ss)); - - for (i = 0; i < ss.channels; i++) { - if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) { - pa_log("jack_port_register() failed."); - goto fail; - } - } - - pa_sink_new_data_init(&data); - data.driver = __FILE__; - data.module = m; - pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); - pa_sink_new_data_set_sample_spec(&data, &ss); - pa_sink_new_data_set_channel_map(&data, &map); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); - if (server_name) - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); - pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client)); - pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); - - u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); - pa_sink_new_data_done(&data); - - if (!u->sink) { - pa_log("Failed to create sink."); - goto fail; - } - - u->sink->parent.process_msg = sink_process_msg; - u->sink->userdata = u; - - pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); - pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec)); - - jack_set_process_callback(u->client, jack_process, u); - jack_on_shutdown(u->client, jack_shutdown, u); - jack_set_thread_init_callback(u->client, jack_init, u); - jack_set_buffer_size_callback(u->client, jack_buffer_size, u); - - if (!(u->thread = pa_thread_new(thread_func, u))) { - pa_log("Failed to create thread."); - goto fail; - } - - if (jack_activate(u->client)) { - pa_log("jack_activate() failed"); - goto fail; - } - - if (do_connect) { - for (i = 0, p = ports; i < ss.channels; i++, p++) { - - if (!*p) { - pa_log("Not enough physical output ports, leaving unconnected."); - break; - } - - pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); - - if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) { - pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); - break; - } - } - } - - pa_sink_put(u->sink); - - free(ports); - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - free(ports); - - pa__done(m); - - return -1; -} - -int pa__get_n_used(pa_module *m) { - struct userdata *u; - - pa_assert(m); - pa_assert_se(u = m->userdata); - - return pa_sink_linked_by(u->sink); -} - -void pa__done(pa_module*m) { - struct userdata *u; - - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->client) - jack_client_close(u->client); - - if (u->sink) - pa_sink_unlink(u->sink); - - if (u->thread) { - pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); - pa_thread_free(u->thread); - } - - pa_thread_mq_done(&u->thread_mq); - - if (u->sink) - pa_sink_unref(u->sink); - - if (u->rtpoll_item) - pa_rtpoll_item_free(u->rtpoll_item); - - if (u->jack_msgq) - pa_asyncmsgq_unref(u->jack_msgq); - - if (u->rtpoll) - pa_rtpoll_free(u->rtpoll); - - pa_xfree(u); -} diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c deleted file mode 100644 index 9a215c54..00000000 --- a/src/modules/module-jack-source.c +++ /dev/null @@ -1,447 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2006 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module-jack-source-symdef.h" - -/* See module-jack-sink for a few comments how this module basically - * works */ - -PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("JACK Source"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(TRUE); -PA_MODULE_USAGE( - "source_name= " - "server_name= " - "client_name= " - "channels= " - "connect=" - "channel_map="); - -#define DEFAULT_SOURCE_NAME "jack_in" - -struct userdata { - pa_core *core; - pa_module *module; - pa_source *source; - - unsigned channels; - - jack_port_t* port[PA_CHANNELS_MAX]; - jack_client_t *client; - - pa_thread_mq thread_mq; - pa_asyncmsgq *jack_msgq; - pa_rtpoll *rtpoll; - pa_rtpoll_item *rtpoll_item; - - pa_thread *thread; - - jack_nframes_t saved_frame_time; - pa_bool_t saved_frame_time_valid; -}; - -static const char* const valid_modargs[] = { - "source_name", - "server_name", - "client_name", - "channels", - "connect", - "channel_map", - NULL -}; - -enum { - SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX, - SOURCE_MESSAGE_ON_SHUTDOWN -}; - -static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct userdata *u = PA_SOURCE(o)->userdata; - - switch (code) { - - case SOURCE_MESSAGE_POST: - - /* Handle the new block from the JACK thread */ - pa_assert(chunk); - pa_assert(chunk->length > 0); - - if (u->source->thread_info.state == PA_SOURCE_RUNNING) - pa_source_post(u->source, chunk); - - u->saved_frame_time = (jack_nframes_t) offset; - u->saved_frame_time_valid = TRUE; - - return 0; - - case SOURCE_MESSAGE_ON_SHUTDOWN: - pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); - return 0; - - case PA_SOURCE_MESSAGE_GET_LATENCY: { - jack_nframes_t l, ft, d; - size_t n; - - /* This is the "worst-case" latency */ - l = jack_port_get_total_latency(u->client, u->port[0]); - - if (u->saved_frame_time_valid) { - /* Adjust the worst case latency by the time that - * passed since we last handed data to JACK */ - - ft = jack_frame_time(u->client); - d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; - l += d; - } - - /* Convert it to usec */ - n = l * pa_frame_size(&u->source->sample_spec); - *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec); - - return 0; - } - } - - return pa_source_process_msg(o, code, data, offset, chunk); -} - -static int jack_process(jack_nframes_t nframes, void *arg) { - unsigned c; - struct userdata *u = arg; - const void *buffer[PA_CHANNELS_MAX]; - void *p; - jack_nframes_t frame_time; - pa_memchunk chunk; - - pa_assert(u); - - for (c = 0; c < u->channels; c++) - pa_assert_se(buffer[c] = jack_port_get_buffer(u->port[c], nframes)); - - /* We interleave the data and pass it on to the other RT thread */ - - pa_memchunk_reset(&chunk); - chunk.length = nframes * pa_frame_size(&u->source->sample_spec); - chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); - p = pa_memblock_acquire(chunk.memblock); - pa_interleave(buffer, u->channels, p, sizeof(float), nframes); - pa_memblock_release(chunk.memblock); - - frame_time = jack_frame_time(u->client); - - pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, NULL, frame_time, &chunk, NULL); - - pa_memblock_unref(chunk.memblock); - - return 0; -} - -static void thread_func(void *userdata) { - struct userdata *u = userdata; - - pa_assert(u); - - pa_log_debug("Thread starting up"); - - if (u->core->realtime_scheduling) - pa_make_realtime(u->core->realtime_priority); - - pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); - - for (;;) { - int ret; - - if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) - goto fail; - - if (ret == 0) - goto finish; - } - -fail: - /* If this was no regular exit from the loop we have to continue - * processing messages until we received PA_MESSAGE_SHUTDOWN */ - pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); - pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); - -finish: - pa_log_debug("Thread shutting down"); -} - -static void jack_error_func(const char*t) { - char *s; - - s = pa_xstrndup(t, strcspn(t, "\n\r")); - pa_log_warn("JACK error >%s<", s); - pa_xfree(s); -} - -static void jack_init(void *arg) { - struct userdata *u = arg; - - pa_log_info("JACK thread starting up."); - - if (u->core->realtime_scheduling) - pa_make_realtime(u->core->realtime_priority+4); -} - -static void jack_shutdown(void* arg) { - struct userdata *u = arg; - - pa_log_info("JACK thread shutting down.."); - pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL); -} - -int pa__init(pa_module*m) { - struct userdata *u = NULL; - pa_sample_spec ss; - pa_channel_map map; - pa_modargs *ma = NULL; - jack_status_t status; - const char *server_name, *client_name; - uint32_t channels = 0; - pa_bool_t do_connect = TRUE; - unsigned i; - const char **ports = NULL, **p; - pa_source_new_data data; - - pa_assert(m); - - jack_set_error_function(jack_error_func); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { - pa_log("Failed to parse connect= argument."); - goto fail; - } - - server_name = pa_modargs_get_value(ma, "server_name", NULL); - client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Source"); - - m->userdata = u = pa_xnew0(struct userdata, 1); - u->core = m->core; - u->module = m; - u->saved_frame_time_valid = FALSE; - u->rtpoll = pa_rtpoll_new(); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - - u->jack_msgq = pa_asyncmsgq_new(0); - u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); - - if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { - pa_log("jack_client_open() failed."); - goto fail; - } - - ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); - - channels = 0; - for (p = ports; *p; p++) - channels++; - - if (!channels) - channels = m->core->default_sample_spec.channels; - - if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || - channels <= 0 || - channels >= PA_CHANNELS_MAX) { - pa_log("failed to parse channels= argument."); - goto fail; - } - - if (channels == m->core->default_channel_map.channels) - map = m->core->default_channel_map; - else - pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); - - if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { - pa_log("failed to parse channel_map= argument."); - goto fail; - } - - pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); - - u->channels = ss.channels = (uint8_t) channels; - ss.rate = jack_get_sample_rate(u->client); - ss.format = PA_SAMPLE_FLOAT32NE; - - pa_assert(pa_sample_spec_valid(&ss)); - - for (i = 0; i < ss.channels; i++) { - if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0))) { - pa_log("jack_port_register() failed."); - goto fail; - } - } - - pa_source_new_data_init(&data); - data.driver = __FILE__; - data.module = m; - pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); - pa_source_new_data_set_sample_spec(&data, &ss); - pa_source_new_data_set_channel_map(&data, &map); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); - if (server_name) - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); - pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client)); - pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); - - u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY); - pa_source_new_data_done(&data); - - if (!u->source) { - pa_log("Failed to create source."); - goto fail; - } - - u->source->parent.process_msg = source_process_msg; - u->source->userdata = u; - - pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); - pa_source_set_rtpoll(u->source, u->rtpoll); - - jack_set_process_callback(u->client, jack_process, u); - jack_on_shutdown(u->client, jack_shutdown, u); - jack_set_thread_init_callback(u->client, jack_init, u); - - if (!(u->thread = pa_thread_new(thread_func, u))) { - pa_log("Failed to create thread."); - goto fail; - } - - if (jack_activate(u->client)) { - pa_log("jack_activate() failed"); - goto fail; - } - - if (do_connect) { - for (i = 0, p = ports; i < ss.channels; i++, p++) { - - if (!*p) { - pa_log("Not enough physical output ports, leaving unconnected."); - break; - } - - pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); - - if (jack_connect(u->client, *p, jack_port_name(u->port[i]))) { - pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); - break; - } - } - - } - - pa_source_put(u->source); - - free(ports); - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - free(ports); - - pa__done(m); - - return -1; -} - -int pa__get_n_used(pa_module *m) { - struct userdata *u; - - pa_assert(m); - pa_assert_se(u = m->userdata); - - return pa_source_linked_by(u->source); -} - -void pa__done(pa_module*m) { - struct userdata *u; - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->client) - jack_client_close(u->client); - - if (u->source) - pa_source_unlink(u->source); - - if (u->thread) { - pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); - pa_thread_free(u->thread); - } - - pa_thread_mq_done(&u->thread_mq); - - if (u->source) - pa_source_unref(u->source); - - if (u->rtpoll_item) - pa_rtpoll_item_free(u->rtpoll_item); - - if (u->jack_msgq) - pa_asyncmsgq_unref(u->jack_msgq); - - if (u->rtpoll) - pa_rtpoll_free(u->rtpoll); - - pa_xfree(u); -} diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c deleted file mode 100644 index ac303c3b..00000000 --- a/src/modules/module-x11-bell.c +++ /dev/null @@ -1,190 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "module-x11-bell-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("X11 bell interceptor"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(FALSE); -PA_MODULE_USAGE("sink= sample= display="); - -static const char* const valid_modargs[] = { - "sink", - "sample", - "display", - NULL -}; - -struct userdata { - pa_core *core; - pa_module *module; - - int xkb_event_base; - - char *sink_name; - char *scache_item; - - pa_x11_wrapper *x11_wrapper; - pa_x11_client *x11_client; -}; - -static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) { - XkbBellNotifyEvent *bne; - struct userdata *u = userdata; - - pa_assert(w); - pa_assert(e); - pa_assert(u); - pa_assert(u->x11_wrapper == w); - - if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify) - return 0; - - bne = (XkbBellNotifyEvent*) e; - - if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) { - pa_log_info("Ringing bell failed, reverting to X11 device bell."); - XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent); - } - - return 1; -} - -static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { - struct userdata *u = userdata; - - pa_assert(w); - pa_assert(u); - pa_assert(u->x11_wrapper == w); - - if (u->x11_client) - pa_x11_client_free(u->x11_client); - - if (u->x11_wrapper) - pa_x11_wrapper_unref(u->x11_wrapper); - - u->x11_client = NULL; - u->x11_wrapper = NULL; - - pa_module_unload_request(u->module, TRUE); -} - -int pa__init(pa_module*m) { - - struct userdata *u = NULL; - pa_modargs *ma = NULL; - int major, minor; - unsigned int auto_ctrls, auto_values; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments"); - goto fail; - } - - m->userdata = u = pa_xnew(struct userdata, 1); - u->core = m->core; - u->module = m; - u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system")); - u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); - u->x11_client = NULL; - - if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) - goto fail; - - major = XkbMajorVersion; - minor = XkbMinorVersion; - - if (!XkbLibraryVersion(&major, &minor)) { - pa_log("XkbLibraryVersion() failed"); - goto fail; - } - - major = XkbMajorVersion; - minor = XkbMinorVersion; - - if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) { - pa_log("XkbQueryExtension() failed"); - goto fail; - } - - XkbSelectEvents(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); - auto_ctrls = auto_values = XkbAudibleBellMask; - XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values); - XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0); - - u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -void pa__done(pa_module*m) { - struct userdata *u; - - pa_assert(m); - - if (!(u = m->userdata)) - return; - - pa_xfree(u->scache_item); - pa_xfree(u->sink_name); - - if (u->x11_client) - pa_x11_client_free(u->x11_client); - - if (u->x11_wrapper) - pa_x11_wrapper_unref(u->x11_wrapper); - - pa_xfree(u); -} diff --git a/src/modules/module-x11-cork-request.c b/src/modules/module-x11-cork-request.c deleted file mode 100644 index c1380c27..00000000 --- a/src/modules/module-x11-cork-request.c +++ /dev/null @@ -1,189 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include "module-x11-cork-request-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("Synthesize X11 media key events when cork/uncork is requested"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(FALSE); -PA_MODULE_USAGE("display="); - -static const char* const valid_modargs[] = { - "display", - NULL -}; - -struct userdata { - pa_module *module; - - pa_x11_wrapper *x11_wrapper; - pa_x11_client *x11_client; - - pa_hook_slot *hook_slot; -}; - -static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { - struct userdata *u = userdata; - - pa_assert(w); - pa_assert(u); - pa_assert(u->x11_wrapper == w); - - if (u->x11_client) { - pa_x11_client_free(u->x11_client); - u->x11_client = NULL; - } - - if (u->x11_wrapper) { - pa_x11_wrapper_unref(u->x11_wrapper); - u->x11_wrapper = NULL; - } - - pa_module_unload_request(u->module, TRUE); -} - -static pa_hook_result_t sink_input_send_event_hook_cb( - pa_core *c, - pa_sink_input_send_event_hook_data *data, - struct userdata *u) { - - KeySym sym; - KeyCode code; - Display *display; - - pa_assert(c); - pa_assert(data); - pa_assert(u); - - if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_CORK)) - sym = XF86XK_AudioPause; - else if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_UNCORK)) - sym = XF86XK_AudioPlay; - else - return PA_HOOK_OK; - - pa_log_debug("Triggering X11 keysym: %s", XKeysymToString(sym)); - - display = pa_x11_wrapper_get_display(u->x11_wrapper); - code = XKeysymToKeycode(display, sym); - - XTestFakeKeyEvent(display, code, True, CurrentTime); - XSync(display, False); - - XTestFakeKeyEvent(display, code, False, CurrentTime); - XSync(display, False); - - return PA_HOOK_OK; -} - -int pa__init(pa_module *m) { - struct userdata *u; - pa_modargs *ma; - int xtest_event_base, xtest_error_base; - int major_version, minor_version; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("failed to parse module arguments"); - goto fail; - } - - m->userdata = u = pa_xnew0(struct userdata, 1); - u->module = m; - - if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) - goto fail; - - if (!XTestQueryExtension( - pa_x11_wrapper_get_display(u->x11_wrapper), - &xtest_event_base, &xtest_error_base, - &major_version, &minor_version)) { - - pa_log("XTest extension not supported."); - goto fail; - } - - pa_log_debug("XTest %i.%i supported.", major_version, minor_version); - - u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u); - - u->hook_slot = pa_hook_connect( - &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], - PA_HOOK_NORMAL, - (pa_hook_cb_t) sink_input_send_event_hook_cb, u); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -void pa__done(pa_module*m) { - struct userdata*u; - - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->x11_client) - pa_x11_client_free(u->x11_client); - - if (u->x11_wrapper) - pa_x11_wrapper_unref(u->x11_wrapper); - - if (u->hook_slot) - pa_hook_slot_free(u->hook_slot); - - pa_xfree(u); -} diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c deleted file mode 100644 index 2c7fdc12..00000000 --- a/src/modules/module-x11-publish.c +++ /dev/null @@ -1,245 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module-x11-publish-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("X11 credential publisher"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(FALSE); -PA_MODULE_USAGE( - "display= " - "sink= " - "source= " - "cookie= "); - -static const char* const valid_modargs[] = { - "display", - "sink", - "source", - "cookie", - NULL -}; - -struct userdata { - pa_core *core; - pa_module *module; - pa_native_protocol *protocol; - - char *id; - pa_auth_cookie *auth_cookie; - - pa_x11_wrapper *x11_wrapper; - pa_x11_client *x11_client; - - pa_hook_slot *hook_slot; -}; - -static void publish_servers(struct userdata *u, pa_strlist *l) { - - if (l) { - char *s; - - l = pa_strlist_reverse(l); - s = pa_strlist_tostring(l); - l = pa_strlist_reverse(l); - - pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s); - pa_xfree(s); - } else - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER"); -} - -static pa_hook_result_t servers_changed_cb(void *hook_data, void *call_data, void *slot_data) { - pa_strlist *servers = call_data; - struct userdata *u = slot_data; - char t[256]; - - pa_assert(u); - - if (!pa_x11_get_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", t, sizeof(t)) || strcmp(t, u->id)) { - pa_log_warn("PulseAudio information vanished from X11!"); - return PA_HOOK_OK; - } - - publish_servers(u, servers); - return PA_HOOK_OK; -} - -static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { - struct userdata *u = userdata; - - pa_assert(w); - pa_assert(u); - pa_assert(u->x11_wrapper == w); - - if (u->x11_client) - pa_x11_client_free(u->x11_client); - - if (u->x11_wrapper) - pa_x11_wrapper_unref(u->x11_wrapper); - - u->x11_client = NULL; - u->x11_wrapper = NULL; - - pa_module_unload_request(u->module, TRUE); -} - -int pa__init(pa_module*m) { - struct userdata *u; - pa_modargs *ma = NULL; - char *mid, *sid; - char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; - const char *t; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("failed to parse module arguments"); - goto fail; - } - - m->userdata = u = pa_xnew(struct userdata, 1); - u->core = m->core; - u->module = m; - u->protocol = pa_native_protocol_get(m->core); - u->id = NULL; - u->auth_cookie = NULL; - u->x11_client = NULL; - u->x11_wrapper = NULL; - - u->hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_SERVERS_CHANGED], PA_HOOK_NORMAL, servers_changed_cb, u); - - if (!(u->auth_cookie = pa_auth_cookie_get(m->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), PA_NATIVE_COOKIE_LENGTH))) - goto fail; - - if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) - goto fail; - - mid = pa_machine_id(); - u->id = pa_sprintf_malloc("%lu@%s/%lu", (unsigned long) getuid(), mid, (unsigned long) getpid()); - pa_xfree(mid); - - pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", u->id); - - if ((sid = pa_session_id())) { - pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SESSION_ID", sid); - pa_xfree(sid); - } - - publish_servers(u, pa_native_protocol_servers(u->protocol)); - - if ((t = pa_modargs_get_value(ma, "source", NULL))) - pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SOURCE", t); - - if ((t = pa_modargs_get_value(ma, "sink", NULL))) - pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK", t); - - pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE", - pa_hexstr(pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH, hx, sizeof(hx))); - - u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -void pa__done(pa_module*m) { - struct userdata*u; - - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->x11_client) - pa_x11_client_free(u->x11_client); - - if (u->x11_wrapper) { - char t[256]; - - /* Yes, here is a race condition */ - if (!pa_x11_get_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", t, sizeof(t)) || strcmp(t, u->id)) - pa_log_warn("PulseAudio information vanished from X11!"); - else { - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID"); - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER"); - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK"); - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SOURCE"); - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE"); - pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SESSION_ID"); - XSync(pa_x11_wrapper_get_display(u->x11_wrapper), False); - } - - pa_x11_wrapper_unref(u->x11_wrapper); - } - - if (u->auth_cookie) - pa_auth_cookie_unref(u->auth_cookie); - - if (u->hook_slot) - pa_hook_slot_free(u->hook_slot); - - if (u->protocol) - pa_native_protocol_unref(u->protocol); - - pa_xfree(u->id); - pa_xfree(u); -} diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c deleted file mode 100644 index 28fd373a..00000000 --- a/src/modules/module-x11-xsmp.c +++ /dev/null @@ -1,254 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module-x11-xsmp-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering"); -PA_MODULE_DESCRIPTION("X11 session management"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(FALSE); -PA_MODULE_USAGE("session_manager= display="); - -static pa_bool_t ice_in_use = FALSE; - -static const char* const valid_modargs[] = { - "session_manager", - "display", - NULL -}; - -struct userdata { - pa_core *core; - pa_module *module; - pa_client *client; - SmcConn connection; - pa_x11_wrapper *x11; -}; - -static void die_cb(SmcConn connection, SmPointer client_data){ - struct userdata *u = client_data; - pa_assert(u); - - pa_log_debug("Got die message from XSMP."); - - pa_x11_wrapper_kill(u->x11); - - pa_x11_wrapper_unref(u->x11); - u->x11 = NULL; - - pa_module_unload_request(u->module, TRUE); -} - -static void save_complete_cb(SmcConn connection, SmPointer client_data) { -} - -static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) { - SmcSaveYourselfDone(connection, True); -} - -static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) { - SmcSaveYourselfDone(connection, True); -} - -static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { - IceConn connection = userdata; - - if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) { - IceSetShutdownNegotiation(connection, False); - IceCloseConnection(connection); - } -} - -static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) { - struct pa_core *c = client_data; - - if (opening) - *watch_data = c->mainloop->io_new( - c->mainloop, - IceConnectionNumber(connection), - PA_IO_EVENT_INPUT, - ice_io_cb, - connection); - else - c->mainloop->io_free(*watch_data); -} - -int pa__init(pa_module*m) { - - pa_modargs *ma = NULL; - char t[256], *vendor, *client_id; - SmcCallbacks callbacks; - SmProp prop_program, prop_user; - SmProp *prop_list[2]; - SmPropValue val_program, val_user; - struct userdata *u; - const char *e; - pa_client_new_data data; - - pa_assert(m); - - if (ice_in_use) { - pa_log("module-x11-xsmp may no be loaded twice."); - return -1; - } - - IceAddConnectionWatch(new_ice_connection, m->core); - ice_in_use = TRUE; - - m->userdata = u = pa_xnew(struct userdata, 1); - u->core = m->core; - u->module = m; - u->client = NULL; - u->connection = NULL; - u->x11 = NULL; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments"); - goto fail; - } - - if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) - goto fail; - - e = pa_modargs_get_value(ma, "session_manager", NULL); - - if (!e && !getenv("SESSION_MANAGER")) { - pa_log("X11 session manager not running."); - goto fail; - } - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.die.callback = die_cb; - callbacks.die.client_data = u; - callbacks.save_yourself.callback = save_yourself_cb; - callbacks.save_yourself.client_data = m->core; - callbacks.save_complete.callback = save_complete_cb; - callbacks.save_complete.client_data = m->core; - callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb; - callbacks.shutdown_cancelled.client_data = m->core; - - if (!(u->connection = SmcOpenConnection( - (char*) e, m->core, - SmProtoMajor, SmProtoMinor, - SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, - &callbacks, NULL, &client_id, - sizeof(t), t))) { - - pa_log("Failed to open connection to session manager: %s", t); - goto fail; - } - - prop_program.name = (char*) SmProgram; - prop_program.type = (char*) SmARRAY8; - val_program.value = (char*) PACKAGE_NAME; - val_program.length = (int) strlen(val_program.value); - prop_program.num_vals = 1; - prop_program.vals = &val_program; - prop_list[0] = &prop_program; - - prop_user.name = (char*) SmUserID; - prop_user.type = (char*) SmARRAY8; - pa_get_user_name(t, sizeof(t)); - val_user.value = t; - val_user.length = (int) strlen(val_user.value); - prop_user.num_vals = 1; - prop_user.vals = &val_user; - prop_list[1] = &prop_user; - - SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list); - - pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id); - - pa_client_new_data_init(&data); - data.module = m; - data.driver = __FILE__; - pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id); - pa_proplist_sets(data.proplist, "xsmp.vendor", vendor); - pa_proplist_sets(data.proplist, "xsmp.client.id", client_id); - u->client = pa_client_new(u->core, &data); - pa_client_new_data_done(&data); - - free(vendor); - free(client_id); - - if (!u->client) - goto fail; - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -void pa__done(pa_module*m) { - struct userdata *u; - - pa_assert(m); - - if ((u = m->userdata)) { - - if (u->connection) - SmcCloseConnection(u->connection, 0, NULL); - - if (u->client) - pa_client_free(u->client); - - if (u->x11) - pa_x11_wrapper_unref(u->x11); - - pa_xfree(u); - } - - if (ice_in_use) { - IceRemoveConnectionWatch(new_ice_connection, m->core); - ice_in_use = FALSE; - } -} diff --git a/src/modules/x11/module-x11-bell.c b/src/modules/x11/module-x11-bell.c new file mode 100644 index 00000000..ac303c3b --- /dev/null +++ b/src/modules/x11/module-x11-bell.c @@ -0,0 +1,190 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "module-x11-bell-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("X11 bell interceptor"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE("sink= sample= display="); + +static const char* const valid_modargs[] = { + "sink", + "sample", + "display", + NULL +}; + +struct userdata { + pa_core *core; + pa_module *module; + + int xkb_event_base; + + char *sink_name; + char *scache_item; + + pa_x11_wrapper *x11_wrapper; + pa_x11_client *x11_client; +}; + +static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) { + XkbBellNotifyEvent *bne; + struct userdata *u = userdata; + + pa_assert(w); + pa_assert(e); + pa_assert(u); + pa_assert(u->x11_wrapper == w); + + if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify) + return 0; + + bne = (XkbBellNotifyEvent*) e; + + if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) { + pa_log_info("Ringing bell failed, reverting to X11 device bell."); + XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent); + } + + return 1; +} + +static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { + struct userdata *u = userdata; + + pa_assert(w); + pa_assert(u); + pa_assert(u->x11_wrapper == w); + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + u->x11_client = NULL; + u->x11_wrapper = NULL; + + pa_module_unload_request(u->module, TRUE); +} + +int pa__init(pa_module*m) { + + struct userdata *u = NULL; + pa_modargs *ma = NULL; + int major, minor; + unsigned int auto_ctrls, auto_values; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->module = m; + u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "bell-window-system")); + u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + u->x11_client = NULL; + + if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + major = XkbMajorVersion; + minor = XkbMinorVersion; + + if (!XkbLibraryVersion(&major, &minor)) { + pa_log("XkbLibraryVersion() failed"); + goto fail; + } + + major = XkbMajorVersion; + minor = XkbMinorVersion; + + if (!XkbQueryExtension(pa_x11_wrapper_get_display(u->x11_wrapper), NULL, &u->xkb_event_base, NULL, &major, &minor)) { + pa_log("XkbQueryExtension() failed"); + goto fail; + } + + XkbSelectEvents(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); + auto_ctrls = auto_values = XkbAudibleBellMask; + XkbSetAutoResetControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbAudibleBellMask, &auto_ctrls, &auto_values); + XkbChangeEnabledControls(pa_x11_wrapper_get_display(u->x11_wrapper), XkbUseCoreKbd, XkbAudibleBellMask, 0); + + u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_cb, x11_kill_cb, u); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + pa_xfree(u->scache_item); + pa_xfree(u->sink_name); + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + pa_xfree(u); +} diff --git a/src/modules/x11/module-x11-cork-request.c b/src/modules/x11/module-x11-cork-request.c new file mode 100644 index 00000000..c1380c27 --- /dev/null +++ b/src/modules/x11/module-x11-cork-request.c @@ -0,0 +1,189 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "module-x11-cork-request-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Synthesize X11 media key events when cork/uncork is requested"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE("display="); + +static const char* const valid_modargs[] = { + "display", + NULL +}; + +struct userdata { + pa_module *module; + + pa_x11_wrapper *x11_wrapper; + pa_x11_client *x11_client; + + pa_hook_slot *hook_slot; +}; + +static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { + struct userdata *u = userdata; + + pa_assert(w); + pa_assert(u); + pa_assert(u->x11_wrapper == w); + + if (u->x11_client) { + pa_x11_client_free(u->x11_client); + u->x11_client = NULL; + } + + if (u->x11_wrapper) { + pa_x11_wrapper_unref(u->x11_wrapper); + u->x11_wrapper = NULL; + } + + pa_module_unload_request(u->module, TRUE); +} + +static pa_hook_result_t sink_input_send_event_hook_cb( + pa_core *c, + pa_sink_input_send_event_hook_data *data, + struct userdata *u) { + + KeySym sym; + KeyCode code; + Display *display; + + pa_assert(c); + pa_assert(data); + pa_assert(u); + + if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_CORK)) + sym = XF86XK_AudioPause; + else if (pa_streq(data->event, PA_STREAM_EVENT_REQUEST_UNCORK)) + sym = XF86XK_AudioPlay; + else + return PA_HOOK_OK; + + pa_log_debug("Triggering X11 keysym: %s", XKeysymToString(sym)); + + display = pa_x11_wrapper_get_display(u->x11_wrapper); + code = XKeysymToKeycode(display, sym); + + XTestFakeKeyEvent(display, code, True, CurrentTime); + XSync(display, False); + + XTestFakeKeyEvent(display, code, False, CurrentTime); + XSync(display, False); + + return PA_HOOK_OK; +} + +int pa__init(pa_module *m) { + struct userdata *u; + pa_modargs *ma; + int xtest_event_base, xtest_error_base; + int major_version, minor_version; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->module = m; + + if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + if (!XTestQueryExtension( + pa_x11_wrapper_get_display(u->x11_wrapper), + &xtest_event_base, &xtest_error_base, + &major_version, &minor_version)) { + + pa_log("XTest extension not supported."); + goto fail; + } + + pa_log_debug("XTest %i.%i supported.", major_version, minor_version); + + u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u); + + u->hook_slot = pa_hook_connect( + &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], + PA_HOOK_NORMAL, + (pa_hook_cb_t) sink_input_send_event_hook_cb, u); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata*u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + if (u->hook_slot) + pa_hook_slot_free(u->hook_slot); + + pa_xfree(u); +} diff --git a/src/modules/x11/module-x11-publish.c b/src/modules/x11/module-x11-publish.c new file mode 100644 index 00000000..2c7fdc12 --- /dev/null +++ b/src/modules/x11/module-x11-publish.c @@ -0,0 +1,245 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-x11-publish-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("X11 credential publisher"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( + "display= " + "sink= " + "source= " + "cookie= "); + +static const char* const valid_modargs[] = { + "display", + "sink", + "source", + "cookie", + NULL +}; + +struct userdata { + pa_core *core; + pa_module *module; + pa_native_protocol *protocol; + + char *id; + pa_auth_cookie *auth_cookie; + + pa_x11_wrapper *x11_wrapper; + pa_x11_client *x11_client; + + pa_hook_slot *hook_slot; +}; + +static void publish_servers(struct userdata *u, pa_strlist *l) { + + if (l) { + char *s; + + l = pa_strlist_reverse(l); + s = pa_strlist_tostring(l); + l = pa_strlist_reverse(l); + + pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER", s); + pa_xfree(s); + } else + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER"); +} + +static pa_hook_result_t servers_changed_cb(void *hook_data, void *call_data, void *slot_data) { + pa_strlist *servers = call_data; + struct userdata *u = slot_data; + char t[256]; + + pa_assert(u); + + if (!pa_x11_get_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", t, sizeof(t)) || strcmp(t, u->id)) { + pa_log_warn("PulseAudio information vanished from X11!"); + return PA_HOOK_OK; + } + + publish_servers(u, servers); + return PA_HOOK_OK; +} + +static void x11_kill_cb(pa_x11_wrapper *w, void *userdata) { + struct userdata *u = userdata; + + pa_assert(w); + pa_assert(u); + pa_assert(u->x11_wrapper == w); + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + u->x11_client = NULL; + u->x11_wrapper = NULL; + + pa_module_unload_request(u->module, TRUE); +} + +int pa__init(pa_module*m) { + struct userdata *u; + pa_modargs *ma = NULL; + char *mid, *sid; + char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; + const char *t; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->module = m; + u->protocol = pa_native_protocol_get(m->core); + u->id = NULL; + u->auth_cookie = NULL; + u->x11_client = NULL; + u->x11_wrapper = NULL; + + u->hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_SERVERS_CHANGED], PA_HOOK_NORMAL, servers_changed_cb, u); + + if (!(u->auth_cookie = pa_auth_cookie_get(m->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), PA_NATIVE_COOKIE_LENGTH))) + goto fail; + + if (!(u->x11_wrapper = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + mid = pa_machine_id(); + u->id = pa_sprintf_malloc("%lu@%s/%lu", (unsigned long) getuid(), mid, (unsigned long) getpid()); + pa_xfree(mid); + + pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", u->id); + + if ((sid = pa_session_id())) { + pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SESSION_ID", sid); + pa_xfree(sid); + } + + publish_servers(u, pa_native_protocol_servers(u->protocol)); + + if ((t = pa_modargs_get_value(ma, "source", NULL))) + pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SOURCE", t); + + if ((t = pa_modargs_get_value(ma, "sink", NULL))) + pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK", t); + + pa_x11_set_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE", + pa_hexstr(pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH, hx, sizeof(hx))); + + u->x11_client = pa_x11_client_new(u->x11_wrapper, NULL, x11_kill_cb, u); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata*u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) { + char t[256]; + + /* Yes, here is a race condition */ + if (!pa_x11_get_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID", t, sizeof(t)) || strcmp(t, u->id)) + pa_log_warn("PulseAudio information vanished from X11!"); + else { + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_ID"); + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SERVER"); + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SINK"); + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SOURCE"); + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_COOKIE"); + pa_x11_del_prop(pa_x11_wrapper_get_display(u->x11_wrapper), "PULSE_SESSION_ID"); + XSync(pa_x11_wrapper_get_display(u->x11_wrapper), False); + } + + pa_x11_wrapper_unref(u->x11_wrapper); + } + + if (u->auth_cookie) + pa_auth_cookie_unref(u->auth_cookie); + + if (u->hook_slot) + pa_hook_slot_free(u->hook_slot); + + if (u->protocol) + pa_native_protocol_unref(u->protocol); + + pa_xfree(u->id); + pa_xfree(u); +} diff --git a/src/modules/x11/module-x11-xsmp.c b/src/modules/x11/module-x11-xsmp.c new file mode 100644 index 00000000..28fd373a --- /dev/null +++ b/src/modules/x11/module-x11-xsmp.c @@ -0,0 +1,254 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-x11-xsmp-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("X11 session management"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE("session_manager= display="); + +static pa_bool_t ice_in_use = FALSE; + +static const char* const valid_modargs[] = { + "session_manager", + "display", + NULL +}; + +struct userdata { + pa_core *core; + pa_module *module; + pa_client *client; + SmcConn connection; + pa_x11_wrapper *x11; +}; + +static void die_cb(SmcConn connection, SmPointer client_data){ + struct userdata *u = client_data; + pa_assert(u); + + pa_log_debug("Got die message from XSMP."); + + pa_x11_wrapper_kill(u->x11); + + pa_x11_wrapper_unref(u->x11); + u->x11 = NULL; + + pa_module_unload_request(u->module, TRUE); +} + +static void save_complete_cb(SmcConn connection, SmPointer client_data) { +} + +static void shutdown_cancelled_cb(SmcConn connection, SmPointer client_data) { + SmcSaveYourselfDone(connection, True); +} + +static void save_yourself_cb(SmcConn connection, SmPointer client_data, int save_type, Bool _shutdown, int interact_style, Bool fast) { + SmcSaveYourselfDone(connection, True); +} + +static void ice_io_cb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { + IceConn connection = userdata; + + if (IceProcessMessages(connection, NULL, NULL) == IceProcessMessagesIOError) { + IceSetShutdownNegotiation(connection, False); + IceCloseConnection(connection); + } +} + +static void new_ice_connection(IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) { + struct pa_core *c = client_data; + + if (opening) + *watch_data = c->mainloop->io_new( + c->mainloop, + IceConnectionNumber(connection), + PA_IO_EVENT_INPUT, + ice_io_cb, + connection); + else + c->mainloop->io_free(*watch_data); +} + +int pa__init(pa_module*m) { + + pa_modargs *ma = NULL; + char t[256], *vendor, *client_id; + SmcCallbacks callbacks; + SmProp prop_program, prop_user; + SmProp *prop_list[2]; + SmPropValue val_program, val_user; + struct userdata *u; + const char *e; + pa_client_new_data data; + + pa_assert(m); + + if (ice_in_use) { + pa_log("module-x11-xsmp may no be loaded twice."); + return -1; + } + + IceAddConnectionWatch(new_ice_connection, m->core); + ice_in_use = TRUE; + + m->userdata = u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->module = m; + u->client = NULL; + u->connection = NULL; + u->x11 = NULL; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + if (!(u->x11 = pa_x11_wrapper_get(m->core, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + e = pa_modargs_get_value(ma, "session_manager", NULL); + + if (!e && !getenv("SESSION_MANAGER")) { + pa_log("X11 session manager not running."); + goto fail; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.die.callback = die_cb; + callbacks.die.client_data = u; + callbacks.save_yourself.callback = save_yourself_cb; + callbacks.save_yourself.client_data = m->core; + callbacks.save_complete.callback = save_complete_cb; + callbacks.save_complete.client_data = m->core; + callbacks.shutdown_cancelled.callback = shutdown_cancelled_cb; + callbacks.shutdown_cancelled.client_data = m->core; + + if (!(u->connection = SmcOpenConnection( + (char*) e, m->core, + SmProtoMajor, SmProtoMinor, + SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, + &callbacks, NULL, &client_id, + sizeof(t), t))) { + + pa_log("Failed to open connection to session manager: %s", t); + goto fail; + } + + prop_program.name = (char*) SmProgram; + prop_program.type = (char*) SmARRAY8; + val_program.value = (char*) PACKAGE_NAME; + val_program.length = (int) strlen(val_program.value); + prop_program.num_vals = 1; + prop_program.vals = &val_program; + prop_list[0] = &prop_program; + + prop_user.name = (char*) SmUserID; + prop_user.type = (char*) SmARRAY8; + pa_get_user_name(t, sizeof(t)); + val_user.value = t; + val_user.length = (int) strlen(val_user.value); + prop_user.num_vals = 1; + prop_user.vals = &val_user; + prop_list[1] = &prop_user; + + SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list); + + pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id); + + pa_client_new_data_init(&data); + data.module = m; + data.driver = __FILE__; + pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id); + pa_proplist_sets(data.proplist, "xsmp.vendor", vendor); + pa_proplist_sets(data.proplist, "xsmp.client.id", client_id); + u->client = pa_client_new(u->core, &data); + pa_client_new_data_done(&data); + + free(vendor); + free(client_id); + + if (!u->client) + goto fail; + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if ((u = m->userdata)) { + + if (u->connection) + SmcCloseConnection(u->connection, 0, NULL); + + if (u->client) + pa_client_free(u->client); + + if (u->x11) + pa_x11_wrapper_unref(u->x11); + + pa_xfree(u); + } + + if (ice_in_use) { + IceRemoveConnectionWatch(new_ice_connection, m->core); + ice_in_use = FALSE; + } +} -- cgit