summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Willmann <d.willmann@samsung.com>2013-04-17 18:31:38 +0100
committerDaniel Willmann <d.willmann@samsung.com>2013-04-18 19:14:32 +0100
commit72556567385540aaf98cb9848fd5ecbff1bcad67 (patch)
tree106ca27e7cb53ba82a6d5ed9a3da44a345838669
parent01e4c9597e344ce4e34f7889883452ae0dd0a928 (diff)
downloadefl-72556567385540aaf98cb9848fd5ecbff1bcad67.tar.gz
ecore_audio: Add pulseaudio output for eo
Signed-off-by: Daniel Willmann <d.willmann@samsung.com>
-rw-r--r--src/Makefile_Ecore_Audio.am3
-rw-r--r--src/lib/ecore_audio/Ecore_Audio.h2
-rw-r--r--src/lib/ecore_audio/ecore_audio_obj_out_pulse.c271
-rw-r--r--src/lib/ecore_audio/ecore_audio_obj_out_pulse.h57
-rw-r--r--src/lib/ecore_audio/ecore_audio_private.h14
-rw-r--r--src/lib/ecore_audio/ecore_audio_pulse_ml.c308
6 files changed, 641 insertions, 14 deletions
diff --git a/src/Makefile_Ecore_Audio.am b/src/Makefile_Ecore_Audio.am
index 7e062702f2..ded4d35785 100644
--- a/src/Makefile_Ecore_Audio.am
+++ b/src/Makefile_Ecore_Audio.am
@@ -13,6 +13,7 @@ lib/ecore_audio/ecore_audio_obj_out.h \
lib/ecore_audio/ecore_audio_obj_in_sndfile.h \
lib/ecore_audio/ecore_audio_obj_out_sndfile.h \
lib/ecore_audio/ecore_audio_obj_in_tone.h \
+lib/ecore_audio/ecore_audio_obj_out_pulse.h \
lib/ecore_audio/ecore_audio_protected.h
@@ -24,6 +25,8 @@ lib/ecore_audio/ecore_audio_obj_out.c \
lib/ecore_audio/ecore_audio_obj_in_sndfile.c \
lib/ecore_audio/ecore_audio_obj_out_sndfile.c \
lib/ecore_audio/ecore_audio_obj_in_tone.c \
+lib/ecore_audio/ecore_audio_pulse_ml.c \
+lib/ecore_audio/ecore_audio_obj_out_pulse.c \
lib/ecore_audio/ecore_audio_private.h
lib_ecore_audio_libecore_audio_la_CPPFLAGS = @ECORE_AUDIO_CFLAGS@
diff --git a/src/lib/ecore_audio/Ecore_Audio.h b/src/lib/ecore_audio/Ecore_Audio.h
index 8a542176e1..b87f6479cc 100644
--- a/src/lib/ecore_audio/Ecore_Audio.h
+++ b/src/lib/ecore_audio/Ecore_Audio.h
@@ -576,4 +576,6 @@ EAPI void ecore_audio_input_callback_setup(Ecore_Audio_Object *in
#include <ecore_audio_obj_in_tone.h>
+#include <ecore_audio_obj_out_pulse.h>
+
#endif
diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
new file mode 100644
index 0000000000..4a3f9f86b5
--- /dev/null
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
@@ -0,0 +1,271 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <Eo.h>
+#include "ecore_audio_private.h"
+#include <pulse/pulseaudio.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+extern pa_mainloop_api functable;
+
+EAPI Eo_Op ECORE_AUDIO_OBJ_OUT_PULSE_BASE_ID = EO_NOOP;
+
+#define MY_CLASS ECORE_AUDIO_OBJ_OUT_PULSE_CLASS
+#define MY_CLASS_NAME "ecore_audio_obj_out_pulse"
+
+const Eo_Event_Description _ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY;
+#define ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY (&(_ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY))
+
+struct _Ecore_Audio_Pulse_Class {
+ pa_mainloop_api *api;
+ pa_context *context;
+ pa_context_state_t state;
+ Eina_List *outputs;
+};
+
+static struct _Ecore_Audio_Pulse_Class class_vars = {
+ .api = &functable,
+};
+
+struct _Ecore_Audio_Pulse
+{
+ char *foo;
+};
+
+typedef struct _Ecore_Audio_Pulse Ecore_Audio_Pulse;
+
+static void _volume_set(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list)
+{
+ Eo *in;
+ pa_stream *stream;
+ Eina_List *input;
+ uint32_t idx;
+ pa_cvolume pa_volume;
+ Ecore_Audio_Output *out_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_OUT_CLASS);
+
+ double volume = va_arg(*list, double);
+
+ if (volume < 0)
+ volume = 0;
+
+ pa_cvolume_set(&pa_volume, 2, volume * PA_VOLUME_NORM);
+
+ eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_volume_set(volume));
+
+ EINA_LIST_FOREACH(out_obj->inputs, input, in) {
+ eo_do(in, eo_base_data_get("pulse_data", (void **)&stream));
+ idx = pa_stream_get_index(stream);
+ pa_operation_unref(pa_context_set_sink_input_volume(class_vars.context, idx, &pa_volume, NULL, NULL));
+ }
+
+}
+
+static void _write_cb(pa_stream *stream, size_t len, void *data)
+{
+ Eo *in = data;
+
+ void *buf;
+ ssize_t bread;
+
+ buf = malloc(len);
+
+ eo_do(in, ecore_audio_obj_in_read(buf, len, &bread));
+ pa_stream_write(stream, buf, bread, free, 0, PA_SEEK_RELATIVE);
+ if (bread < len)
+ {
+ //in->ended = EINA_TRUE;
+ pa_operation_unref(pa_stream_drain(stream, NULL, NULL));
+ }
+}
+
+static Eina_Bool _update_samplerate_cb(void *data, Eo *eo_obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Eo *out = data;
+ pa_stream *stream;
+ int samplerate;
+ double speed;
+
+ eo_do(eo_obj, ecore_audio_obj_in_samplerate_get(&samplerate));
+ eo_do(eo_obj, ecore_audio_obj_in_speed_get(&speed));
+
+ eo_do(eo_obj, eo_base_data_get("pulse_data", &stream));
+
+ pa_operation_unref(pa_stream_update_sample_rate(stream, samplerate * speed, NULL, NULL));
+}
+
+static void _input_attach_internal(Eo *eo_obj, Eo *in)
+{
+ const char *name;
+ pa_sample_spec ss;
+ double speed;
+ pa_stream *stream;
+ Ecore_Audio_Object *ea_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS);
+
+ ss.format = PA_SAMPLE_FLOAT32LE;
+ eo_do(in, ecore_audio_obj_in_samplerate_get(&ss.rate));
+ eo_do(in, ecore_audio_obj_in_speed_get(&speed));
+ eo_do(in, ecore_audio_obj_in_channels_get(&ss.channels));
+ eo_do(in, ecore_audio_obj_name_get(&name));
+
+ ss.rate = ss.rate * speed;
+
+ stream = pa_stream_new(class_vars.context, name, &ss, NULL);
+ if (!stream) {
+ ERR("Could not create stream");
+ return;
+ }
+
+ eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_out_input_attach(in));
+
+ eo_do(in, eo_event_callback_add(ECORE_AUDIO_EV_IN_SAMPLERATE_CHANGED, _update_samplerate_cb, eo_obj));
+
+ eo_do(in, eo_base_data_set("pulse_data", stream, NULL));
+
+ //FIXME: Handle paused state
+
+ pa_stream_set_write_callback(stream, _write_cb, in);
+ pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_VARIABLE_RATE, NULL, NULL);
+}
+
+static Eina_Bool _delayed_attach_cb(void *data, Eo *eo_obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Eo *in = data;
+ eo_do(eo_obj, eo_event_callback_del(ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY, _delayed_attach_cb, in));
+
+ _input_attach_internal(eo_obj, in);
+
+ return EINA_TRUE;
+}
+
+static void _input_attach(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list)
+{
+ Eo *in = va_arg(*list, Eo *);
+
+ if (class_vars.state != PA_CONTEXT_READY) {
+ DBG("Delaying input_attach because PA context is not ready.");
+ eo_do(eo_obj, eo_event_callback_add(ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY, _delayed_attach_cb, in));
+ } else {
+ _input_attach_internal(eo_obj, in);
+ }
+}
+
+static void _drain_cb(pa_stream *stream, int success EINA_UNUSED, void *data EINA_UNUSED)
+{
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
+}
+
+static void _input_detach(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list)
+{
+ pa_stream *stream;
+
+ Eo *in = va_arg(*list, Eo *);
+
+ eo_do(in, eo_base_data_get("pulse_data", (void **)&stream));
+
+ pa_stream_set_write_callback(stream, NULL, NULL);
+ pa_operation_unref(pa_stream_drain(stream, _drain_cb, NULL));
+
+ eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_out_input_detach(in));
+}
+
+static void _state_cb(pa_context *context, void *data EINA_UNUSED)
+{
+ Eina_List *out;
+ Eo *eo_obj;
+ pa_context_state_t state;
+
+ state = pa_context_get_state(context);
+
+ if (state == PA_CONTEXT_READY) {
+ DBG("PA context ready.");
+ EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) {
+ eo_do(eo_obj, eo_event_callback_call(ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY, NULL, NULL));
+ }
+ } else {
+ DBG("Connection state %i", state);
+ }
+
+ class_vars.state = state;
+}
+
+static void _constructor(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ int argc;
+ char **argv;
+
+ eo_do_super(eo_obj, MY_CLASS, eo_constructor());
+
+ if (!class_vars.context) {
+ ecore_app_args_get(&argc, &argv);
+ if (!argc) {
+ DBG("Could not get program name, pulse outputs will be named ecore_audio");
+ class_vars.context = pa_context_new(class_vars.api, "ecore_audio");
+ } else {
+ class_vars.context = pa_context_new(class_vars.api, basename(argv[0]));
+ }
+ pa_context_set_state_callback(class_vars.context, _state_cb, NULL);
+ pa_context_connect(class_vars.context, NULL, PA_CONTEXT_NOFLAGS, NULL);
+ }
+
+ class_vars.outputs = eina_list_append(class_vars.outputs, eo_obj);
+}
+
+static void _destructor(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ eo_do_super(eo_obj, MY_CLASS, eo_destructor());
+
+ class_vars.outputs = eina_list_remove(class_vars.outputs, eo_obj);
+}
+
+static void _class_constructor(Eo_Class *klass)
+{
+ const Eo_Op_Func_Description func_desc[] = {
+ /* Virtual functions of parent class implemented in this class */
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
+
+ EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_VOLUME_SET), _volume_set),
+
+ EO_OP_FUNC(ECORE_AUDIO_OBJ_OUT_ID(ECORE_AUDIO_OBJ_OUT_SUB_ID_INPUT_ATTACH), _input_attach),
+ EO_OP_FUNC(ECORE_AUDIO_OBJ_OUT_ID(ECORE_AUDIO_OBJ_OUT_SUB_ID_INPUT_DETACH), _input_detach),
+ EO_OP_FUNC_SENTINEL
+ };
+
+ eo_class_funcs_set(klass, func_desc);
+}
+
+static const Eo_Op_Description op_desc[] = {
+ EO_OP_DESCRIPTION_SENTINEL
+};
+
+static const Eo_Event_Description *event_desc[] = {
+ ECORE_AUDIO_EV_OUT_PULSE_CONTEXT_READY,
+ NULL
+};
+
+
+static const Eo_Class_Description class_desc = {
+ EO_VERSION,
+ MY_CLASS_NAME,
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(&ECORE_AUDIO_OBJ_OUT_PULSE_BASE_ID, op_desc, ECORE_AUDIO_OBJ_OUT_PULSE_SUB_ID_LAST),
+ event_desc,
+ sizeof(Ecore_Audio_Pulse),
+ _class_constructor,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_audio_obj_out_pulse_class_get, &class_desc, ECORE_AUDIO_OBJ_OUT_CLASS, NULL);
diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h
new file mode 100644
index 0000000000..f89413b8a4
--- /dev/null
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h
@@ -0,0 +1,57 @@
+#ifndef ECORE_AUDIO_OUT_PULSE_H
+#define ECORE_AUDIO_OUT_PULSE_H
+
+#include <Eina.h>
+#include <Eo.h>
+
+#ifdef EAPI
+#undef EAPI
+#endif
+
+#ifdef __GNUC__
+#if __GNUC__ >= 4
+#define EAPI __attribute__ ((visibility("default")))
+#else
+#define EAPI
+#endif
+#else
+#define EAPI
+#endif
+
+/**
+ * @file ecore_audio_obj_out_pulse.h
+ * @brief Audio Module
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @addtogroup Ecore_Audio_Group
+ * @{
+ */
+
+#define ECORE_AUDIO_OBJ_OUT_PULSE_CLASS ecore_audio_obj_out_pulse_class_get()
+
+const Eo_Class *ecore_audio_obj_out_pulse_class_get() EINA_CONST;
+
+extern EAPI Eo_Op ECORE_AUDIO_OBJ_OUT_PULSE_BASE_ID;
+
+enum _Ecore_Audio_Obj_Out_Pulse_Sub_Ids
+{
+ ECORE_AUDIO_OBJ_OUT_PULSE_SUB_ID_LAST
+};
+
+#define ECORE_AUDIO_OBJ_OUT_PULSE_ID(sub_id) (ECORE_AUDIO_OBJ_OUT_PULSE_BASE_ID + EO_TYPECHECK(enum _Ecore_Audio_Obj_Out_Pulse_Sub_Ids, sub_id))
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/ecore_audio/ecore_audio_private.h b/src/lib/ecore_audio/ecore_audio_private.h
index ef08b74a9b..7868b08b5f 100644
--- a/src/lib/ecore_audio/ecore_audio_private.h
+++ b/src/lib/ecore_audio/ecore_audio_private.h
@@ -155,20 +155,6 @@ void ecore_audio_alsa_shutdown(void);
#endif /* HAVE_ALSA */
#ifdef HAVE_PULSE
-/* PA mainloop integration */
-struct _Ecore_Audio_Pa_Private
-{
- pa_mainloop_api api;
- pa_context *context;
- pa_context_state_t state;
-};
-
-/* ecore_audio_pulse */
-struct _Ecore_Audio_Pulse
-{
- pa_stream *stream;
-};
-
Ecore_Audio_Module *ecore_audio_pulse_init(void);
void ecore_audio_pulse_shutdown(void);
#endif /* HAVE_PULSE */
diff --git a/src/lib/ecore_audio/ecore_audio_pulse_ml.c b/src/lib/ecore_audio/ecore_audio_pulse_ml.c
new file mode 100644
index 0000000000..67dd96b132
--- /dev/null
+++ b/src/lib/ecore_audio/ecore_audio_pulse_ml.c
@@ -0,0 +1,308 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+
+#include <pulse/pulseaudio.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "ecore_audio_private.h"
+
+/* Ecore mainloop integration start */
+struct pa_io_event
+{
+ pa_mainloop_api *mainloop;
+ Ecore_Fd_Handler *handler;
+
+ void *userdata;
+
+ pa_io_event_flags_t flags;
+ pa_io_event_cb_t callback;
+ pa_io_event_destroy_cb_t destroy_callback;
+};
+
+static Ecore_Fd_Handler_Flags
+map_flags_to_ecore(pa_io_event_flags_t flags)
+{
+ return (Ecore_Fd_Handler_Flags)((flags & PA_IO_EVENT_INPUT ? ECORE_FD_READ : 0) | (flags & PA_IO_EVENT_OUTPUT ? ECORE_FD_WRITE : 0) | (flags & PA_IO_EVENT_ERROR ? ECORE_FD_ERROR : 0) | (flags & PA_IO_EVENT_HANGUP ? ECORE_FD_READ : 0));
+}
+
+static Eina_Bool
+_ecore_io_wrapper(void *data, Ecore_Fd_Handler *handler)
+{
+ char buf[64];
+ pa_io_event_flags_t flags = 0;
+ pa_io_event *event = (pa_io_event *)data;
+
+ if (ecore_main_fd_handler_active_get(handler, ECORE_FD_READ))
+ {
+ flags |= PA_IO_EVENT_INPUT;
+
+ /* Check for HUP and report */
+ if (recv(ecore_main_fd_handler_fd_get(handler), buf, 64, MSG_PEEK))
+ {
+ if (errno == ESHUTDOWN || errno == ECONNRESET || errno == ECONNABORTED || errno == ENETRESET)
+ {
+ DBG("HUP condition detected");
+ flags |= PA_IO_EVENT_HANGUP;
+ }
+ }
+ }
+
+ if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE))
+ flags |= PA_IO_EVENT_OUTPUT;
+ if (ecore_main_fd_handler_active_get(handler, ECORE_FD_ERROR))
+ flags |= PA_IO_EVENT_ERROR;
+
+ event->callback(event->mainloop, event, ecore_main_fd_handler_fd_get(handler), flags, event->userdata);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static pa_io_event *
+_ecore_pa_io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t flags, pa_io_event_cb_t cb, void *userdata)
+{
+ pa_io_event *event;
+
+ event = calloc(1, sizeof(pa_io_event));
+ event->mainloop = api;
+ event->userdata = userdata;
+ event->callback = cb;
+ event->flags = flags;
+ event->handler = ecore_main_fd_handler_add(fd, map_flags_to_ecore(flags), _ecore_io_wrapper, event, NULL, NULL);
+
+ return event;
+}
+
+static void
+_ecore_pa_io_enable(pa_io_event *event, pa_io_event_flags_t flags)
+{
+ event->flags = flags;
+ ecore_main_fd_handler_active_set(event->handler, map_flags_to_ecore(flags));
+}
+
+static void
+_ecore_pa_io_free(pa_io_event *event)
+{
+ ecore_main_fd_handler_del(event->handler);
+ free(event);
+}
+
+static void
+_ecore_pa_io_set_destroy(pa_io_event *event, pa_io_event_destroy_cb_t cb)
+{
+ event->destroy_callback = cb;
+}
+
+/* Timed events */
+struct pa_time_event
+{
+ pa_mainloop_api *mainloop;
+ Ecore_Timer *timer;
+ struct timeval tv;
+
+ void *userdata;
+
+ pa_time_event_cb_t callback;
+ pa_time_event_destroy_cb_t destroy_callback;
+};
+
+Eina_Bool
+_ecore_time_wrapper(void *data)
+{
+ pa_time_event *event = (pa_time_event *)data;
+
+ event->callback(event->mainloop, event, &event->tv, event->userdata);
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+pa_time_event *
+_ecore_pa_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
+{
+ pa_time_event *event;
+ struct timeval now;
+ double interval;
+
+ event = calloc(1, sizeof(pa_time_event));
+ event->mainloop = api;
+ event->userdata = userdata;
+ event->callback = cb;
+ event->tv = *tv;
+
+ if (gettimeofday(&now, NULL) == -1)
+ {
+ ERR("Failed to get the current time!");
+ return NULL;
+ }
+
+ interval = (tv->tv_sec - now.tv_sec) + (tv->tv_usec - now.tv_usec) / 1000;
+ event->timer = ecore_timer_add(interval, _ecore_time_wrapper, event);
+
+ return event;
+}
+
+void
+_ecore_pa_time_restart(pa_time_event *event, const struct timeval *tv)
+{
+ struct timeval now;
+ double interval;
+
+ /* If tv is NULL disable timer */
+ if (!tv)
+ {
+ ecore_timer_del(event->timer);
+ event->timer = NULL;
+ return;
+ }
+
+ event->tv = *tv;
+
+ if (gettimeofday(&now, NULL) == -1)
+ {
+ ERR("Failed to get the current time!");
+ return;
+ }
+
+ interval = (tv->tv_sec - now.tv_sec) + (tv->tv_usec - now.tv_usec) / 1000;
+ if (event->timer)
+ {
+ event->timer = ecore_timer_add(interval, _ecore_time_wrapper, event);
+ }
+ else
+ {
+ ecore_timer_interval_set(event->timer, interval);
+ ecore_timer_reset(event->timer);
+ }
+}
+
+void
+_ecore_pa_time_free(pa_time_event *event)
+{
+ if (event->timer)
+ ecore_timer_del(event->timer);
+
+ event->timer = NULL;
+
+ free(event);
+}
+
+void
+_ecore_pa_time_set_destroy(pa_time_event *event, pa_time_event_destroy_cb_t cb)
+{
+ event->destroy_callback = cb;
+}
+
+/* Deferred events */
+struct pa_defer_event
+{
+ pa_mainloop_api *mainloop;
+ Ecore_Idler *idler;
+
+ void *userdata;
+
+ pa_defer_event_cb_t callback;
+ pa_defer_event_destroy_cb_t destroy_callback;
+};
+
+Eina_Bool
+_ecore_defer_wrapper(void *data)
+{
+ pa_defer_event *event = (pa_defer_event *)data;
+
+ event->idler = NULL;
+ event->callback(event->mainloop, event, event->userdata);
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+pa_defer_event *
+_ecore_pa_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata)
+{
+ pa_defer_event *event;
+
+ event = calloc(1, sizeof(pa_defer_event));
+ event->mainloop = api;
+ event->userdata = userdata;
+ event->callback = cb;
+
+ event->idler = ecore_idler_add(_ecore_defer_wrapper, event);
+
+ return event;
+}
+
+void
+_ecore_pa_defer_enable(pa_defer_event *event, int b)
+{
+ if (!b && event->idler)
+ {
+ ecore_idler_del(event->idler);
+ event->idler = NULL;
+ }
+ else if (b && !event->idler)
+ {
+ event->idler = ecore_idler_add(_ecore_defer_wrapper, event);
+ }
+}
+
+void
+_ecore_pa_defer_free(pa_defer_event *event)
+{
+ if (event->idler)
+ ecore_idler_del(event->idler);
+
+ event->idler = NULL;
+
+ free(event);
+}
+
+void
+_ecore_pa_defer_set_destroy(pa_defer_event *event, pa_defer_event_destroy_cb_t cb)
+{
+ event->destroy_callback = cb;
+}
+
+static void
+_ecore_pa_quit(pa_mainloop_api *api EINA_UNUSED, int retval EINA_UNUSED)
+{
+ /* FIXME: Need to clean up timers, etc.? */
+ WRN("Not quitting mainloop, although PA requested it");
+}
+
+/* Function table for PA mainloop integration */
+const pa_mainloop_api functable = {
+ .userdata = NULL,
+
+ .io_new = _ecore_pa_io_new,
+ .io_enable = _ecore_pa_io_enable,
+ .io_free = _ecore_pa_io_free,
+ .io_set_destroy = _ecore_pa_io_set_destroy,
+
+ .time_new = _ecore_pa_time_new,
+ .time_restart = _ecore_pa_time_restart,
+ .time_free = _ecore_pa_time_free,
+ .time_set_destroy = _ecore_pa_time_set_destroy,
+
+ .defer_new = _ecore_pa_defer_new,
+ .defer_enable = _ecore_pa_defer_enable,
+ .defer_free = _ecore_pa_defer_free,
+ .defer_set_destroy = _ecore_pa_defer_set_destroy,
+
+ .quit = _ecore_pa_quit,
+};
+
+/* *****************************************************
+ * Ecore mainloop integration end
+ */