This exposes the actual devices (and virtual sinks/sources) as reported by PulseAudio to an application, allowing it to select the devices itself and, for example, record from (or render to) two devices at the same time. The "PulseAudio" device (which is movable) is still the default, as before, with the same GUID to preserve compatibility with existing setups.
Based on a patch by Mark Harmstone mark@harmstone.com, with changes by Sebastian Lackner sebastian@fds-team.de.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/winepulse.drv/mmdevdrv.c | 212 ++++++++++++++++++++++++++++------ dlls/winepulse.drv/pulse.c | 138 ++++++++++++++++++++-- dlls/winepulse.drv/unixlib.h | 13 +++ 3 files changed, 319 insertions(+), 44 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index e3d5ea9..ac5f0a5 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -69,6 +69,8 @@ static GUID pulse_render_guid = static GUID pulse_capture_guid = { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
+static const WCHAR *drv_key_devicesW = L"Software\Wine\Drivers\winepulse.drv\devices"; + static CRITICAL_SECTION session_cs; static CRITICAL_SECTION_DEBUG session_cs_debug = { 0, 0, &session_cs, @@ -145,9 +147,9 @@ struct ACImpl {
AudioSession *session; AudioSessionWrapper *session_wrapper; -};
-static const WCHAR defaultW[] = L"PulseAudio"; + char pulse_name[0]; +};
static const IAudioClient3Vtbl AudioClient3_Vtbl; static const IAudioRenderClientVtbl AudioRenderClient_Vtbl; @@ -267,39 +269,115 @@ static void set_stream_volumes(ACImpl *This) pulse_call(set_volumes, ¶ms); }
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys, - UINT *num, UINT *def_index) +static void get_device_guid(HKEY drv_key, EDataFlow flow, const WCHAR *pulse_name, GUID *guid) { - WCHAR *id; + WCHAR key_name[MAX_PULSE_NAME_LEN + 2]; + DWORD type, size = sizeof(*guid); + LSTATUS status; + HKEY dev_key;
- TRACE("%d %p %p %p\n", flow, ids, num, def_index); + if (!pulse_name[0]) { + *guid = (flow == eRender) ? pulse_render_guid : pulse_capture_guid; + return; + }
- *num = 1; - *def_index = 0; + if (!drv_key) { + CoCreateGuid(guid); + return; + }
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids)); - *keys = NULL; - if (!*ids) - return E_OUTOFMEMORY; + key_name[0] = (flow == eRender) ? '0' : '1'; + key_name[1] = ','; + wcscpy(key_name + 2, pulse_name);
- (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW)); - *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys)); - if (!*keys || !id) { - HeapFree(GetProcessHeap(), 0, id); - HeapFree(GetProcessHeap(), 0, *keys); - HeapFree(GetProcessHeap(), 0, *ids); - *ids = NULL; - *keys = NULL; - return E_OUTOFMEMORY; + status = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY, + NULL, &dev_key, NULL); + if (status != ERROR_SUCCESS) { + ERR("Failed to open registry key for device %s: %u\n", debugstr_w(pulse_name), status); + CoCreateGuid(guid); + return; } - memcpy(id, defaultW, sizeof(defaultW));
- if (flow == eRender) - (*keys)[0] = pulse_render_guid; - else - (*keys)[0] = pulse_capture_guid; + status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE*)guid, &size); + if (status != ERROR_SUCCESS || type != REG_BINARY || size != sizeof(*guid)) { + CoCreateGuid(guid); + status = RegSetValueExW(dev_key, L"guid", 0, REG_BINARY, (BYTE*)guid, sizeof(*guid)); + if (status != ERROR_SUCCESS) + ERR("Failed to store device GUID for %s to registry: %u\n", debugstr_w(pulse_name), status); + } + RegCloseKey(dev_key); +}
- return S_OK; +HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **keys, + UINT *num, UINT *def_index) +{ + struct get_endpoint_ids_params params; + GUID *guids = NULL; + WCHAR **ids = NULL; + unsigned int i = 0; + LSTATUS status; + DWORD name_len; + HKEY drv_key; + WCHAR *p; + + TRACE("%d %p %p %p\n", flow, ids_out, num, def_index); + + params.flow = flow; + params.size = MAX_PULSE_NAME_LEN * 4; + params.device_strings = NULL; + do { + HeapFree(GetProcessHeap(), 0, params.device_strings); + params.device_strings = HeapAlloc(GetProcessHeap(), 0, params.size); + pulse_call(get_endpoint_ids, ¶ms); + } while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + + if (FAILED(params.result)) + goto end; + + ids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*ids)); + guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids)); + if (!ids || !guids) { + params.result = E_OUTOFMEMORY; + goto end; + } + + status = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, + KEY_WRITE | KEY_WOW64_64KEY, NULL, &drv_key, NULL); + if (status != ERROR_SUCCESS) { + ERR("Failed to open devices registry key: %u\n", status); + drv_key = NULL; + } + + /* straight sequence of (pulse_name+friendly name) pairs of NUL terminated strings */ + p = params.device_strings; + for (i = 0; i < params.num; i++) { + get_device_guid(drv_key, flow, p, &guids[i]); + p += wcslen(p) + 1; /* skip pulse_name */ + + name_len = wcslen(p) + 1; + if (!(ids[i] = HeapAlloc(GetProcessHeap(), 0, name_len * sizeof(WCHAR)))) { + params.result = E_OUTOFMEMORY; + break; + } + memcpy(ids[i], p, name_len * sizeof(WCHAR)); + p += name_len; + } + if (drv_key) + RegCloseKey(drv_key); + +end: + HeapFree(GetProcessHeap(), 0, params.device_strings); + if (FAILED(params.result)) { + HeapFree(GetProcessHeap(), 0, guids); + while (i--) HeapFree(GetProcessHeap(), 0, ids[i]); + HeapFree(GetProcessHeap(), 0, ids); + } else { + *ids_out = ids; + *keys = guids; + *num = params.num; + *def_index = 0; + } + return params.result; }
int WINAPI AUDDRV_GetPriority(void) @@ -314,23 +392,87 @@ int WINAPI AUDDRV_GetPriority(void) return SUCCEEDED(params.result) ? Priority_Preferred : Priority_Unavailable; }
+static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_NAME_LEN], EDataFlow *flow) +{ + WCHAR key_name[MAX_PULSE_NAME_LEN + 2]; + DWORD key_name_size; + DWORD index = 0; + HKEY key; + + /* Return empty string for default PulseAudio device */ + pulse_name[0] = 0; + if (IsEqualGUID(guid, &pulse_render_guid)) { + *flow = eRender; + return TRUE; + } else if (IsEqualGUID(guid, &pulse_capture_guid)) { + *flow = eCapture; + return TRUE; + } + + if (RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ | KEY_WOW64_64KEY, &key) != ERROR_SUCCESS) { + WARN("No devices found in registry\n"); + return FALSE; + } + + for (;;) { + DWORD size, type; + LSTATUS status; + GUID reg_guid; + HKEY dev_key; + + key_name_size = ARRAY_SIZE(key_name); + if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + break; + + if (RegOpenKeyExW(key, key_name, 0, KEY_READ | KEY_WOW64_64KEY, &dev_key) != ERROR_SUCCESS) { + ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name)); + continue; + } + + size = sizeof(reg_guid); + status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE *)®_guid, &size); + RegCloseKey(dev_key); + + if (status == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(®_guid, guid)) { + RegCloseKey(key); + + TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name)); + + if (key_name[0] == '0') + *flow = eRender; + else if (key_name[0] == '1') + *flow = eCapture; + else { + WARN("Unknown device type: %c\n", key_name[0]); + return FALSE; + } + + return WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, pulse_name, MAX_PULSE_NAME_LEN, NULL, NULL); + } + } + + RegCloseKey(key); + WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid)); + return FALSE; +} + HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out) { ACImpl *This; + char pulse_name[MAX_PULSE_NAME_LEN]; EDataFlow dataflow; + unsigned len; HRESULT hr;
TRACE("%s %p %p\n", debugstr_guid(guid), dev, out); - if (IsEqualGUID(guid, &pulse_render_guid)) - dataflow = eRender; - else if (IsEqualGUID(guid, &pulse_capture_guid)) - dataflow = eCapture; - else - return E_UNEXPECTED; + + if (!get_pulse_name_by_guid(guid, pulse_name, &dataflow)) + return AUDCLNT_E_DEVICE_INVALIDATED;
*out = NULL;
- This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This)); + len = strlen(pulse_name) + 1; + This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(ACImpl, pulse_name[len])); if (!This) return E_OUTOFMEMORY;
@@ -342,6 +484,7 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl; This->dataflow = dataflow; This->parent = dev; + memcpy(This->pulse_name, pulse_name, len);
hr = CoCreateFreeThreadedMarshaler((IUnknown*)&This->IAudioClient3_iface, &This->marshal); if (FAILED(hr)) { @@ -609,6 +752,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, }
params.name = name = get_application_name(); + params.pulse_name = This->pulse_name; params.dataflow = This->dataflow; params.mode = mode; params.flags = flags; diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 3e65936..724b444 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -81,6 +81,12 @@ typedef struct _ACPacket UINT32 discont; } ACPacket;
+typedef struct _PhysDevice { + struct list entry; + WCHAR *name; + char pulse_name[0]; +} PhysDevice; + static pa_context *pulse_ctx; static pa_mainloop *pulse_ml;
@@ -89,6 +95,8 @@ static WAVEFORMATEXTENSIBLE pulse_fmt[2]; static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
static UINT g_phys_speakers_mask = 0; +static struct list g_phys_speakers = LIST_INIT(g_phys_speakers); +static struct list g_phys_sources = LIST_INIT(g_phys_sources);
static const REFERENCE_TIME MinimumPeriod = 30000; static const REFERENCE_TIME DefaultPeriod = 100000; @@ -128,6 +136,20 @@ static void dump_attr(const pa_buffer_attr *attr) TRACE("prebuf: %u\n", attr->prebuf); }
+static void free_phys_device_lists(void) +{ + static struct list *const lists[] = { &g_phys_speakers, &g_phys_sources, NULL }; + struct list *const *list = lists; + PhysDevice *dev, *dev_next; + + do { + LIST_FOR_EACH_ENTRY_SAFE(dev, dev_next, *list, PhysDevice, entry) { + free(dev->name); + free(dev); + } + } while (*(++list)); +} + /* copied from kernelbase */ static int muldiv(int a, int b, int c) { @@ -190,6 +212,7 @@ static NTSTATUS pulse_process_attach(void *args)
static NTSTATUS pulse_process_detach(void *args) { + free_phys_device_lists(); if (pulse_ctx) { pa_context_disconnect(pulse_ctx); @@ -215,6 +238,42 @@ static NTSTATUS pulse_main_loop(void *args) return STATUS_SUCCESS; }
+static NTSTATUS pulse_get_endpoint_ids(void *args) +{ + struct get_endpoint_ids_params *params = args; + struct list *list = (params->flow == eRender) ? &g_phys_speakers : &g_phys_sources; + WCHAR pulse_name[MAX_PULSE_NAME_LEN]; + WCHAR *p = params->device_strings; + DWORD len, name_len, needed = 0; + PhysDevice *dev; + + /* params->num is the number of devices returned. Each returned device is a pair of + pulse_name followed by its friendly name, both NUL terminated, one after another. + The next device immediately follows as another two NUL terminated strings, etc. */ + params->num = 0; + LIST_FOR_EACH_ENTRY(dev, list, PhysDevice, entry) { + if (!(len = ntdll_umbstowcs(dev->pulse_name, strlen(dev->pulse_name) + 1, pulse_name, ARRAY_SIZE(pulse_name)))) + continue; + name_len = lstrlenW(dev->name) + 1; + + needed += (len + name_len) * sizeof(WCHAR); + if (needed <= params->size) { + memcpy(p, pulse_name, len * sizeof(WCHAR)); + p += len; + memcpy(p, dev->name, name_len * sizeof(WCHAR)); + p += name_len; + } + params->num++; + } + + if (needed > params->size) { + params->size = needed; + params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } else + params->result = S_OK; + return STATUS_SUCCESS; +} + static void pulse_contextcallback(pa_context *c, void *userdata) { switch (pa_context_get_state(c)) { @@ -357,12 +416,49 @@ static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) return mask; }
-/* For default PulseAudio render device, OR together all of the - * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */ +static void pulse_add_device(struct list *list, const char *pulse_name, const char *name) +{ + DWORD len = strlen(pulse_name), name_len = strlen(name); + PhysDevice *dev = malloc(FIELD_OFFSET(PhysDevice, pulse_name[len + 1])); + WCHAR *wname; + + if (!dev) + return; + + if (!(wname = malloc((name_len + 1) * sizeof(WCHAR)))) { + free(dev); + return; + } + + if (!(name_len = ntdll_umbstowcs(name, name_len, wname, name_len)) || + !(dev->name = realloc(wname, (name_len + 1) * sizeof(WCHAR)))) { + free(wname); + free(dev); + return; + } + dev->name[name_len] = 0; + memcpy(dev->pulse_name, pulse_name, len + 1); + + list_add_tail(list, &dev->entry); +} + static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) { - if (i) - g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map); + if (!i || !i->name || !i->name[0]) + return; + + /* For default PulseAudio render device, OR together all of the + * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */ + g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map); + + pulse_add_device(&g_phys_speakers, i->name, i->description); +} + +static void pulse_phys_sources_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) +{ + if (!i || !i->name || !i->name[0]) + return; + pulse_add_device(&g_phys_sources, i->name, i->description); }
/* For most hardware on Windows, users must choose a configuration with an even @@ -579,7 +675,14 @@ static NTSTATUS pulse_test_connect(void *args) pulse_probe_settings(1, &pulse_fmt[0]); pulse_probe_settings(0, &pulse_fmt[1]);
+ free_phys_device_lists(); + list_init(&g_phys_speakers); + list_init(&g_phys_sources); g_phys_speakers_mask = 0; + + pulse_add_device(&g_phys_speakers, "", "PulseAudio"); + pulse_add_device(&g_phys_sources, "", "PulseAudio"); + o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL); if (o) { while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 && @@ -588,6 +691,14 @@ static NTSTATUS pulse_test_connect(void *args) pa_operation_unref(o); }
+ o = pa_context_get_source_info_list(pulse_ctx, &pulse_phys_sources_cb, NULL); + if (o) { + while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 && + pa_operation_get_state(o) == PA_OPERATION_RUNNING) + {} + pa_operation_unref(o); + } + pa_context_unref(pulse_ctx); pulse_ctx = NULL; pa_mainloop_free(pulse_ml); @@ -771,8 +882,9 @@ static HRESULT pulse_spec_from_waveformat(struct pulse_stream *stream, const WAV return S_OK; }
-static HRESULT pulse_stream_connect(struct pulse_stream *stream, UINT32 period_bytes) +static HRESULT pulse_stream_connect(struct pulse_stream *stream, const char *pulse_name, UINT32 period_bytes) { + pa_stream_flags_t flags = PA_STREAM_START_CORKED | PA_STREAM_START_UNMUTED | PA_STREAM_ADJUST_LATENCY; int ret; char buffer[64]; static LONG number; @@ -797,12 +909,17 @@ static HRESULT pulse_stream_connect(struct pulse_stream *stream, UINT32 period_b attr.maxlength = stream->bufsize_frames * pa_frame_size(&stream->ss); attr.prebuf = pa_frame_size(&stream->ss); dump_attr(&attr); + + /* If specific device was requested, use it exactly */ + if (pulse_name[0]) + flags |= PA_STREAM_DONT_MOVE; + else + pulse_name = NULL; /* use default */ + if (stream->dataflow == eRender) - ret = pa_stream_connect_playback(stream->stream, NULL, &attr, - PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_ADJUST_LATENCY, NULL, NULL); + ret = pa_stream_connect_playback(stream->stream, pulse_name, &attr, flags, NULL, NULL); else - ret = pa_stream_connect_record(stream->stream, NULL, &attr, - PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_ADJUST_LATENCY); + ret = pa_stream_connect_record(stream->stream, pulse_name, &attr, flags); if (ret < 0) { WARN("Returns %i\n", ret); return AUDCLNT_E_ENDPOINT_CREATE_FAILED; @@ -864,7 +981,7 @@ static NTSTATUS pulse_create_stream(void *args)
stream->share = params->mode; stream->flags = params->flags; - hr = pulse_stream_connect(stream, stream->period_bytes); + hr = pulse_stream_connect(stream, params->pulse_name, stream->period_bytes); if (SUCCEEDED(hr)) { UINT32 unalign; const pa_buffer_attr *attr = pa_stream_get_buffer_attr(stream->stream); @@ -1967,6 +2084,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = pulse_process_attach, pulse_process_detach, pulse_main_loop, + pulse_get_endpoint_ids, pulse_create_stream, pulse_release_stream, pulse_start, diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index d28a73c..0afd0ef 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -19,6 +19,8 @@ #include "wine/list.h" #include "wine/unixlib.h"
+#define MAX_PULSE_NAME_LEN 256 + struct pulse_stream;
struct pulse_config @@ -37,9 +39,19 @@ struct main_loop_params HANDLE event; };
+struct get_endpoint_ids_params +{ + EDataFlow flow; + DWORD size; + WCHAR *device_strings; + HRESULT result; + unsigned int num; +}; + struct create_stream_params { const char *name; + const char *pulse_name; EDataFlow dataflow; AUDCLNT_SHAREMODE mode; DWORD flags; @@ -191,6 +203,7 @@ enum unix_funcs process_attach, process_detach, main_loop, + get_endpoint_ids, create_stream, release_stream, start,
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Will be useful for next patch since we'll have to look it up on each prop call.
dlls/winepulse.drv/mmdevdrv.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index ac5f0a5..1a61f81 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -63,6 +63,14 @@ static struct pulse_config pulse_config;
static HANDLE pulse_thread; static struct list g_sessions = LIST_INIT(g_sessions); +static struct list g_devices_cache = LIST_INIT(g_devices_cache); + +struct device_cache { + struct list entry; + GUID guid; + EDataFlow dataflow; + char pulse_name[0]; +};
static GUID pulse_render_guid = { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } }; @@ -90,6 +98,10 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) if (__wine_unix_call(pulse_handle, process_attach, NULL)) return FALSE; } else if (reason == DLL_PROCESS_DETACH) { + struct device_cache *device, *device_next; + + LIST_FOR_EACH_ENTRY_SAFE(device, device_next, &g_devices_cache, struct device_cache, entry) + free(device); __wine_unix_call(pulse_handle, process_detach, NULL); if (pulse_thread) { WaitForSingleObject(pulse_thread, INFINITE); @@ -394,6 +406,7 @@ int WINAPI AUDDRV_GetPriority(void)
static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_NAME_LEN], EDataFlow *flow) { + struct device_cache *device; WCHAR key_name[MAX_PULSE_NAME_LEN + 2]; DWORD key_name_size; DWORD index = 0; @@ -409,6 +422,15 @@ static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_N return TRUE; }
+ /* Check the cache first */ + LIST_FOR_EACH_ENTRY(device, &g_devices_cache, struct device_cache, entry) { + if (!IsEqualGUID(guid, &device->guid)) + continue; + *flow = device->dataflow; + strcpy(pulse_name, device->pulse_name); + return TRUE; + } + if (RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ | KEY_WOW64_64KEY, &key) != ERROR_SUCCESS) { WARN("No devices found in registry\n"); return FALSE; @@ -419,6 +441,7 @@ static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_N LSTATUS status; GUID reg_guid; HKEY dev_key; + int len;
key_name_size = ARRAY_SIZE(key_name); if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) @@ -447,7 +470,16 @@ static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_N return FALSE; }
- return WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, pulse_name, MAX_PULSE_NAME_LEN, NULL, NULL); + if (!(len = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, pulse_name, MAX_PULSE_NAME_LEN, NULL, NULL))) + return FALSE; + + if ((device = malloc(FIELD_OFFSET(struct device_cache, pulse_name[len])))) { + device->guid = reg_guid; + device->dataflow = *flow; + strcpy(device->pulse_name, pulse_name); + list_add_tail(&g_devices_cache, &device->entry); + } + return TRUE; } }
Based on a patch by Mark Harmstone mark@harmstone.com.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/winepulse.drv/mmdevdrv.c | 32 +++++++++- dlls/winepulse.drv/pulse.c | 114 ++++++++++++++++++++++++++++++++-- dlls/winepulse.drv/unixlib.h | 22 +++++++ 3 files changed, 162 insertions(+), 6 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 1a61f81..0016da0 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -2550,6 +2550,10 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out) { + struct get_prop_value_params params; + char pulse_name[MAX_PULSE_NAME_LEN]; + DWORD size; + TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) { @@ -2559,5 +2563,31 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI return out->ulVal ? S_OK : E_FAIL; }
- return E_NOTIMPL; + if (!get_pulse_name_by_guid(guid, pulse_name, ¶ms.flow)) + return E_FAIL; + + params.pulse_name = pulse_name; + params.guid = guid; + params.prop = prop; + pulse_call(get_prop_value, ¶ms); + + if (params.result != S_OK) + return params.result; + + switch (params.vt) { + case VT_LPWSTR: + size = (wcslen(params.wstr) + 1) * sizeof(WCHAR); + if (!(out->pwszVal = CoTaskMemAlloc(size))) + return E_OUTOFMEMORY; + memcpy(out->pwszVal, params.wstr, size); + break; + case VT_UI4: + out->ulVal = params.ulVal; + break; + default: + assert(0); + } + out->vt = params.vt; + + return S_OK; } diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 724b444..b4e574f 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -84,6 +84,10 @@ typedef struct _ACPacket typedef struct _PhysDevice { struct list entry; WCHAR *name; + enum phys_device_bus_type bus_type; + USHORT vendor_id, product_id; + EndpointFormFactor form; + UINT index; char pulse_name[0]; } PhysDevice;
@@ -416,7 +420,33 @@ static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) return mask; }
-static void pulse_add_device(struct list *list, const char *pulse_name, const char *name) +static void fill_device_info(PhysDevice *dev, pa_proplist *p) +{ + const char *buffer; + + dev->bus_type = phys_device_bus_invalid; + dev->vendor_id = 0; + dev->product_id = 0; + + if (!p) + return; + + if ((buffer = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) { + if (!strcmp(buffer, "usb")) + dev->bus_type = phys_device_bus_usb; + else if (!strcmp(buffer, "pci")) + dev->bus_type = phys_device_bus_pci; + } + + if ((buffer = pa_proplist_gets(p, PA_PROP_DEVICE_VENDOR_ID))) + dev->vendor_id = strtol(buffer, NULL, 16); + + if ((buffer = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_ID))) + dev->product_id = strtol(buffer, NULL, 16); +} + +static void pulse_add_device(struct list *list, pa_proplist *proplist, int index, EndpointFormFactor form, + const char *pulse_name, const char *name) { DWORD len = strlen(pulse_name), name_len = strlen(name); PhysDevice *dev = malloc(FIELD_OFFSET(PhysDevice, pulse_name[len + 1])); @@ -437,6 +467,9 @@ static void pulse_add_device(struct list *list, const char *pulse_name, const ch return; } dev->name[name_len] = 0; + dev->form = form; + dev->index = index; + fill_device_info(dev, proplist); memcpy(dev->pulse_name, pulse_name, len + 1);
list_add_tail(list, &dev->entry); @@ -451,14 +484,15 @@ static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */ g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
- pulse_add_device(&g_phys_speakers, i->name, i->description); + pulse_add_device(&g_phys_speakers, i->proplist, i->index, Speakers, i->name, i->description); }
static void pulse_phys_sources_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) { if (!i || !i->name || !i->name[0]) return; - pulse_add_device(&g_phys_sources, i->name, i->description); + pulse_add_device(&g_phys_sources, i->proplist, i->index, + (i->monitor_of_sink == PA_INVALID_INDEX) ? Microphone : LineLevel, i->name, i->description); }
/* For most hardware on Windows, users must choose a configuration with an even @@ -680,8 +714,8 @@ static NTSTATUS pulse_test_connect(void *args) list_init(&g_phys_sources); g_phys_speakers_mask = 0;
- pulse_add_device(&g_phys_speakers, "", "PulseAudio"); - pulse_add_device(&g_phys_sources, "", "PulseAudio"); + pulse_add_device(&g_phys_speakers, NULL, 0, Speakers, "", "PulseAudio"); + pulse_add_device(&g_phys_sources, NULL, 0, Microphone, "", "PulseAudio");
o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL); if (o) { @@ -2079,6 +2113,75 @@ static NTSTATUS pulse_is_started(void *args) return STATUS_SUCCESS; }
+static BOOL get_device_path(PhysDevice *dev, struct get_prop_value_params *params) +{ + const GUID *guid = params->guid; + UINT serial_number; + const char *fmt; + char path[128]; + int len; + + switch (dev->bus_type) { + case phys_device_bus_pci: + fmt = "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X"; + break; + case phys_device_bus_usb: + fmt = "{1}.USB\VID_%04X&PID_%04X\%u&%08X"; + break; + default: + return FALSE; + } + + /* As hardly any audio devices have serial numbers, Windows instead + appears to use a persistent random number. We emulate this here + by instead using the last 8 hex digits of the GUID. */ + serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7]; + + len = sprintf(path, fmt, dev->vendor_id, dev->product_id, dev->index, serial_number); + ntdll_umbstowcs(path, len + 1, params->wstr, ARRAY_SIZE(params->wstr)); + + params->vt = VT_LPWSTR; + return TRUE; +} + +static NTSTATUS pulse_get_prop_value(void *args) +{ + static const GUID PKEY_AudioEndpoint_GUID = { + 0x1da5d803, 0xd492, 0x4edd, {0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e} + }; + static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */ + {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2 + }; + struct get_prop_value_params *params = args; + struct list *list = (params->flow == eRender) ? &g_phys_speakers : &g_phys_sources; + PhysDevice *dev; + + params->result = S_OK; + LIST_FOR_EACH_ENTRY(dev, list, PhysDevice, entry) { + if (strcmp(params->pulse_name, dev->pulse_name)) + continue; + if (IsEqualPropertyKey(*params->prop, devicepath_key)) { + if (!get_device_path(dev, params)) + break; + return STATUS_SUCCESS; + } else if (IsEqualGUID(¶ms->prop->fmtid, &PKEY_AudioEndpoint_GUID)) { + switch (params->prop->pid) { + case 0: /* FormFactor */ + params->vt = VT_UI4; + params->ulVal = dev->form; + return STATUS_SUCCESS; + default: + break; + } + } + params->result = E_NOTIMPL; + return STATUS_SUCCESS; + } + + params->result = E_FAIL; + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { pulse_process_attach, @@ -2105,4 +2208,5 @@ const unixlib_entry_t __wine_unix_call_funcs[] = pulse_set_event_handle, pulse_test_connect, pulse_is_started, + pulse_get_prop_value, }; diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index 0afd0ef..6014dff 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -23,6 +23,12 @@
struct pulse_stream;
+enum phys_device_bus_type { + phys_device_bus_invalid = -1, + phys_device_bus_pci, + phys_device_bus_usb +}; + struct pulse_config { struct @@ -198,6 +204,21 @@ struct is_started_params BOOL started; };
+struct get_prop_value_params +{ + const char *pulse_name; + const GUID *guid; + const PROPERTYKEY *prop; + EDataFlow flow; + HRESULT result; + VARTYPE vt; + union + { + WCHAR wstr[128]; + ULONG ulVal; + }; +}; + enum unix_funcs { process_attach, @@ -224,4 +245,5 @@ enum unix_funcs set_event_handle, test_connect, is_started, + get_prop_value, };
From: Mark Harmstone mark@harmstone.com Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/winepulse.drv/Makefile.in | 2 +- dlls/winepulse.drv/mmdevdrv.c | 106 +++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 5 deletions(-)
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in index c71b283..d4b40e6 100644 --- a/dlls/winepulse.drv/Makefile.in +++ b/dlls/winepulse.drv/Makefile.in @@ -1,7 +1,7 @@ EXTRADEFS = -DWINE_NO_LONG_TYPES MODULE = winepulse.drv UNIXLIB = winepulse.so -IMPORTS = dxguid uuid winmm user32 advapi32 ole32 +IMPORTS = dxguid uuid winmm user32 advapi32 ole32 version EXTRALIBS = $(PULSE_LIBS) $(PTHREAD_LIBS) -lm EXTRAINCL = $(PULSE_CFLAGS)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 0016da0..2bc4551 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -30,6 +30,7 @@ #include "wine/list.h"
#include "ole2.h" +#include "mimeole.h" #include "dshow.h" #include "dsound.h" #include "propsys.h" @@ -243,13 +244,110 @@ static DWORD CALLBACK pulse_mainloop_thread(void *event) return 0; }
-static char *get_application_name(void) +typedef struct tagLANGANDCODEPAGE +{ + WORD wLanguage; + WORD wCodePage; +} LANGANDCODEPAGE; + +static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, DWORD *len) +{ + WCHAR pn[37]; + swprintf(pn, ARRAY_SIZE(pn), L"\StringFileInfo\%04x%04x\ProductName", lang->wLanguage, lang->wCodePage); + return VerQueryValueW(data, pn, buffer, len) && *len; +} + +static char *get_application_name(BOOL query_app_name) { WCHAR path[MAX_PATH], *name; + char *str = NULL; size_t len; - char *str;
GetModuleFileNameW(NULL, path, ARRAY_SIZE(path)); + + if (query_app_name) + { + UINT translate_size, productname_size; + LANGANDCODEPAGE *translate; + LPVOID productname; + BOOL found = FALSE; + void *data = NULL; + unsigned int i; + LCID locale; + DWORD size; + + size = GetFileVersionInfoSizeW(path, NULL); + if (!size) + goto skip; + + data = malloc(size); + if (!data) + goto skip; + + if (!GetFileVersionInfoW(path, 0, size, data)) + goto skip; + + if (!VerQueryValueW(data, L"\VarFileInfo\Translation", (LPVOID *)&translate, &translate_size)) + goto skip; + + /* no translations found */ + if (translate_size < sizeof(LANGANDCODEPAGE)) + goto skip; + + /* The following code will try to find the best translation. We first search for an + * exact match of the language, then a match of the language PRIMARYLANGID, then we + * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the + * first entry which contains a proper productname. */ + locale = GetThreadLocale(); + + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { + if (translate[i].wLanguage == locale && + query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + + if (!found) { + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { + if (PRIMARYLANGID(translate[i].wLanguage) == PRIMARYLANGID(locale) && + query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + } + + if (!found) { + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { + if (PRIMARYLANGID(translate[i].wLanguage) == LANG_NEUTRAL && + query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + } + + if (!found) { + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { + if (query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + } + + if (found) { + len = WideCharToMultiByte(CP_UTF8, 0, productname, -1, NULL, 0, NULL, NULL); + str = malloc(len); + if (str) WideCharToMultiByte(CP_UTF8, 0, productname, -1, str, len, NULL, NULL); + } + + skip: + free(data); + if (str) return str; + } + name = wcsrchr(path, '\'); if (!name) name = path; @@ -397,7 +495,7 @@ int WINAPI AUDDRV_GetPriority(void) struct test_connect_params params; char *name;
- params.name = name = get_application_name(); + params.name = name = get_application_name(FALSE); params.config = &pulse_config; pulse_call(test_connect, ¶ms); free(name); @@ -783,7 +881,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, CloseHandle(event); }
- params.name = name = get_application_name(); + params.name = name = get_application_name(TRUE); params.pulse_name = This->pulse_name; params.dataflow = This->dataflow; params.mode = mode;
From: Mark Harmstone mark@harmstone.com
And get rid of config->speakers_mask and g_phys_speakers_mask since they are no longer needed.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/winepulse.drv/mmdevdrv.c | 7 ------- dlls/winepulse.drv/pulse.c | 30 +++++++++++++++++++++--------- dlls/winepulse.drv/unixlib.h | 1 - 3 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 2bc4551..0fd642e 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -2654,13 +2654,6 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI
TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
- if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) { - out->vt = VT_UI4; - out->ulVal = pulse_config.speakers_mask; - - return out->ulVal ? S_OK : E_FAIL; - } - if (!get_pulse_name_by_guid(guid, pulse_name, ¶ms.flow)) return E_FAIL;
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index b4e574f..497cf77 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -87,6 +87,7 @@ typedef struct _PhysDevice { enum phys_device_bus_type bus_type; USHORT vendor_id, product_id; EndpointFormFactor form; + DWORD channel_mask; UINT index; char pulse_name[0]; } PhysDevice; @@ -98,7 +99,6 @@ static pa_mainloop *pulse_ml; static WAVEFORMATEXTENSIBLE pulse_fmt[2]; static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
-static UINT g_phys_speakers_mask = 0; static struct list g_phys_speakers = LIST_INIT(g_phys_speakers); static struct list g_phys_sources = LIST_INIT(g_phys_sources);
@@ -446,7 +446,7 @@ static void fill_device_info(PhysDevice *dev, pa_proplist *p) }
static void pulse_add_device(struct list *list, pa_proplist *proplist, int index, EndpointFormFactor form, - const char *pulse_name, const char *name) + DWORD channel_mask, const char *pulse_name, const char *name) { DWORD len = strlen(pulse_name), name_len = strlen(name); PhysDevice *dev = malloc(FIELD_OFFSET(PhysDevice, pulse_name[len + 1])); @@ -469,6 +469,7 @@ static void pulse_add_device(struct list *list, pa_proplist *proplist, int index dev->name[name_len] = 0; dev->form = form; dev->index = index; + dev->channel_mask = channel_mask; fill_device_info(dev, proplist); memcpy(dev->pulse_name, pulse_name, len + 1);
@@ -477,14 +478,20 @@ static void pulse_add_device(struct list *list, pa_proplist *proplist, int index
static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) { + struct list *speaker; + DWORD channel_mask; + if (!i || !i->name || !i->name[0]) return; + channel_mask = pulse_channel_map_to_channel_mask(&i->channel_map);
/* For default PulseAudio render device, OR together all of the * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */ - g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map); + speaker = list_head(&g_phys_speakers); + if (speaker) + LIST_ENTRY(speaker, PhysDevice, entry)->channel_mask |= channel_mask;
- pulse_add_device(&g_phys_speakers, i->proplist, i->index, Speakers, i->name, i->description); + pulse_add_device(&g_phys_speakers, i->proplist, i->index, Speakers, channel_mask, i->name, i->description); }
static void pulse_phys_sources_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) @@ -492,7 +499,7 @@ static void pulse_phys_sources_cb(pa_context *c, const pa_source_info *i, int eo if (!i || !i->name || !i->name[0]) return; pulse_add_device(&g_phys_sources, i->proplist, i->index, - (i->monitor_of_sink == PA_INVALID_INDEX) ? Microphone : LineLevel, i->name, i->description); + (i->monitor_of_sink == PA_INVALID_INDEX) ? Microphone : LineLevel, 0, i->name, i->description); }
/* For most hardware on Windows, users must choose a configuration with an even @@ -712,10 +719,9 @@ static NTSTATUS pulse_test_connect(void *args) free_phys_device_lists(); list_init(&g_phys_speakers); list_init(&g_phys_sources); - g_phys_speakers_mask = 0;
- pulse_add_device(&g_phys_speakers, NULL, 0, Speakers, "", "PulseAudio"); - pulse_add_device(&g_phys_sources, NULL, 0, Microphone, "", "PulseAudio"); + pulse_add_device(&g_phys_speakers, NULL, 0, Speakers, 0, "", "PulseAudio"); + pulse_add_device(&g_phys_sources, NULL, 0, Microphone, 0, "", "PulseAudio");
o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL); if (o) { @@ -738,7 +744,6 @@ static NTSTATUS pulse_test_connect(void *args) pa_mainloop_free(pulse_ml); pulse_ml = NULL;
- config->speakers_mask = g_phys_speakers_mask; config->modes[0].format = pulse_fmt[0]; config->modes[0].def_period = pulse_def_period[0]; config->modes[0].min_period = pulse_min_period[0]; @@ -2170,6 +2175,12 @@ static NTSTATUS pulse_get_prop_value(void *args) params->vt = VT_UI4; params->ulVal = dev->form; return STATUS_SUCCESS; + case 3: /* PhysicalSpeakers */ + if (!dev->channel_mask) + goto fail; + params->vt = VT_UI4; + params->ulVal = dev->channel_mask; + return STATUS_SUCCESS; default: break; } @@ -2178,6 +2189,7 @@ static NTSTATUS pulse_get_prop_value(void *args) return STATUS_SUCCESS; }
+fail: params->result = E_FAIL; return STATUS_SUCCESS; } diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index 6014dff..74d2c96 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -37,7 +37,6 @@ struct pulse_config REFERENCE_TIME def_period; REFERENCE_TIME min_period; } modes[2]; - unsigned int speakers_mask; };
struct main_loop_params
On Thu, Feb 17, 2022 at 03:55:03PM +0200, Gabriel Ivăncescu wrote:
+struct get_endpoint_ids_params +{
- EDataFlow flow;
- DWORD size;
- WCHAR *device_strings;
- HRESULT result;
- unsigned int num;
+};
As already mentioned, it would be better to try to keep this as close as possible to the existing get_endpoint_ids_params in the coreaudio and alsa drivers. i.e. pass a ptr to a (client allocated) struct endpoint array. Sure, the endpoint struct will be slightly different, but once we have all of the drivers converted it'll make harmonising the interface rather easier.
Huw.
On 17/02/2022 17:23, Huw Davies wrote:
On Thu, Feb 17, 2022 at 03:55:03PM +0200, Gabriel Ivăncescu wrote:
+struct get_endpoint_ids_params +{
- EDataFlow flow;
- DWORD size;
- WCHAR *device_strings;
- HRESULT result;
- unsigned int num;
+};
As already mentioned, it would be better to try to keep this as close as possible to the existing get_endpoint_ids_params in the coreaudio and alsa drivers. i.e. pass a ptr to a (client allocated) struct endpoint array. Sure, the endpoint struct will be slightly different, but once we have all of the drivers converted it'll make harmonising the interface rather easier.
Huw.
Well I used a simple serialization approach where all the strings are concatenated. I have to admit, at least the coreaudio part is a bit confusing. The array holds pointers to the name (I would need two strings per endpoint), but those pointers are never allocated per se.
If I understand it correctly, they're made to point to data beyond the array by the unixlib? I guess it's slightly easier on the PE side but will be a lot more complicated on the unixlib side...
I also have some concerns about Wow64 unixlib, as I tried to avoid output pointers (or VARIANT or other types that are different) as much as possible, because it's different between 32-bit and 64-bit. IIRC Zeb said it's not safe to return pointers.
I know that these pointers just point to the existing array already, but they'd still be larger than what 32-bit PE side expects. Honestly I don't know how *input* pointers would be handled either, since they're used all over the place.
Is there some guidelines for writing unixlib params? I don't even know how the future wow64 is supposed to work, but I certainly don't want to add more weight to whoever ends up converting it (if it does).