Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com> On Tue, May 11, 2021 at 06:30:17PM +0200, Jacek Caban wrote:
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com> --- dlls/winepulse.drv/mmdevdrv.c | 170 ++++++---------------------------- dlls/winepulse.drv/pulse.c | 81 ++++++++++++++++ dlls/winepulse.drv/unixlib.h | 2 + 3 files changed, 109 insertions(+), 144 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 59806f28465..0bb3491a577 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -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,15 +1216,29 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
pulse->lock();
- hr = pulse_connect(); - if (FAILED(hr)) { + if (This->stream) { pulse->unlock(); - return hr; + return AUDCLNT_E_ALREADY_INITIALIZED; }
- if (This->stream) { + 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 AUDCLNT_E_ALREADY_INITIALIZED; + return hr; }
hr = pulse_spec_from_waveformat(This, 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); diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 4b4d2497314..5369d5d08a5 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -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; diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index 865a6f31ec6..f2bb7c78c82 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -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); };