winepulse: Move pulse main loop to unix lib.
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Andrew Eikum <aeikum@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
e264ec9c71
commit
8df72bade5
|
@ -23,7 +23,6 @@
|
|||
#define _GNU_SOURCE
|
||||
|
||||
#include "config.h"
|
||||
#include <poll.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
@ -74,9 +73,6 @@ enum DriverPriority {
|
|||
|
||||
static struct pulse_config pulse_config;
|
||||
|
||||
static pa_context *pulse_ctx;
|
||||
static pa_mainloop *pulse_ml;
|
||||
|
||||
static HANDLE pulse_thread;
|
||||
static struct list g_sessions = LIST_INIT(g_sessions);
|
||||
|
||||
|
@ -95,14 +91,7 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
|
|||
if (__wine_init_unix_lib(dll, reason, NULL, &pulse))
|
||||
return FALSE;
|
||||
} else if (reason == DLL_PROCESS_DETACH) {
|
||||
if (pulse_thread)
|
||||
SetThreadPriority(pulse_thread, 0);
|
||||
if (pulse_ctx) {
|
||||
pa_context_disconnect(pulse_ctx);
|
||||
pa_context_unref(pulse_ctx);
|
||||
}
|
||||
if (pulse_ml)
|
||||
pa_mainloop_quit(pulse_ml, 0);
|
||||
__wine_init_unix_lib(dll, reason, NULL, NULL);
|
||||
if (pulse_thread) {
|
||||
WaitForSingleObject(pulse_thread, INFINITE);
|
||||
CloseHandle(pulse_thread);
|
||||
|
@ -244,65 +233,9 @@ static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
|
|||
return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
|
||||
}
|
||||
|
||||
/* Following pulseaudio design here, mainloop has the lock taken whenever
|
||||
* it is handling something for pulse, and the lock is required whenever
|
||||
* doing any pa_* call that can affect the state in any way
|
||||
*
|
||||
* pa_cond_wait is used when waiting on results, because the mainloop needs
|
||||
* the same lock taken to affect the state
|
||||
*
|
||||
* This is basically the same as the pa_threaded_mainloop implementation,
|
||||
* but that cannot be used because it uses pthread_create directly
|
||||
*
|
||||
* pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
|
||||
* pa_threaded_mainloop_signal -> pthread_cond_broadcast
|
||||
* pa_threaded_mainloop_wait -> pthread_cond_wait
|
||||
*/
|
||||
|
||||
static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
|
||||
int r;
|
||||
pulse->unlock();
|
||||
r = poll(ufds, nfds, timeout);
|
||||
pulse->lock();
|
||||
return r;
|
||||
}
|
||||
|
||||
static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
|
||||
int ret;
|
||||
pulse_ml = pa_mainloop_new();
|
||||
pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
|
||||
pulse->lock();
|
||||
pulse->broadcast();
|
||||
pa_mainloop_run(pulse_ml, &ret);
|
||||
pulse->unlock();
|
||||
pa_mainloop_free(pulse_ml);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pulse_contextcallback(pa_context *c, void *userdata)
|
||||
{
|
||||
switch (pa_context_get_state(c)) {
|
||||
default:
|
||||
FIXME("Unhandled state: %i\n", pa_context_get_state(c));
|
||||
return;
|
||||
|
||||
case PA_CONTEXT_CONNECTING:
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
case PA_CONTEXT_AUTHORIZING:
|
||||
case PA_CONTEXT_SETTING_NAME:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
TRACE("State change to %i\n", pa_context_get_state(c));
|
||||
return;
|
||||
|
||||
case PA_CONTEXT_READY:
|
||||
TRACE("Ready\n");
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
WARN("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
|
||||
break;
|
||||
}
|
||||
pulse->broadcast();
|
||||
pulse->main_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pulse_stream_state(pa_stream *s, void *user)
|
||||
|
@ -352,73 +285,6 @@ static char *get_application_name(void)
|
|||
return str;
|
||||
}
|
||||
|
||||
static HRESULT pulse_connect(void)
|
||||
{
|
||||
int len;
|
||||
WCHAR path[MAX_PATH], *name;
|
||||
char *str;
|
||||
|
||||
if (!pulse_thread)
|
||||
{
|
||||
if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
|
||||
{
|
||||
ERR("Failed to create mainloop thread.\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
pulse->cond_wait();
|
||||
}
|
||||
|
||||
if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
|
||||
return S_OK;
|
||||
if (pulse_ctx)
|
||||
pa_context_unref(pulse_ctx);
|
||||
|
||||
GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
|
||||
name = strrchrW(path, '\\');
|
||||
if (!name)
|
||||
name = path;
|
||||
else
|
||||
name++;
|
||||
len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
|
||||
str = pa_xmalloc(len);
|
||||
WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
|
||||
TRACE("Name: %s\n", str);
|
||||
pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
|
||||
pa_xfree(str);
|
||||
if (!pulse_ctx) {
|
||||
ERR("Failed to create context\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
|
||||
|
||||
TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
|
||||
if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
|
||||
goto fail;
|
||||
|
||||
/* Wait for connection */
|
||||
while (pulse->cond_wait()) {
|
||||
pa_context_state_t state = pa_context_get_state(pulse_ctx);
|
||||
|
||||
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
|
||||
goto fail;
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("Connected to server %s with protocol version: %i.\n",
|
||||
pa_context_get_server(pulse_ctx),
|
||||
pa_context_get_server_protocol_version(pulse_ctx));
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
pa_context_unref(pulse_ctx);
|
||||
pulse_ctx = NULL;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
static HRESULT pulse_stream_valid(ACImpl *This) {
|
||||
if (!This->stream)
|
||||
return AUDCLNT_E_NOT_INITIALIZED;
|
||||
|
@ -831,7 +697,7 @@ static DWORD WINAPI pulse_timer_cb(void *user)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
|
||||
static HRESULT pulse_stream_connect(ACImpl *This, pa_context *pulse_ctx, UINT32 period_bytes) {
|
||||
int ret;
|
||||
char buffer[64];
|
||||
static LONG number;
|
||||
|
@ -1318,6 +1184,8 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
|
|||
const GUID *sessionguid)
|
||||
{
|
||||
ACImpl *This = impl_from_IAudioClient3(iface);
|
||||
pa_context *pulse_ctx;
|
||||
char *name;
|
||||
HRESULT hr = S_OK;
|
||||
UINT32 bufsize_bytes;
|
||||
|
||||
|
@ -1348,17 +1216,31 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
|
|||
|
||||
pulse->lock();
|
||||
|
||||
hr = pulse_connect();
|
||||
if (FAILED(hr)) {
|
||||
pulse->unlock();
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (This->stream) {
|
||||
pulse->unlock();
|
||||
return AUDCLNT_E_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!pulse_thread)
|
||||
{
|
||||
if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
|
||||
{
|
||||
ERR("Failed to create mainloop thread.\n");
|
||||
pulse->unlock();
|
||||
return E_FAIL;
|
||||
}
|
||||
SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
pulse->cond_wait();
|
||||
}
|
||||
|
||||
name = get_application_name();
|
||||
hr = pulse->connect(name, &pulse_ctx);
|
||||
free(name);
|
||||
if (FAILED(hr)) {
|
||||
pulse->unlock();
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = pulse_spec_from_waveformat(This, fmt);
|
||||
TRACE("Obtaining format returns %08x\n", hr);
|
||||
dump_fmt(fmt);
|
||||
|
@ -1378,7 +1260,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
|
|||
|
||||
This->share = mode;
|
||||
This->flags = flags;
|
||||
hr = pulse_stream_connect(This, This->period_bytes);
|
||||
hr = pulse_stream_connect(This, pulse_ctx, This->period_bytes);
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT32 unalign;
|
||||
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
|
||||
|
|
|
@ -76,6 +76,20 @@ static void WINAPI pulse_broadcast(void)
|
|||
pthread_cond_broadcast(&pulse_cond);
|
||||
}
|
||||
|
||||
/* Following pulseaudio design here, mainloop has the lock taken whenever
|
||||
* it is handling something for pulse, and the lock is required whenever
|
||||
* doing any pa_* call that can affect the state in any way
|
||||
*
|
||||
* pa_cond_wait is used when waiting on results, because the mainloop needs
|
||||
* the same lock taken to affect the state
|
||||
*
|
||||
* This is basically the same as the pa_threaded_mainloop implementation,
|
||||
* but that cannot be used because it uses pthread_create directly
|
||||
*
|
||||
* pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
|
||||
* pa_threaded_mainloop_signal -> pthread_cond_broadcast
|
||||
* pa_threaded_mainloop_wait -> pthread_cond_wait
|
||||
*/
|
||||
static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
|
||||
{
|
||||
int r;
|
||||
|
@ -85,6 +99,18 @@ static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout,
|
|||
return r;
|
||||
}
|
||||
|
||||
static void WINAPI pulse_main_loop(void)
|
||||
{
|
||||
int ret;
|
||||
pulse_ml = pa_mainloop_new();
|
||||
pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
|
||||
pulse_lock();
|
||||
pulse_broadcast();
|
||||
pa_mainloop_run(pulse_ml, &ret);
|
||||
pulse_unlock();
|
||||
pa_mainloop_free(pulse_ml);
|
||||
}
|
||||
|
||||
static void pulse_contextcallback(pa_context *c, void *userdata)
|
||||
{
|
||||
switch (pa_context_get_state(c)) {
|
||||
|
@ -118,6 +144,50 @@ static void pulse_stream_state(pa_stream *s, void *user)
|
|||
pulse_broadcast();
|
||||
}
|
||||
|
||||
static HRESULT WINAPI pulse_connect(const char *name, pa_context **ctx)
|
||||
{
|
||||
if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx))) {
|
||||
*ctx = pulse_ctx;
|
||||
return S_OK;
|
||||
}
|
||||
if (pulse_ctx)
|
||||
pa_context_unref(pulse_ctx);
|
||||
|
||||
pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), name);
|
||||
if (!pulse_ctx) {
|
||||
ERR("Failed to create context\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
|
||||
|
||||
TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
|
||||
if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
|
||||
goto fail;
|
||||
|
||||
/* Wait for connection */
|
||||
while (pulse_cond_wait()) {
|
||||
pa_context_state_t state = pa_context_get_state(pulse_ctx);
|
||||
|
||||
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
|
||||
goto fail;
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("Connected to server %s with protocol version: %i.\n",
|
||||
pa_context_get_server(pulse_ctx),
|
||||
pa_context_get_server_protocol_version(pulse_ctx));
|
||||
*ctx = pulse_ctx;
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
pa_context_unref(pulse_ctx);
|
||||
pulse_ctx = NULL;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map)
|
||||
{
|
||||
int i;
|
||||
|
@ -412,6 +482,8 @@ static const struct unix_funcs unix_funcs =
|
|||
pulse_unlock,
|
||||
pulse_cond_wait,
|
||||
pulse_broadcast,
|
||||
pulse_main_loop,
|
||||
pulse_connect,
|
||||
pulse_test_connect,
|
||||
};
|
||||
|
||||
|
@ -430,6 +502,15 @@ NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *pt
|
|||
|
||||
*(const struct unix_funcs **)ptr_out = &unix_funcs;
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (pulse_ctx)
|
||||
{
|
||||
pa_context_disconnect(pulse_ctx);
|
||||
pa_context_unref(pulse_ctx);
|
||||
}
|
||||
if (pulse_ml)
|
||||
pa_mainloop_quit(pulse_ml, 0);
|
||||
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
|
|
@ -33,5 +33,7 @@ struct unix_funcs
|
|||
void (WINAPI *unlock)(void);
|
||||
int (WINAPI *cond_wait)(void);
|
||||
void (WINAPI *broadcast)(void);
|
||||
void (WINAPI *main_loop)(void);
|
||||
HRESULT (WINAPI *connect)(const char *name, pa_context **ret);
|
||||
HRESULT (WINAPI *test_connect)(const char *name, struct pulse_config *config);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue