Wine-devel
Threads by month
- ----- 2026 -----
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 5 participants
- 84522 discussions
Hello all,
I have a Windows software which tries to access dos device name but it
is failing:
0009:Call KERNEL32.CreateFileA(0060f588
"\\\\.\\d6CDE-0",40000000,00000002,00000000,00000003,00000000,00000000)
ret=00b1117f
0009:Call ntdll.RtlInitAnsiString(0060f49c,0060f588 "\\\\.\\d6CDE-0")
ret=7b036296
0009:Ret ntdll.RtlInitAnsiString() retval=0060f49c ret=7b036296
0009:Call ntdll.RtlAnsiStringToUnicodeString(3fff8bf8,0060f49c,00000000)
ret=7b0362fb
0009:Ret ntdll.RtlAnsiStringToUnicodeString() retval=00000000
ret=7b0362fb
0009:Call ntdll._wcsicmp(3fff8c00 L"\\\\.\\d6CDE-0",7b09d0c0 L"CONIN$")
ret=7b0355c9
0009:Ret ntdll._wcsicmp() retval=fffffff9 ret=7b0355c9
0009:Call ntdll._wcsicmp(3fff8c00 L"\\\\.\\d6CDE-0",7b09d0ce L"CONOUT$")
ret=7b035636
0009:Ret ntdll._wcsicmp() retval=fffffff9 ret=7b035636
0009:Call ntdll.wcsncmp(3fff8c00 L"\\\\.\\d6CDE-0",7b09d644
L"\\\\.\\",00000004) ret=7b035670
0009:Ret ntdll.wcsncmp() retval=00000000 ret=7b035670
0009:Call ntdll.iswalpha(00000064) ret=7b0356cd
0009:Ret ntdll.iswalpha() retval=00000102 ret=7b0356cd
0009:Call ntdll._wcsnicmp(3fff8c08 L"d6CDE-0",7b09d638
L"PIPE\\",00000005) ret=7b0356f5
0009:Ret ntdll._wcsnicmp() retval=fffffff4 ret=7b0356f5
0009:Call ntdll._wcsnicmp(3fff8c08 L"d6CDE-0",7b09d624
L"MAILSLOT\\",00000009) ret=7b03570e
0009:Ret ntdll._wcsnicmp() retval=fffffff7 ret=7b03570e
0009:Call ntdll.RtlIsDosDeviceName_U(3fff8c08 L"d6CDE-0") ret=7b035724
0009:Ret ntdll.RtlIsDosDeviceName_U() retval=00000000 ret=7b035724
0009:Call ntdll.RtlDosPathNameToNtPathName_U(3fff8c00
L"\\\\.\\d6CDE-0",0060f458,00000000,00000000) ret=7b035769
0009:Ret ntdll.RtlDosPathNameToNtPathName_U() retval=00000001
ret=7b035769
0009:Call
ntdll.NtCreateFile(0060f454,40100080,0060f474,0060f460,00000000,00000000,00000002,00000001,00000060,00000000,00000000)
ret=7b03581e
0009:Ret ntdll.NtCreateFile() retval=c0000034 ret=7b03581e
0009:Call ntdll.RtlNtStatusToDosError(c0000034) ret=7b035a11
0009:Ret ntdll.RtlNtStatusToDosError() retval=00000002 ret=7b035a11
0009:Call ntdll.RtlFreeUnicodeString(0060f458) ret=7b03592b
0009:Ret ntdll.RtlFreeUnicodeString() retval=0060f458 ret=7b03592b
0009:Ret KERNEL32.CreateFileA() retval=ffffffff ret=00b1117f
The problem is, that wine doesn't recognize "\\\\.\\d6CDE-0" as valid
DOS device name:
https://github.com/wine-mirror/wine/blob/e909986e6ea5ecd49b2b847f321ad89b2a…
Even when I created the device in HKEY_LOCAL_MACHINE\Software\Wine\Ports
it doesn't allow to open it.
When I patched the corresponding DLL to use COM81 and created the device
in Ports, it works okay (can open the device).
I want to write a patch for ntdll/path.c to recognize my string as DOS
device but I want it to be generic enough to be acceptable.
What approach do you suggest? Can I add a generic match, something like:
"\\\\.\\[^\\]+$" ?
To add some context here, the software is for controlling USB
osclloscope from Hantek:
http://hantek.com/uploadpic/hantek/files/20210731/Hantek-6000_Ver2.2.7_D202…
It installs a driver which creates mentioned "DOS device" and
communicates through it using ioctl.
I am reversing the driver and I'll create a Linux char device to handle
those ioctl calls and talk to the USB device using libusb.
Thanks.
--
S pozdravom / Best regards
Daniel Kucera.
6
30
[PATCH 1/2] kernel32/tests: Add additional Get/SetThreadDescription() tests.
by Brendan Shanks 15 Feb '22
by Brendan Shanks 15 Feb '22
15 Feb '22
Signed-off-by: Brendan Shanks <bshanks(a)codeweavers.com>
---
dlls/kernel32/tests/thread.c | 77 ++++++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c
index 6ccf7af85f8..9c58095fe01 100644
--- a/dlls/kernel32/tests/thread.c
+++ b/dlls/kernel32/tests/thread.c
@@ -100,6 +100,8 @@ static BOOL (WINAPI *pSetThreadGroupAffinity)(HANDLE,const GROUP_AFFINITY*,GROUP
static NTSTATUS (WINAPI *pNtSetInformationThread)(HANDLE,THREADINFOCLASS,LPCVOID,ULONG);
static HRESULT (WINAPI *pSetThreadDescription)(HANDLE,const WCHAR *);
static HRESULT (WINAPI *pGetThreadDescription)(HANDLE,WCHAR **);
+static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG,PVECTORED_EXCEPTION_HANDLER);
+static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID);
static HANDLE create_target_process(const char *arg)
{
@@ -2351,6 +2353,23 @@ static void test_thread_info(void)
CloseHandle(thread);
}
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; /* Must be 0x1000. */
+ LPCSTR szName; /* Pointer to name (in user addr space). */
+ DWORD dwThreadID; /* Thread ID (-1 = caller thread). */
+ DWORD dwFlags; /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+
+static LONG CALLBACK msvc_threadname_vec_handler(EXCEPTION_POINTERS *ExceptionInfo)
+{
+ if (ExceptionInfo->ExceptionRecord != NULL &&
+ ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_NAME_THREAD)
+ return EXCEPTION_CONTINUE_EXECUTION;
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
static void test_thread_description(void)
{
THREAD_NAME_INFORMATION *thread_desc;
@@ -2360,6 +2379,9 @@ static void test_thread_description(void)
char buff[128];
WCHAR *ptr;
HRESULT hr;
+ HANDLE thread;
+ PVOID vectored_handler;
+ THREADNAME_INFO info;
if (!pGetThreadDescription)
{
@@ -2476,6 +2498,59 @@ static void test_thread_description(void)
ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr);
ok(!lstrcmpW(ptr, L""), "Unexpected description %s.\n", wine_dbgstr_w(ptr));
LocalFree(ptr);
+
+ /* Get with only THREAD_QUERY_LIMITED_INFORMATION access. */
+ thread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentThreadId());
+
+ ptr = NULL;
+ hr = pGetThreadDescription(thread, &ptr);
+ ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr);
+ ok(!lstrcmpW(ptr, L""), "Unexpected description %s.\n", wine_dbgstr_w(ptr));
+ LocalFree(ptr);
+
+ len = 0;
+ status = pNtQueryInformationThread(thread, ThreadNameInformation, NULL, 0, &len);
+ ok(status == STATUS_BUFFER_TOO_SMALL, "Unexpected status %#x.\n", status);
+ ok(len == sizeof(*thread_desc), "Unexpected structure length %u.\n", len);
+
+ CloseHandle(thread);
+
+ /* Set with only THREAD_SET_LIMITED_INFORMATION access. */
+ thread = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, GetCurrentThreadId());
+
+ hr = pSetThreadDescription(thread, desc);
+ todo_wine
+ ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr);
+
+ ptr = NULL;
+ hr = pGetThreadDescription(GetCurrentThread(), &ptr);
+ ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr);
+ todo_wine
+ ok(!lstrcmpW(ptr, desc), "Unexpected description %s.\n", wine_dbgstr_w(ptr));
+ LocalFree(ptr);
+
+ CloseHandle(thread);
+
+ /* The old exception-based thread name method should not affect GetThreadDescription. */
+ hr = pSetThreadDescription(GetCurrentThread(), desc);
+ ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr);
+
+ vectored_handler = pRtlAddVectoredExceptionHandler(FALSE, &msvc_threadname_vec_handler);
+ ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
+
+ info.dwType = 0x1000;
+ info.szName = "123";
+ info.dwThreadID = -1;
+ info.dwFlags = 0;
+ RaiseException(EXCEPTION_WINE_NAME_THREAD, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+
+ pRtlRemoveVectoredExceptionHandler(vectored_handler);
+
+ ptr = NULL;
+ hr = pGetThreadDescription(GetCurrentThread(), &ptr);
+ ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr);
+ ok(!lstrcmpW(ptr, desc), "Unexpected description %s.\n", wine_dbgstr_w(ptr));
+ LocalFree(ptr);
}
static void init_funcs(void)
@@ -2524,6 +2599,8 @@ static void init_funcs(void)
X(NtQueryInformationThread);
X(RtlGetThreadErrorMode);
X(NtSetInformationThread);
+ X(RtlAddVectoredExceptionHandler);
+ X(RtlRemoveVectoredExceptionHandler);
}
#undef X
}
--
2.34.1
1
1
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/Makefile.in | 2 +
dlls/winealsa.drv/alsa.c | 458 ++++++++++++++++++++++++++++++++++
dlls/winealsa.drv/mmdevdrv.c | 458 +++++-----------------------------
dlls/winealsa.drv/unixlib.h | 42 ++++
4 files changed, 560 insertions(+), 400 deletions(-)
create mode 100644 dlls/winealsa.drv/alsa.c
create mode 100644 dlls/winealsa.drv/unixlib.h
diff --git a/dlls/winealsa.drv/Makefile.in b/dlls/winealsa.drv/Makefile.in
index 7ce382e64c2..2158e087251 100644
--- a/dlls/winealsa.drv/Makefile.in
+++ b/dlls/winealsa.drv/Makefile.in
@@ -1,5 +1,6 @@
EXTRADEFS = -DWINE_NO_LONG_TYPES
MODULE = winealsa.drv
+UNIXLIB = winealsa.so
IMPORTS = uuid ole32 advapi32
DELAYIMPORTS = winmm
EXTRALIBS = $(ALSA_LIBS)
@@ -7,5 +8,6 @@ EXTRALIBS = $(ALSA_LIBS)
EXTRADLLFLAGS = -mcygwin
C_SRCS = \
+ alsa.c \
midi.c \
mmdevdrv.c
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
new file mode 100644
index 00000000000..ae05c7a8b9b
--- /dev/null
+++ b/dlls/winealsa.drv/alsa.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2010 Maarten Lankhorst for CodeWeavers
+ * Copyright 2011 Andrew Eikum for CodeWeavers
+ * Copyright 2022 Huw Davies
+ *
+ * This library 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.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#if 0
+#pragma makedep unix
+#endif
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <alsa/asoundlib.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "mmdeviceapi.h"
+
+#include "wine/debug.h"
+#include "wine/list.h"
+#include "wine/unixlib.h"
+
+#include "unixlib.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(alsa);
+
+static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
+ 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
+ 'w','i','n','e','a','l','s','a','.','d','r','v'};
+
+static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
+{
+ while (len--) *dst++ = (unsigned char)*src++;
+}
+
+static HKEY reg_open_key( HKEY root, const WCHAR *name, ULONG name_len )
+{
+ UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name };
+ OBJECT_ATTRIBUTES attr;
+ HANDLE ret;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = root;
+ attr.ObjectName = &nameW;
+ attr.Attributes = 0;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return 0;
+ return ret;
+}
+
+static HKEY open_hkcu(void)
+{
+ char buffer[256];
+ WCHAR bufferW[256];
+ DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)];
+ DWORD i, len = sizeof(sid_data);
+ SID *sid;
+
+ if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len ))
+ return 0;
+
+ sid = ((TOKEN_USER *)sid_data)->User.Sid;
+ len = sprintf( buffer, "\\Registry\\User\\S-%u-%u", sid->Revision,
+ MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ),
+ MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] )));
+ for (i = 0; i < sid->SubAuthorityCount; i++)
+ len += sprintf( buffer + len, "-%u", sid->SubAuthority[i] );
+ ascii_to_unicode( bufferW, buffer, len + 1 );
+
+ return reg_open_key( NULL, bufferW, len * sizeof(WCHAR) );
+}
+
+static HKEY reg_open_hkcu_key( const WCHAR *name, ULONG name_len )
+{
+ HKEY hkcu = open_hkcu(), key;
+
+ key = reg_open_key( hkcu, name, name_len );
+ NtClose( hkcu );
+
+ return key;
+}
+
+ULONG reg_query_value( HKEY hkey, const WCHAR *name,
+ KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
+{
+ unsigned int name_size = name ? wcslen( name ) * sizeof(WCHAR) : 0;
+ UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name };
+
+ if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
+ info, size, &size ))
+ return 0;
+
+ return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
+}
+
+static snd_pcm_stream_t alsa_get_direction(EDataFlow flow)
+{
+ return (flow == eRender) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
+}
+
+static WCHAR *strdupAtoW(const char *str)
+{
+ unsigned int len;
+ WCHAR *ret;
+
+ if(!str) return NULL;
+
+ len = strlen(str) + 1;
+ ret = malloc(len * sizeof(WCHAR));
+ if(ret) ntdll_umbstowcs(str, len, ret, len);
+ return ret;
+}
+
+static BOOL alsa_try_open(const char *devnode, EDataFlow flow)
+{
+ snd_pcm_t *handle;
+ int err;
+
+ TRACE("devnode: %s, flow: %d\n", devnode, flow);
+
+ if((err = snd_pcm_open(&handle, devnode, alsa_get_direction(flow), SND_PCM_NONBLOCK)) < 0){
+ WARN("The device \"%s\" failed to open: %d (%s).\n", devnode, err, snd_strerror(err));
+ return FALSE;
+ }
+
+ snd_pcm_close(handle);
+ return TRUE;
+}
+
+static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const WCHAR *chunk2)
+{
+ WCHAR *ret;
+ const WCHAR *prefix;
+ size_t len_wchars = 0, chunk1_len = 0, chunk2_len = 0, copied = 0, prefix_len;
+
+ static const WCHAR dashW[] = {' ','-',' ',0};
+ static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
+ static const WCHAR outW[] = {'O','u','t',':',' ',0};
+ static const WCHAR inW[] = {'I','n',':',' ',0};
+
+ if(flow == eRender){
+ prefix = outW;
+ prefix_len = ARRAY_SIZE(outW) - 1;
+ len_wchars += prefix_len;
+ }else{
+ prefix = inW;
+ prefix_len = ARRAY_SIZE(inW) - 1;
+ len_wchars += prefix_len;
+ }
+ if(chunk1){
+ chunk1_len = wcslen(chunk1);
+ len_wchars += chunk1_len;
+ }
+ if(chunk1 && chunk2)
+ len_wchars += dashW_len;
+ if(chunk2){
+ chunk2_len = wcslen(chunk2);
+ len_wchars += chunk2_len;
+ }
+ len_wchars += 1; /* NULL byte */
+
+ ret = malloc(len_wchars * sizeof(WCHAR));
+
+ memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
+ copied += prefix_len;
+ if(chunk1){
+ memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
+ copied += chunk1_len;
+ }
+ if(chunk1 && chunk2){
+ memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
+ copied += dashW_len;
+ }
+ if(chunk2){
+ memcpy(ret + copied, chunk2, chunk2_len * sizeof(WCHAR));
+ copied += chunk2_len;
+ }
+ ret[copied] = 0;
+
+ TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
+
+ return ret;
+}
+
+struct endpoints_info
+{
+ unsigned int num, size;
+ struct endpoint *endpoints;
+};
+
+static void endpoints_add(struct endpoints_info *endpoints, WCHAR *name, char *device)
+{
+ if(endpoints->num >= endpoints->size){
+ if (!endpoints->size) endpoints->size = 16;
+ else endpoints->size *= 2;
+ endpoints->endpoints = realloc(endpoints->endpoints, endpoints->size * sizeof(*endpoints->endpoints));
+ }
+
+ endpoints->endpoints[endpoints->num].name = name;
+ endpoints->endpoints[endpoints->num++].device = device;
+}
+
+static HRESULT alsa_get_card_devices(EDataFlow flow, struct endpoints_info *endpoints_info,
+ snd_ctl_t *ctl, int card, const WCHAR *cardname)
+{
+ int err, device;
+ snd_pcm_info_t *info;
+
+ info = calloc(1, snd_pcm_info_sizeof());
+ if(!info)
+ return E_OUTOFMEMORY;
+
+ snd_pcm_info_set_subdevice(info, 0);
+ snd_pcm_info_set_stream(info, alsa_get_direction(flow));
+
+ device = -1;
+ for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
+ err = snd_ctl_pcm_next_device(ctl, &device)){
+ char devnode[32];
+ WCHAR *devname;
+
+ snd_pcm_info_set_device(info, device);
+
+ if((err = snd_ctl_pcm_info(ctl, info)) < 0){
+ if(err == -ENOENT)
+ /* This device doesn't have the right stream direction */
+ continue;
+
+ WARN("Failed to get info for card %d, device %d: %d (%s)\n",
+ card, device, err, snd_strerror(err));
+ continue;
+ }
+
+ sprintf(devnode, "plughw:%d,%d", card, device);
+ if(!alsa_try_open(devnode, flow))
+ continue;
+
+ devname = strdupAtoW(snd_pcm_info_get_name(info));
+ if(!devname){
+ WARN("Unable to get device name for card %d, device %d\n", card, device);
+ continue;
+ }
+
+ endpoints_add(endpoints_info, construct_device_id(flow, cardname, devname), strdup(devnode));
+ free(devname);
+ }
+
+ free(info);
+
+ if(err != 0)
+ WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
+ card, err, snd_strerror(err));
+
+ return S_OK;
+}
+
+static void get_reg_devices(EDataFlow flow, struct endpoints_info *endpoints_info)
+{
+ static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
+ static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
+ char buffer[4096];
+ KEY_VALUE_PARTIAL_INFORMATION *key_info = (void *)buffer;
+ HKEY key;
+ DWORD size;
+ const WCHAR *value_name = (flow == eRender) ? ALSAOutputDevices : ALSAInputDevices;
+
+ /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
+ if((key = reg_open_hkcu_key(drv_keyW, sizeof(drv_keyW)))){
+ if((size = reg_query_value(key, value_name, key_info, sizeof(buffer)))){
+ WCHAR *p = (WCHAR *)key_info->Data;
+
+ if(key_info->Type != REG_MULTI_SZ){
+ ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
+ NtClose(key);
+ return;
+ }
+
+ while(*p){
+ int len = wcslen(p);
+ char *devname = malloc(len * 3 + 1);
+
+ ntdll_wcstoumbs(p, len + 1, devname, len * 3 + 1, FALSE);
+
+ if(alsa_try_open(devname, flow))
+ endpoints_add(endpoints_info, construct_device_id(flow, p, NULL), strdup(devname));
+
+ free(devname);
+ p += len + 1;
+ }
+ }
+
+ NtClose(key);
+ }
+}
+
+struct card_type {
+ struct list entry;
+ int first_card_number;
+ char string[1];
+};
+
+static struct list card_types = LIST_INIT(card_types);
+
+static BOOL need_card_number(int card, const char *string)
+{
+ struct card_type *cptr;
+
+ LIST_FOR_EACH_ENTRY(cptr, &card_types, struct card_type, entry)
+ {
+ if(!strcmp(string, cptr->string))
+ return card != cptr->first_card_number;
+ }
+
+ /* this is the first instance of string */
+ cptr = malloc(sizeof(struct card_type) + strlen(string));
+ if(!cptr)
+ /* Default to displaying card number if we can't track cards */
+ return TRUE;
+
+ cptr->first_card_number = card;
+ strcpy(cptr->string, string);
+ list_add_head(&card_types, &cptr->entry);
+ return FALSE;
+}
+
+static WCHAR *alsa_get_card_name(int card)
+{
+ char *cardname;
+ WCHAR *ret;
+ int err;
+
+ if((err = snd_card_get_name(card, &cardname)) < 0){
+ /* FIXME: Should be localized */
+ WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card, err, snd_strerror(err));
+ cardname = strdup("Unknown soundcard");
+ }
+
+ if(need_card_number(card, cardname)){
+ char *cardnameN;
+ /*
+ * For identical card names, second and subsequent instances get
+ * card number prefix to distinguish them (like Windows).
+ */
+ if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
+ free(cardname);
+ cardname = cardnameN;
+ }
+ }
+
+ ret = strdupAtoW(cardname);
+ free(cardname);
+
+ return ret;
+}
+
+static NTSTATUS get_endpoint_ids(void *args)
+{
+ static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
+ struct get_endpoint_ids_params *params = args;
+ struct endpoints_info endpoints_info;
+ unsigned int i, needed, name_len, device_len;
+ struct endpoint *endpoint;
+ int err, card;
+ char *ptr;
+
+ card = -1;
+
+ endpoints_info.num = endpoints_info.size = 0;
+ endpoints_info.endpoints = NULL;
+
+ if(alsa_try_open("default", params->flow))
+ endpoints_add(&endpoints_info, construct_device_id(params->flow, defaultW, NULL), strdup("default"));
+
+ get_reg_devices(params->flow, &endpoints_info);
+
+ for(err = snd_card_next(&card); card != -1 && err >= 0; err = snd_card_next(&card)){
+ char cardpath[64];
+ WCHAR *cardname;
+ snd_ctl_t *ctl;
+
+ sprintf(cardpath, "hw:%u", card);
+
+ if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
+ WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
+ err, snd_strerror(err));
+ continue;
+ }
+
+ cardname = alsa_get_card_name(card);
+ alsa_get_card_devices(params->flow, &endpoints_info, ctl, card, cardname);
+ free(cardname);
+
+ snd_ctl_close(ctl);
+ }
+
+ if(err != 0)
+ WARN("Got a failure during card enumeration: %d (%s)\n", err, snd_strerror(err));
+
+ needed = endpoints_info.num * sizeof(*params->endpoints);
+ endpoint = params->endpoints;
+ ptr = (char *)(endpoint + endpoints_info.num);
+
+ for(i = 0; i < endpoints_info.num; i++){
+ name_len = wcslen(endpoints_info.endpoints[i].name) + 1;
+ device_len = strlen(endpoints_info.endpoints[i].device) + 1;
+ needed += name_len * sizeof(WCHAR) + ((device_len + 1) & ~1);
+
+ if(needed <= params->size){
+ endpoint->name = (WCHAR *)ptr;
+ memcpy(endpoint->name, endpoints_info.endpoints[i].name, name_len * sizeof(WCHAR));
+ ptr += name_len * sizeof(WCHAR);
+ endpoint->device = ptr;
+ memcpy(endpoint->device, endpoints_info.endpoints[i].device, device_len);
+ ptr += (device_len + 1) & ~1;
+ endpoint++;
+ }
+ free(endpoints_info.endpoints[i].name);
+ free(endpoints_info.endpoints[i].device);
+ }
+ free(endpoints_info.endpoints);
+
+ params->num = endpoints_info.num;
+ params->default_idx = 0;
+
+ if(needed > params->size){
+ params->size = needed;
+ params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ } else
+ params->result = S_OK;
+
+ return STATUS_SUCCESS;
+}
+
+unixlib_entry_t __wine_unix_call_funcs[] =
+{
+ get_endpoint_ids,
+};
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 9a8f80a7beb..516bba1e5fc 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -1,6 +1,7 @@
/*
* Copyright 2010 Maarten Lankhorst for CodeWeavers
* Copyright 2011 Andrew Eikum for CodeWeavers
+ * Copyright 2022 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -32,6 +33,7 @@
#include "wine/debug.h"
#include "wine/unicode.h"
#include "wine/list.h"
+#include "wine/unixlib.h"
#include "propsys.h"
#include "initguid.h"
@@ -49,8 +51,12 @@
#include <alsa/asoundlib.h>
+#include "unixlib.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
+unixlib_handle_t alsa_handle = 0;
+
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
static const REFERENCE_TIME DefaultPeriod = 100000;
@@ -161,12 +167,6 @@ static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
static struct list g_sessions = LIST_INIT(g_sessions);
-static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
-static const char defname[] = "default";
-
-static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
- 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
- 'w','i','n','e','a','l','s','a','.','d','r','v'};
static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
@@ -240,6 +240,9 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
switch (reason)
{
case DLL_PROCESS_ATTACH:
+ if(NtQueryVirtualMemory(GetCurrentProcess(), dll, MemoryWineUnixFuncs,
+ &alsa_handle, sizeof(alsa_handle), NULL))
+ return FALSE;
g_timer_q = CreateTimerQueue();
if(!g_timer_q)
return FALSE;
@@ -266,19 +269,6 @@ int WINAPI AUDDRV_GetPriority(void)
return Priority_Neutral;
}
-static WCHAR *strdupAtoW(const char *str)
-{
- unsigned int len;
- WCHAR *ret;
-
- if(!str) return NULL;
-
- len = MultiByteToWideChar(CP_UNIXCP, 0, str, -1, NULL, 0);
- ret = malloc(len * sizeof(WCHAR));
- if(ret) MultiByteToWideChar(CP_UNIXCP, 0, str, -1, ret, len);
- return ret;
-}
-
static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
GUID *guid)
{
@@ -351,399 +341,67 @@ static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
RegCloseKey(key);
}
-static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
-{
- while (len--) *dst++ = (unsigned char)*src++;
-}
-
-static HKEY reg_open_key( HKEY root, const WCHAR *name, ULONG name_len )
-{
- UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name };
- OBJECT_ATTRIBUTES attr;
- HANDLE ret;
-
- attr.Length = sizeof(attr);
- attr.RootDirectory = root;
- attr.ObjectName = &nameW;
- attr.Attributes = 0;
- attr.SecurityDescriptor = NULL;
- attr.SecurityQualityOfService = NULL;
-
- if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return 0;
- return ret;
-}
-
-static HKEY open_hkcu(void)
-{
- char buffer[256];
- WCHAR bufferW[256];
- DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)];
- DWORD i, len = sizeof(sid_data);
- SID *sid;
-
- if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len ))
- return 0;
-
- sid = ((TOKEN_USER *)sid_data)->User.Sid;
- len = sprintf( buffer, "\\Registry\\User\\S-%u-%u", sid->Revision,
- MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ),
- MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] )));
- for (i = 0; i < sid->SubAuthorityCount; i++)
- len += sprintf( buffer + len, "-%u", sid->SubAuthority[i] );
- ascii_to_unicode( bufferW, buffer, len + 1 );
-
- return reg_open_key( NULL, bufferW, len * sizeof(WCHAR) );
-}
-
-static HKEY reg_open_hkcu_key( const WCHAR *name, ULONG name_len )
-{
- HKEY hkcu = open_hkcu(), key;
-
- key = reg_open_key( hkcu, name, name_len );
- NtClose( hkcu );
-
- return key;
-}
-
-ULONG reg_query_value( HKEY hkey, const WCHAR *name,
- KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
-{
- unsigned int name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0;
- UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name };
-
- if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
- info, size, &size ))
- return 0;
-
- return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
-}
-
static snd_pcm_stream_t alsa_get_direction(EDataFlow flow)
{
return (flow == eRender) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
}
-static BOOL alsa_try_open(const char *devnode, EDataFlow flow)
-{
- snd_pcm_t *handle;
- int err;
-
- TRACE("devnode: %s, flow: %d\n", devnode, flow);
-
- if((err = snd_pcm_open(&handle, devnode, alsa_get_direction(flow), SND_PCM_NONBLOCK)) < 0){
- WARN("The device \"%s\" failed to open: %d (%s).\n", devnode, err, snd_strerror(err));
- return FALSE;
- }
-
- snd_pcm_close(handle);
- return TRUE;
-}
-
-static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const WCHAR *chunk2)
-{
- WCHAR *ret;
- const WCHAR *prefix;
- size_t len_wchars = 0, chunk1_len = 0, chunk2_len = 0, copied = 0, prefix_len;
-
- static const WCHAR dashW[] = {' ','-',' ',0};
- static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
- static const WCHAR outW[] = {'O','u','t',':',' ',0};
- static const WCHAR inW[] = {'I','n',':',' ',0};
-
- if(flow == eRender){
- prefix = outW;
- prefix_len = ARRAY_SIZE(outW) - 1;
- len_wchars += prefix_len;
- }else{
- prefix = inW;
- prefix_len = ARRAY_SIZE(inW) - 1;
- len_wchars += prefix_len;
- }
- if(chunk1){
- chunk1_len = strlenW(chunk1);
- len_wchars += chunk1_len;
- }
- if(chunk1 && chunk2)
- len_wchars += dashW_len;
- if(chunk2){
- chunk2_len = strlenW(chunk2);
- len_wchars += chunk2_len;
- }
- len_wchars += 1; /* NULL byte */
-
- ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
-
- memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
- copied += prefix_len;
- if(chunk1){
- memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
- copied += chunk1_len;
- }
- if(chunk1 && chunk2){
- memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
- copied += dashW_len;
- }
- if(chunk2){
- memcpy(ret + copied, chunk2, chunk2_len * sizeof(WCHAR));
- copied += chunk2_len;
- }
- ret[copied] = 0;
-
- TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
-
- return ret;
-}
-
-static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num,
- snd_ctl_t *ctl, int card, const WCHAR *cardname)
-{
- int err, device;
- snd_pcm_info_t *info;
-
- info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
- if(!info)
- return E_OUTOFMEMORY;
-
- snd_pcm_info_set_subdevice(info, 0);
- snd_pcm_info_set_stream(info, alsa_get_direction(flow));
-
- device = -1;
- for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
- err = snd_ctl_pcm_next_device(ctl, &device)){
- char devnode[32];
- WCHAR *devname;
-
- snd_pcm_info_set_device(info, device);
-
- if((err = snd_ctl_pcm_info(ctl, info)) < 0){
- if(err == -ENOENT)
- /* This device doesn't have the right stream direction */
- continue;
-
- WARN("Failed to get info for card %d, device %d: %d (%s)\n",
- card, device, err, snd_strerror(err));
- continue;
- }
-
- sprintf(devnode, "plughw:%d,%d", card, device);
- if(!alsa_try_open(devnode, flow))
- continue;
-
- if(*num){
- *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
- *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
- }else{
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
- *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
- }
-
- devname = strdupAtoW(snd_pcm_info_get_name(info));
- if(!devname){
- WARN("Unable to get device name for card %d, device %d\n", card, device);
- continue;
- }
-
- (*ids)[*num] = construct_device_id(flow, cardname, devname);
- get_device_guid(flow, devnode, &(*guids)[*num]);
- free(devname);
-
- ++(*num);
- }
-
- HeapFree(GetProcessHeap(), 0, info);
-
- if(err != 0)
- WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
- card, err, snd_strerror(err));
-
- return S_OK;
-}
-
-static void get_reg_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num)
-{
- static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
- static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
- char buffer[4096];
- KEY_VALUE_PARTIAL_INFORMATION *key_info = (void *)buffer;
- HKEY key;
- DWORD size;
- const WCHAR *value_name = (flow == eRender) ? ALSAOutputDevices : ALSAInputDevices;
-
- /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
- if((key = reg_open_hkcu_key(drv_keyW, sizeof(drv_keyW)))){
- if((size = reg_query_value(key, value_name, key_info, sizeof(buffer)))){
- WCHAR *p = (WCHAR *)key_info->Data;
-
- if(key_info->Type != REG_MULTI_SZ){
- ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
- NtClose(key);
- return;
- }
-
- while(*p){
- int len = lstrlenW(p);
- char *devname = malloc(len * 3 + 1);
-
- WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, len * 3 + 1, NULL, NULL);
-
- if(alsa_try_open(devname, flow)){
- if(*num){
- *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
- *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
- }else{
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
- *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
- }
- (*ids)[*num] = construct_device_id(flow, p, NULL);
- get_device_guid(flow, devname, &(*guids)[*num]);
- ++*num;
- }
- free(devname);
- p += len + 1;
- }
- }
-
- NtClose(key);
- }
-}
-
-struct card_type {
- struct list entry;
- int first_card_number;
- char string[1];
-};
-
-static struct list card_types = LIST_INIT(card_types);
-
-static BOOL need_card_number(int card, const char *string)
-{
- struct card_type *cptr;
-
- LIST_FOR_EACH_ENTRY(cptr, &card_types, struct card_type, entry)
- {
- if(!strcmp(string, cptr->string))
- return card != cptr->first_card_number;
- }
-
- /* this is the first instance of string */
- cptr = HeapAlloc(GetProcessHeap(), 0, sizeof(struct card_type) + strlen(string));
- if(!cptr)
- /* Default to displaying card number if we can't track cards */
- return TRUE;
-
- cptr->first_card_number = card;
- strcpy(cptr->string, string);
- list_add_head(&card_types, &cptr->entry);
- return FALSE;
-}
-
-static WCHAR *alsa_get_card_name(int card)
-{
- char *cardname;
- WCHAR *ret;
- int err;
-
- if((err = snd_card_get_name(card, &cardname)) < 0){
- /* FIXME: Should be localized */
- WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card, err, snd_strerror(err));
- cardname = strdup("Unknown soundcard");
- }
-
- if(need_card_number(card, cardname)){
- char *cardnameN;
- /*
- * For identical card names, second and subsequent instances get
- * card number prefix to distinguish them (like Windows).
- */
- if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
- free(cardname);
- cardname = cardnameN;
- }
- }
-
- ret = strdupAtoW(cardname);
- free(cardname);
-
- return ret;
-}
-
-static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
- UINT *num)
-{
- int err, card;
-
- card = -1;
- *num = 0;
-
- if(alsa_try_open(defname, flow)){
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
- (*ids)[0] = construct_device_id(flow, defaultW, NULL);
- *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
- get_device_guid(flow, defname, &(*guids)[0]);
- ++*num;
- }
-
- get_reg_devices(flow, ids, guids, num);
-
- for(err = snd_card_next(&card); card != -1 && err >= 0; err = snd_card_next(&card)){
- char cardpath[64];
- WCHAR *cardname;
- snd_ctl_t *ctl;
-
- sprintf(cardpath, "hw:%u", card);
-
- if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
- WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
- err, snd_strerror(err));
- continue;
- }
-
- cardname = alsa_get_card_name(card);
- alsa_get_card_devices(flow, ids, guids, num, ctl, card, cardname);
- free(cardname);
-
- snd_ctl_close(ctl);
- }
-
- if(err != 0)
- WARN("Got a failure during card enumeration: %d (%s)\n",
- err, snd_strerror(err));
-
- return S_OK;
-}
-
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **guids_out,
UINT *num, UINT *def_index)
{
- HRESULT hr;
+ struct get_endpoint_ids_params params;
+ unsigned int i;
+ GUID *guids = NULL;
+ WCHAR **ids = NULL;
TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
- *ids = NULL;
- *guids = NULL;
-
- hr = alsa_enum_devices(flow, ids, guids, num);
- if(FAILED(hr)){
- UINT i;
- for(i = 0; i < *num; ++i)
- HeapFree(GetProcessHeap(), 0, (*ids)[i]);
- HeapFree(GetProcessHeap(), 0, *ids);
- HeapFree(GetProcessHeap(), 0, *guids);
- return E_OUTOFMEMORY;
- }
-
- TRACE("Enumerated %u devices\n", *num);
-
- if(*num == 0){
- HeapFree(GetProcessHeap(), 0, *ids);
- *ids = NULL;
- HeapFree(GetProcessHeap(), 0, *guids);
- *guids = NULL;
+ params.flow = flow;
+ params.size = 1000;
+ params.endpoints = NULL;
+ do{
+ HeapFree(GetProcessHeap(), 0, params.endpoints);
+ params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
+ ALSA_CALL(get_endpoint_ids, ¶ms);
+ }while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
+
+ if(FAILED(params.result)) goto end;
+
+ ids = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, params.num * sizeof(*ids));
+ guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
+ if(!ids || !guids){
+ params.result = E_OUTOFMEMORY;
+ goto end;
+ }
+
+ for(i = 0; i < params.num; i++){
+ unsigned int size = (strlenW(params.endpoints[i].name) + 1) * sizeof(WCHAR);
+ ids[i] = HeapAlloc(GetProcessHeap(), 0, size);
+ if(!ids[i]){
+ params.result = E_OUTOFMEMORY;
+ goto end;
+ }
+ memcpy(ids[i], params.endpoints[i].name, size);
+ get_device_guid(flow, params.endpoints[i].device, guids + i);
+ }
+ *def_index = params.default_idx;
+
+end:
+ HeapFree(GetProcessHeap(), 0, params.endpoints);
+ if(FAILED(params.result)){
+ HeapFree(GetProcessHeap(), 0, guids);
+ if(ids){
+ for(i = 0; i < params.num; i++)
+ HeapFree(GetProcessHeap(), 0, ids[i]);
+ HeapFree(GetProcessHeap(), 0, ids);
+ }
+ }else{
+ *ids_out = ids;
+ *guids_out = guids;
+ *num = params.num;
}
- *def_index = 0;
-
- return S_OK;
+ return params.result;
}
static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
new file mode 100644
index 00000000000..f3014d0b448
--- /dev/null
+++ b/dlls/winealsa.drv/unixlib.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 Huw Davies
+ *
+ * This library 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.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+struct endpoint
+{
+ WCHAR *name;
+ char *device;
+};
+
+struct get_endpoint_ids_params
+{
+ EDataFlow flow;
+ struct endpoint *endpoints;
+ unsigned int size;
+ HRESULT result;
+ unsigned int num;
+ unsigned int default_idx;
+};
+
+enum alsa_funcs
+{
+ alsa_get_endpoint_ids,
+};
+
+extern unixlib_handle_t alsa_handle;
+
+#define ALSA_CALL(func, params) __wine_unix_call(alsa_handle, alsa_ ## func, params)
--
2.25.1
2
1
15 Feb '22
This code will move to the unixlib.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/mmdevdrv.c | 96 ++++++++++++++++++++++++++++++------
1 file changed, 82 insertions(+), 14 deletions(-)
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index a6ca3827e5f..9a8f80a7beb 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -166,7 +166,7 @@ static const char defname[] = "default";
static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
- 'w','i','n','e','a','l','s','a','.','d','r','v',0};
+ 'w','i','n','e','a','l','s','a','.','d','r','v'};
static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
@@ -351,6 +351,73 @@ static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
RegCloseKey(key);
}
+static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
+{
+ while (len--) *dst++ = (unsigned char)*src++;
+}
+
+static HKEY reg_open_key( HKEY root, const WCHAR *name, ULONG name_len )
+{
+ UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name };
+ OBJECT_ATTRIBUTES attr;
+ HANDLE ret;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = root;
+ attr.ObjectName = &nameW;
+ attr.Attributes = 0;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+
+ if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return 0;
+ return ret;
+}
+
+static HKEY open_hkcu(void)
+{
+ char buffer[256];
+ WCHAR bufferW[256];
+ DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)];
+ DWORD i, len = sizeof(sid_data);
+ SID *sid;
+
+ if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len ))
+ return 0;
+
+ sid = ((TOKEN_USER *)sid_data)->User.Sid;
+ len = sprintf( buffer, "\\Registry\\User\\S-%u-%u", sid->Revision,
+ MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ),
+ MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] )));
+ for (i = 0; i < sid->SubAuthorityCount; i++)
+ len += sprintf( buffer + len, "-%u", sid->SubAuthority[i] );
+ ascii_to_unicode( bufferW, buffer, len + 1 );
+
+ return reg_open_key( NULL, bufferW, len * sizeof(WCHAR) );
+}
+
+static HKEY reg_open_hkcu_key( const WCHAR *name, ULONG name_len )
+{
+ HKEY hkcu = open_hkcu(), key;
+
+ key = reg_open_key( hkcu, name, name_len );
+ NtClose( hkcu );
+
+ return key;
+}
+
+ULONG reg_query_value( HKEY hkey, const WCHAR *name,
+ KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
+{
+ unsigned int name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0;
+ UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name };
+
+ if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
+ info, size, &size ))
+ return 0;
+
+ return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
+}
+
static snd_pcm_stream_t alsa_get_direction(EDataFlow flow)
{
return (flow == eRender) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
@@ -496,27 +563,28 @@ static void get_reg_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *nu
{
static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
+ char buffer[4096];
+ KEY_VALUE_PARTIAL_INFORMATION *key_info = (void *)buffer;
HKEY key;
- WCHAR reg_devices[256];
- DWORD size = sizeof(reg_devices), type;
+ DWORD size;
const WCHAR *value_name = (flow == eRender) ? ALSAOutputDevices : ALSAInputDevices;
/* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
- if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
- if(RegQueryValueExW(key, value_name, 0, &type,
- (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
- WCHAR *p = reg_devices;
+ if((key = reg_open_hkcu_key(drv_keyW, sizeof(drv_keyW)))){
+ if((size = reg_query_value(key, value_name, key_info, sizeof(buffer)))){
+ WCHAR *p = (WCHAR *)key_info->Data;
- if(type != REG_MULTI_SZ){
+ if(key_info->Type != REG_MULTI_SZ){
ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
- RegCloseKey(key);
+ NtClose(key);
return;
}
while(*p){
- char devname[64];
+ int len = lstrlenW(p);
+ char *devname = malloc(len * 3 + 1);
- WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
+ WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, len * 3 + 1, NULL, NULL);
if(alsa_try_open(devname, flow)){
if(*num){
@@ -530,12 +598,12 @@ static void get_reg_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *nu
get_device_guid(flow, devname, &(*guids)[*num]);
++*num;
}
-
- p += lstrlenW(p) + 1;
+ free(devname);
+ p += len + 1;
}
}
- RegCloseKey(key);
+ NtClose(key);
}
}
--
2.25.1
2
1
15 Feb '22
This makes construct_device_id() rather more sane.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/mmdevdrv.c | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 534667040a2..a6ca3827e5f 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -372,11 +372,11 @@ static BOOL alsa_try_open(const char *devnode, EDataFlow flow)
return TRUE;
}
-static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
+static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const WCHAR *chunk2)
{
WCHAR *ret;
const WCHAR *prefix;
- DWORD len_wchars = 0, chunk1_len = 0, copied = 0, prefix_len;
+ size_t len_wchars = 0, chunk1_len = 0, chunk2_len = 0, copied = 0, prefix_len;
static const WCHAR dashW[] = {' ','-',' ',0};
static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
@@ -398,8 +398,10 @@ static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const cha
}
if(chunk1 && chunk2)
len_wchars += dashW_len;
- if(chunk2)
- len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
+ if(chunk2){
+ chunk2_len = strlenW(chunk2);
+ len_wchars += chunk2_len;
+ }
len_wchars += 1; /* NULL byte */
ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
@@ -415,9 +417,10 @@ static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const cha
copied += dashW_len;
}
if(chunk2){
- MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
- }else
- ret[copied] = 0;
+ memcpy(ret + copied, chunk2, chunk2_len * sizeof(WCHAR));
+ copied += chunk2_len;
+ }
+ ret[copied] = 0;
TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
@@ -425,7 +428,7 @@ static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const cha
}
static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num,
- snd_ctl_t *ctl, int card, const WCHAR *cardnameW)
+ snd_ctl_t *ctl, int card, const WCHAR *cardname)
{
int err, device;
snd_pcm_info_t *info;
@@ -440,8 +443,8 @@ static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
device = -1;
for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
err = snd_ctl_pcm_next_device(ctl, &device)){
- const char *devname;
char devnode[32];
+ WCHAR *devname;
snd_pcm_info_set_device(info, device);
@@ -467,15 +470,15 @@ static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
*guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
}
- devname = snd_pcm_info_get_name(info);
+ devname = strdupAtoW(snd_pcm_info_get_name(info));
if(!devname){
- WARN("Unable to get device name for card %d, device %d\n", card,
- device);
+ WARN("Unable to get device name for card %d, device %d\n", card, device);
continue;
}
- (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
+ (*ids)[*num] = construct_device_id(flow, cardname, devname);
get_device_guid(flow, devnode, &(*guids)[*num]);
+ free(devname);
++(*num);
}
--
2.25.1
2
1
15 Feb '22
This is primarily to simplify the code. It does however add name
collision detection if there are two unknown cards.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/mmdevdrv.c | 86 ++++++++++++++++++++----------------
1 file changed, 48 insertions(+), 38 deletions(-)
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 6accd1bab4f..534667040a2 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -266,6 +266,19 @@ int WINAPI AUDDRV_GetPriority(void)
return Priority_Neutral;
}
+static WCHAR *strdupAtoW(const char *str)
+{
+ unsigned int len;
+ WCHAR *ret;
+
+ if(!str) return NULL;
+
+ len = MultiByteToWideChar(CP_UNIXCP, 0, str, -1, NULL, 0);
+ ret = malloc(len * sizeof(WCHAR));
+ if(ret) MultiByteToWideChar(CP_UNIXCP, 0, str, -1, ret, len);
+ return ret;
+}
+
static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
GUID *guid)
{
@@ -553,6 +566,36 @@ static BOOL need_card_number(int card, const char *string)
return FALSE;
}
+static WCHAR *alsa_get_card_name(int card)
+{
+ char *cardname;
+ WCHAR *ret;
+ int err;
+
+ if((err = snd_card_get_name(card, &cardname)) < 0){
+ /* FIXME: Should be localized */
+ WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card, err, snd_strerror(err));
+ cardname = strdup("Unknown soundcard");
+ }
+
+ if(need_card_number(card, cardname)){
+ char *cardnameN;
+ /*
+ * For identical card names, second and subsequent instances get
+ * card number prefix to distinguish them (like Windows).
+ */
+ if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
+ free(cardname);
+ cardname = cardnameN;
+ }
+ }
+
+ ret = strdupAtoW(cardname);
+ free(cardname);
+
+ return ret;
+}
+
static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
UINT *num)
{
@@ -571,13 +614,10 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
get_reg_devices(flow, ids, guids, num);
- for(err = snd_card_next(&card); card != -1 && err >= 0;
- err = snd_card_next(&card)){
+ for(err = snd_card_next(&card); card != -1 && err >= 0; err = snd_card_next(&card)){
char cardpath[64];
- char *cardname;
- WCHAR *cardnameW;
+ WCHAR *cardname;
snd_ctl_t *ctl;
- DWORD len;
sprintf(cardpath, "hw:%u", card);
@@ -587,39 +627,9 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
continue;
}
- if(snd_card_get_name(card, &cardname) < 0) {
- /* FIXME: Should be localized */
- static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
- WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
- cardpath, err, snd_strerror(err));
- alsa_get_card_devices(flow, ids, guids, num, ctl, card, nameW);
- }else{
- if(need_card_number(card, cardname)){
- char *cardnameN;
- /*
- * For identical card names, second and subsequent instances get
- * card number prefix to distinguish them (like Windows).
- */
- if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
- free(cardname);
- cardname = cardnameN;
- }
- }
- len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
- cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
-
- if(!cardnameW){
- free(cardname);
- snd_ctl_close(ctl);
- return E_OUTOFMEMORY;
- }
- MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
-
- alsa_get_card_devices(flow, ids, guids, num, ctl, card, cardnameW);
-
- HeapFree(GetProcessHeap(), 0, cardnameW);
- free(cardname);
- }
+ cardname = alsa_get_card_name(card);
+ alsa_get_card_devices(flow, ids, guids, num, ctl, card, cardname);
+ free(cardname);
snd_ctl_close(ctl);
}
--
2.25.1
2
1
Part of the motivation for this patch is to eliminate variables
named "stream", in order to avoid confusion with audio streams.
It also avoids cases where both "flow" and "stream" are passed to
the same function, conveying the same information.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/mmdevdrv.c | 55 ++++++++++++++++--------------------
1 file changed, 24 insertions(+), 31 deletions(-)
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 9d051796765..6accd1bab4f 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -338,16 +338,20 @@ static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
RegCloseKey(key);
}
-static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
+static snd_pcm_stream_t alsa_get_direction(EDataFlow flow)
+{
+ return (flow == eRender) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
+}
+
+static BOOL alsa_try_open(const char *devnode, EDataFlow flow)
{
snd_pcm_t *handle;
int err;
- TRACE("devnode: %s, stream: %d\n", devnode, stream);
+ TRACE("devnode: %s, flow: %d\n", devnode, flow);
- if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
- WARN("The device \"%s\" failed to open: %d (%s).\n",
- devnode, err, snd_strerror(err));
+ if((err = snd_pcm_open(&handle, devnode, alsa_get_direction(flow), SND_PCM_NONBLOCK)) < 0){
+ WARN("The device \"%s\" failed to open: %d (%s).\n", devnode, err, snd_strerror(err));
return FALSE;
}
@@ -407,9 +411,8 @@ static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const cha
return ret;
}
-static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
- WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
- const WCHAR *cardnameW)
+static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num,
+ snd_ctl_t *ctl, int card, const WCHAR *cardnameW)
{
int err, device;
snd_pcm_info_t *info;
@@ -419,7 +422,7 @@ static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
return E_OUTOFMEMORY;
snd_pcm_info_set_subdevice(info, 0);
- snd_pcm_info_set_stream(info, stream);
+ snd_pcm_info_set_stream(info, alsa_get_direction(flow));
device = -1;
for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
@@ -440,7 +443,7 @@ static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
}
sprintf(devnode, "plughw:%d,%d", card, device);
- if(!alsa_try_open(devnode, stream))
+ if(!alsa_try_open(devnode, flow))
continue;
if(*num){
@@ -473,15 +476,14 @@ static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
return S_OK;
}
-static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
- GUID **guids, UINT *num)
+static void get_reg_devices(EDataFlow flow, WCHAR ***ids, GUID **guids, UINT *num)
{
static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
HKEY key;
WCHAR reg_devices[256];
DWORD size = sizeof(reg_devices), type;
- const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
+ const WCHAR *value_name = (flow == eRender) ? ALSAOutputDevices : ALSAInputDevices;
/* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
@@ -500,7 +502,7 @@ static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***id
WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
- if(alsa_try_open(devname, stream)){
+ if(alsa_try_open(devname, flow)){
if(*num){
*ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
*guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
@@ -554,14 +556,12 @@ static BOOL need_card_number(int card, const char *string)
static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
UINT *num)
{
- snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
- SND_PCM_STREAM_CAPTURE);
int err, card;
card = -1;
*num = 0;
- if(alsa_try_open(defname, stream)){
+ if(alsa_try_open(defname, flow)){
*ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
(*ids)[0] = construct_device_id(flow, defaultW, NULL);
*guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
@@ -569,7 +569,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
++*num;
}
- get_reg_devices(flow, stream, ids, guids, num);
+ get_reg_devices(flow, ids, guids, num);
for(err = snd_card_next(&card); card != -1 && err >= 0;
err = snd_card_next(&card)){
@@ -592,7 +592,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
cardpath, err, snd_strerror(err));
- alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
+ alsa_get_card_devices(flow, ids, guids, num, ctl, card, nameW);
}else{
if(need_card_number(card, cardname)){
char *cardnameN;
@@ -615,7 +615,7 @@ static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
}
MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
- alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
+ alsa_get_card_devices(flow, ids, guids, num, ctl, card, cardnameW);
HeapFree(GetProcessHeap(), 0, cardnameW);
free(cardname);
@@ -730,7 +730,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
{
ACImpl *This;
int err;
- snd_pcm_stream_t stream;
char alsa_name[256];
EDataFlow dataflow;
HRESULT hr;
@@ -740,6 +739,9 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
return AUDCLNT_E_DEVICE_INVALIDATED;
+ if(dataflow != eRender && dataflow != eCapture)
+ return E_UNEXPECTED;
+
This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
if(!This)
return E_OUTOFMEMORY;
@@ -751,15 +753,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
- if(dataflow == eRender)
- stream = SND_PCM_STREAM_PLAYBACK;
- else if(dataflow == eCapture)
- stream = SND_PCM_STREAM_CAPTURE;
- else{
- HeapFree(GetProcessHeap(), 0, This);
- return E_UNEXPECTED;
- }
-
hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->pUnkFTMarshal);
if (FAILED(hr)) {
HeapFree(GetProcessHeap(), 0, This);
@@ -767,7 +760,7 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
}
This->dataflow = dataflow;
- err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
+ err = snd_pcm_open(&This->pcm_handle, alsa_name, alsa_get_direction(dataflow), SND_PCM_NONBLOCK);
if(err < 0){
HeapFree(GetProcessHeap(), 0, This);
WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
--
2.25.1
2
1
15 Feb '22
Pulse-based systems should be using winepulse.drv.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/winealsa.drv/mmdevdrv.c | 102 +----------------------------------
1 file changed, 1 insertion(+), 101 deletions(-)
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 5aa1b4aab63..9d051796765 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -50,7 +50,6 @@
#include <alsa/asoundlib.h>
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
-WINE_DECLARE_DEBUG_CHANNEL(winediag);
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
@@ -666,89 +665,6 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
return S_OK;
}
-/* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
- * which causes audio to cease playing after a few minutes of playback.
- * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
- * around this issue. */
-static snd_config_t *make_handle_underrun_config(const char *name)
-{
- snd_config_t *lconf, *dev_node, *hu_node, *type_node;
- char dev_node_name[260];
- const char *type_str;
- int err;
-
- snd_config_update();
-
- if((err = snd_config_copy(&lconf, snd_config)) < 0){
- WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
- return NULL;
- }
-
- sprintf(dev_node_name, "pcm.%s", name);
- err = snd_config_search(lconf, dev_node_name, &dev_node);
- if(err == -ENOENT){
- snd_config_delete(lconf);
- return NULL;
- }
- if(err < 0){
- snd_config_delete(lconf);
- WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
- return NULL;
- }
-
- /* ALSA is extremely fragile. If it runs into a config setting it doesn't
- * recognize, it tends to fail or assert. So we only want to inject
- * handle_underrun=1 on devices that we know will recognize it. */
- err = snd_config_search(dev_node, "type", &type_node);
- if(err == -ENOENT){
- snd_config_delete(lconf);
- return NULL;
- }
- if(err < 0){
- snd_config_delete(lconf);
- WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
- return NULL;
- }
-
- if((err = snd_config_get_string(type_node, &type_str)) < 0){
- snd_config_delete(lconf);
- return NULL;
- }
-
- if(strcmp(type_str, "pulse") != 0){
- snd_config_delete(lconf);
- return NULL;
- }
-
- err = snd_config_search(dev_node, "handle_underrun", &hu_node);
- if(err >= 0){
- /* user already has an explicit handle_underrun setting, so don't
- * use a local config */
- snd_config_delete(lconf);
- return NULL;
- }
- if(err != -ENOENT){
- snd_config_delete(lconf);
- WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
- return NULL;
- }
-
- if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
- snd_config_delete(lconf);
- WARN("snd_config_imake_integer failed: %d (%s)\n", err,
- snd_strerror(err));
- return NULL;
- }
-
- if((err = snd_config_add(dev_node, hu_node)) < 0){
- snd_config_delete(lconf);
- WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
- return NULL;
- }
-
- return lconf;
-}
-
static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
{
HKEY devices_key;
@@ -815,8 +731,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
ACImpl *This;
int err;
snd_pcm_stream_t stream;
- snd_config_t *lconf;
- static BOOL handle_underrun = TRUE;
char alsa_name[256];
EDataFlow dataflow;
HRESULT hr;
@@ -853,21 +767,7 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
}
This->dataflow = dataflow;
- if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
- err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
- TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
- snd_config_delete(lconf);
- /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
- if(err == -EINVAL){
- ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
- " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
- handle_underrun = FALSE;
- }
- }else
- err = -EINVAL;
- if(err == -EINVAL){
- err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
- }
+ err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
if(err < 0){
HeapFree(GetProcessHeap(), 0, This);
WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
--
2.25.1
2
1
[PATCH v3 1/5] ncrypt: Make NCryptOpenStorageProvider return a valid stub pointer.
by Santino Mazza 15 Feb '22
by Santino Mazza 15 Feb '22
15 Feb '22
Some applications crash when the storage provider its null.
Signed-off-by: Santino Mazza <mazzasantino1206(a)gmail.com>
---
dlls/ncrypt/main.c | 16 +++++++++-
dlls/ncrypt/ncrypt_internal.h | 56 +++++++++++++++++++++++++++++++++++
dlls/ncrypt/tests/ncrypt.c | 2 +-
3 files changed, 72 insertions(+), 2 deletions(-)
create mode 100644 dlls/ncrypt/ncrypt_internal.h
diff --git a/dlls/ncrypt/main.c b/dlls/ncrypt/main.c
index f23b239d93f..7f844bf9e85 100644
--- a/dlls/ncrypt/main.c
+++ b/dlls/ncrypt/main.c
@@ -23,6 +23,7 @@
#include "windef.h"
#include "winbase.h"
#include "ncrypt.h"
+#include "ncrypt_internal.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ncrypt);
@@ -129,10 +130,23 @@ SECURITY_STATUS WINAPI NCryptOpenKey(NCRYPT_PROV_HANDLE provider, NCRYPT_KEY_HAN
return NTE_NOT_SUPPORTED;
}
+int allocate_storage_provider_object(struct ncrypt_object **providerobject)
+{
+ *providerobject = malloc(sizeof(struct ncrypt_object));
+ if (providerobject == NULL)
+ {
+ ERR("Error allocating memory.\n");
+ return NTE_NO_MEMORY;
+ }
+ memset(*providerobject, 0, sizeof(struct ncrypt_object));
+ (*providerobject)->type = STORAGE_PROVIDER;
+ return ERROR_SUCCESS;
+}
+
SECURITY_STATUS WINAPI NCryptOpenStorageProvider(NCRYPT_PROV_HANDLE *provider, const WCHAR *name, DWORD flags)
{
FIXME("(%p, %s, %u): stub\n", provider, wine_dbgstr_w(name), flags);
- return NTE_NOT_SUPPORTED;
+ return allocate_storage_provider_object(provider);
}
SECURITY_STATUS WINAPI NCryptSetProperty(NCRYPT_HANDLE object, const WCHAR *property,
diff --git a/dlls/ncrypt/ncrypt_internal.h b/dlls/ncrypt/ncrypt_internal.h
new file mode 100644
index 00000000000..1201cdd857a
--- /dev/null
+++ b/dlls/ncrypt/ncrypt_internal.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021 Santino Mazza
+ *
+ * This library 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.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef NCRYPT_INTERNAL_H
+#define NCRYPT_INTERNAL_H
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+
+struct ncrypt_storage_provider_object
+{
+ // FIXME Stub
+};
+
+enum ncrypt_object_type
+{
+ STORAGE_PROVIDER,
+};
+
+struct ncrypt_object_property
+{
+ WCHAR *key;
+ DWORD value_size;
+ VOID *value;
+};
+
+struct ncrypt_object
+{
+ enum ncrypt_object_type type;
+ DWORD number_of_properties;
+ struct ncrypt_object_property *properties;
+ union
+ {
+ struct ncrypt_storage_provider_object storage_provider;
+ } object;
+};
+
+#endif // NCRYPT_INTERNAL_H
diff --git a/dlls/ncrypt/tests/ncrypt.c b/dlls/ncrypt/tests/ncrypt.c
index b948665ebaa..76e5396b288 100644
--- a/dlls/ncrypt/tests/ncrypt.c
+++ b/dlls/ncrypt/tests/ncrypt.c
@@ -86,11 +86,11 @@ UCHAR invalid_rsa_key_blob[] = {
static void test_key_import_rsa(void)
{
- todo_wine {
NCRYPT_PROV_HANDLE prov;
SECURITY_STATUS ret = NCryptOpenStorageProvider(&prov, NULL, 0);
ok(ret == ERROR_SUCCESS, "got 0x%x\n", ret);
+ todo_wine {
NCRYPT_KEY_HANDLE key = NULL;
ret = NCryptImportKey(prov, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, &key, rsa_key_blob, sizeof(rsa_key_blob), 0);
ok(ret == ERROR_SUCCESS, "got 0x%x\n", ret);
--
2.32.0
3
12
[PATCH v2 1/5] winepulse.drv: Expose audio devices to the application.
by Gabriel Ivăncescu 15 Feb '22
by Gabriel Ivăncescu 15 Feb '22
15 Feb '22
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(a)harmstone.com>, with changes by
Sebastian Lackner <sebastian(a)fds-team.de>.
Signed-off-by: Gabriel Ivăncescu <gabrielopcode(a)gmail.com>
---
This patchset has lived in wine-staging for a long time, but has been
recently rebased to deal with unixlib separation.
dlls/winepulse.drv/mmdevdrv.c | 210 ++++++++++++++++++++++++++++------
dlls/winepulse.drv/pulse.c | 135 ++++++++++++++++++++--
dlls/winepulse.drv/unixlib.h | 11 ++
3 files changed, 310 insertions(+), 46 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 35a66e1..844c14c 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,
@@ -134,6 +136,7 @@ struct ACImpl {
IUnknown *marshal;
IMMDevice *parent;
struct list entry;
+ char device[256];
float *vol;
LONG ref;
@@ -147,8 +150,6 @@ struct ACImpl {
AudioSessionWrapper *session_wrapper;
};
-static const WCHAR defaultW[] = L"PulseAudio";
-
static const IAudioClient3Vtbl AudioClient3_Vtbl;
static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
@@ -267,39 +268,114 @@ 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 *device, GUID *guid)
{
- WCHAR *id;
+ DWORD type, size = sizeof(*guid);
+ WCHAR key_name[258];
+ LSTATUS status;
+ HKEY dev_key;
- TRACE("%d %p %p %p\n", flow, ids, num, def_index);
+ if (!device[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, device);
- (*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(device), 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(device), 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 = 1024;
+ params.devices = NULL;
+ do {
+ HeapFree(GetProcessHeap(), 0, params.devices);
+ params.devices = 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;
+ }
+
+ p = params.devices;
+ for (i = 0; i < params.num; i++) {
+ get_device_guid(drv_key, flow, p, &guids[i]);
+ p += wcslen(p) + 1;
+
+ 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.devices);
+ 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,26 +390,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 name[256], EDataFlow *flow)
+{
+ DWORD key_name_size;
+ WCHAR key_name[258];
+ DWORD index = 0;
+ HKEY key;
+
+ /* Return empty string for default PulseAudio device */
+ 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, name, 256, 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;
+ ACImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
EDataFlow dataflow;
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;
*out = NULL;
-
- This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
+ if (!get_pulse_name_by_guid(guid, This->device, &dataflow)) {
+ HeapFree(GetProcessHeap(), 0, This);
+ return AUDCLNT_E_DEVICE_INVALIDATED;
+ }
+
This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
@@ -609,6 +746,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
}
params.name = name = get_application_name();
+ params.device = This->device;
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..d5f8edf 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 device[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,39 @@ 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;
+ DWORD len, name_len, needed = 0;
+ WCHAR *p = params->devices;
+ WCHAR device[256];
+ PhysDevice *dev;
+
+ params->num = 0;
+ LIST_FOR_EACH_ENTRY(dev, list, PhysDevice, entry) {
+ if (!(len = ntdll_umbstowcs(dev->device, strlen(dev->device) + 1, device, ARRAY_SIZE(device))))
+ continue;
+ name_len = lstrlenW(dev->name) + 1;
+
+ needed += (len + name_len) * sizeof(WCHAR);
+ if (needed <= params->size) {
+ memcpy(p, device, 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 +413,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 *device, const char *name)
+{
+ DWORD len = strlen(device), name_len = strlen(name);
+ PhysDevice *dev = malloc(FIELD_OFFSET(PhysDevice, device[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->device, device, 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 +672,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 +688,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 +879,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 *device, 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 +906,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 device name is given, use exactly the specified device */
+ if (device[0])
+ flags |= PA_STREAM_DONT_MOVE;
+ else
+ device = 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, device, &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, device, &attr, flags);
if (ret < 0) {
WARN("Returns %i\n", ret);
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
@@ -864,7 +978,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->device, stream->period_bytes);
if (SUCCEEDED(hr)) {
UINT32 unalign;
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(stream->stream);
@@ -1967,6 +2081,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..c4d274c 100644
--- a/dlls/winepulse.drv/unixlib.h
+++ b/dlls/winepulse.drv/unixlib.h
@@ -37,9 +37,19 @@ struct main_loop_params
HANDLE event;
};
+struct get_endpoint_ids_params
+{
+ EDataFlow flow;
+ DWORD size;
+ WCHAR *devices;
+ HRESULT result;
+ unsigned int num;
+};
+
struct create_stream_params
{
const char *name;
+ const char *device;
EDataFlow dataflow;
AUDCLNT_SHAREMODE mode;
DWORD flags;
@@ -191,6 +201,7 @@ enum unix_funcs
process_attach,
process_detach,
main_loop,
+ get_endpoint_ids,
create_stream,
release_stream,
start,
--
2.34.1
2
9