2 * Copyright 2011-2012 Maarten Lankhorst
3 * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
4 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * Copyright 2022 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unixlib.h"
45 #include "mmdeviceapi.h"
46 #include "audioclient.h"
47 #include "endpointvolume.h"
48 #include "audiopolicy.h"
50 #include "../mmdevapi/unixlib.h"
51 #include "../mmdevapi/mmdevdrv.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(pulse
);
55 #define MAX_PULSE_NAME_LEN 256
57 static struct list g_devices_cache
= LIST_INIT(g_devices_cache
);
66 static GUID pulse_render_guid
=
67 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
68 static GUID pulse_capture_guid
=
69 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
71 static WCHAR drv_key_devicesW
[256];
73 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
75 if (reason
== DLL_PROCESS_ATTACH
) {
79 DisableThreadLibraryCalls(dll
);
80 if (__wine_init_unix_call())
83 GetModuleFileNameW(dll
, buf
, ARRAY_SIZE(buf
));
85 filename
= wcsrchr(buf
, '\\');
86 filename
= filename
? filename
+ 1 : buf
;
88 swprintf(drv_key_devicesW
, ARRAY_SIZE(drv_key_devicesW
),
89 L
"Software\\Wine\\Drivers\\%s\\devices", filename
);
90 } else if (reason
== DLL_PROCESS_DETACH
) {
91 struct device_cache
*device
, *device_next
;
93 LIST_FOR_EACH_ENTRY_SAFE(device
, device_next
, &g_devices_cache
, struct device_cache
, entry
)
99 void WINAPI
get_device_guid(EDataFlow flow
, const char *pulse_name
, GUID
*guid
)
101 WCHAR key_name
[MAX_PULSE_NAME_LEN
+ 2];
102 DWORD type
, size
= sizeof(*guid
);
104 HKEY drv_key
, dev_key
;
106 if (!pulse_name
[0]) {
107 *guid
= (flow
== eRender
) ? pulse_render_guid
: pulse_capture_guid
;
111 status
= RegCreateKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, NULL
, 0,
112 KEY_WRITE
| KEY_WOW64_64KEY
, NULL
, &drv_key
, NULL
);
113 if (status
!= ERROR_SUCCESS
) {
114 ERR("Failed to open devices registry key: %lu\n", status
);
119 key_name
[0] = (flow
== eRender
) ? '0' : '1';
121 MultiByteToWideChar(CP_UNIXCP
, 0, pulse_name
, -1, key_name
+ 2, ARRAY_SIZE(key_name
) - 2);
123 status
= RegCreateKeyExW(drv_key
, key_name
, 0, NULL
, 0, KEY_READ
| KEY_WRITE
| KEY_WOW64_64KEY
,
124 NULL
, &dev_key
, NULL
);
125 if (status
!= ERROR_SUCCESS
) {
126 ERR("Failed to open registry key for device %s: %lu\n", pulse_name
, status
);
127 RegCloseKey(drv_key
);
132 status
= RegQueryValueExW(dev_key
, L
"guid", 0, &type
, (BYTE
*)guid
, &size
);
133 if (status
!= ERROR_SUCCESS
|| type
!= REG_BINARY
|| size
!= sizeof(*guid
)) {
135 status
= RegSetValueExW(dev_key
, L
"guid", 0, REG_BINARY
, (BYTE
*)guid
, sizeof(*guid
));
136 if (status
!= ERROR_SUCCESS
)
137 ERR("Failed to store device GUID for %s to registry: %lu\n", pulse_name
, status
);
139 RegCloseKey(dev_key
);
140 RegCloseKey(drv_key
);
143 BOOL WINAPI
get_device_name_from_guid(GUID
*guid
, char **name
, EDataFlow
*flow
)
145 struct device_cache
*device
;
146 WCHAR key_name
[MAX_PULSE_NAME_LEN
+ 2];
153 /* Return empty string for default PulseAudio device */
154 if (IsEqualGUID(guid
, &pulse_render_guid
)) {
156 if (!(*name
= malloc(1)))
158 } else if (IsEqualGUID(guid
, &pulse_capture_guid
)) {
160 if (!(*name
= malloc(1)))
169 /* Check the cache first */
170 LIST_FOR_EACH_ENTRY(device
, &g_devices_cache
, struct device_cache
, entry
) {
171 if (!IsEqualGUID(guid
, &device
->guid
))
173 *flow
= device
->dataflow
;
174 if ((*name
= strdup(device
->pulse_name
)))
180 if (RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_READ
| KEY_WOW64_64KEY
, &key
) != ERROR_SUCCESS
) {
181 WARN("No devices found in registry\n");
192 key_name_size
= ARRAY_SIZE(key_name
);
193 if (RegEnumKeyExW(key
, index
++, key_name
, &key_name_size
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
196 if (RegOpenKeyExW(key
, key_name
, 0, KEY_READ
| KEY_WOW64_64KEY
, &dev_key
) != ERROR_SUCCESS
) {
197 ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name
));
201 size
= sizeof(reg_guid
);
202 status
= RegQueryValueExW(dev_key
, L
"guid", 0, &type
, (BYTE
*)®_guid
, &size
);
203 RegCloseKey(dev_key
);
205 if (status
== ERROR_SUCCESS
&& type
== REG_BINARY
&& size
== sizeof(reg_guid
) && IsEqualGUID(®_guid
, guid
)) {
208 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name
));
210 if (key_name
[0] == '0')
212 else if (key_name
[0] == '1')
215 WARN("Unknown device type: %c\n", key_name
[0]);
219 if (!(len
= WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, NULL
, 0, NULL
, NULL
)))
222 if (!(*name
= malloc(len
)))
225 if (!WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, *name
, len
, NULL
, NULL
)) {
230 if ((device
= malloc(FIELD_OFFSET(struct device_cache
, pulse_name
[len
])))) {
231 device
->guid
= reg_guid
;
232 device
->dataflow
= *flow
;
233 memcpy(device
->pulse_name
, *name
, len
);
234 list_add_tail(&g_devices_cache
, &device
->entry
);
241 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid
));