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
)
80 case DLL_PROCESS_ATTACH
:
81 DisableThreadLibraryCalls(dll
);
82 if (__wine_init_unix_call())
85 GetModuleFileNameW(dll
, buf
, ARRAY_SIZE(buf
));
87 filename
= wcsrchr(buf
, '\\');
88 filename
= filename
? filename
+ 1 : buf
;
90 swprintf(drv_key_devicesW
, ARRAY_SIZE(drv_key_devicesW
),
91 L
"Software\\Wine\\Drivers\\%s\\devices", filename
);
93 case DLL_PROCESS_DETACH
:
96 struct device_cache
*device
, *device_next
;
98 LIST_FOR_EACH_ENTRY_SAFE(device
, device_next
, &g_devices_cache
, struct device_cache
, entry
)
106 void WINAPI
get_device_guid(EDataFlow flow
, const char *pulse_name
, GUID
*guid
)
108 WCHAR key_name
[MAX_PULSE_NAME_LEN
+ 2];
109 DWORD type
, size
= sizeof(*guid
);
111 HKEY drv_key
, dev_key
;
113 if (!pulse_name
[0]) {
114 *guid
= (flow
== eRender
) ? pulse_render_guid
: pulse_capture_guid
;
118 status
= RegCreateKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, NULL
, 0,
119 KEY_WRITE
| KEY_WOW64_64KEY
, NULL
, &drv_key
, NULL
);
120 if (status
!= ERROR_SUCCESS
) {
121 ERR("Failed to open devices registry key: %lu\n", status
);
126 key_name
[0] = (flow
== eRender
) ? '0' : '1';
128 MultiByteToWideChar(CP_UNIXCP
, 0, pulse_name
, -1, key_name
+ 2, ARRAY_SIZE(key_name
) - 2);
130 status
= RegCreateKeyExW(drv_key
, key_name
, 0, NULL
, 0, KEY_READ
| KEY_WRITE
| KEY_WOW64_64KEY
,
131 NULL
, &dev_key
, NULL
);
132 if (status
!= ERROR_SUCCESS
) {
133 ERR("Failed to open registry key for device %s: %lu\n", pulse_name
, status
);
134 RegCloseKey(drv_key
);
139 status
= RegQueryValueExW(dev_key
, L
"guid", 0, &type
, (BYTE
*)guid
, &size
);
140 if (status
!= ERROR_SUCCESS
|| type
!= REG_BINARY
|| size
!= sizeof(*guid
)) {
142 status
= RegSetValueExW(dev_key
, L
"guid", 0, REG_BINARY
, (BYTE
*)guid
, sizeof(*guid
));
143 if (status
!= ERROR_SUCCESS
)
144 ERR("Failed to store device GUID for %s to registry: %lu\n", pulse_name
, status
);
146 RegCloseKey(dev_key
);
147 RegCloseKey(drv_key
);
150 BOOL WINAPI
get_device_name_from_guid(GUID
*guid
, char **name
, EDataFlow
*flow
)
152 struct device_cache
*device
;
153 WCHAR key_name
[MAX_PULSE_NAME_LEN
+ 2];
160 /* Return empty string for default PulseAudio device */
161 if (IsEqualGUID(guid
, &pulse_render_guid
)) {
163 if (!(*name
= malloc(1)))
165 } else if (IsEqualGUID(guid
, &pulse_capture_guid
)) {
167 if (!(*name
= malloc(1)))
176 /* Check the cache first */
177 LIST_FOR_EACH_ENTRY(device
, &g_devices_cache
, struct device_cache
, entry
) {
178 if (!IsEqualGUID(guid
, &device
->guid
))
180 *flow
= device
->dataflow
;
181 if ((*name
= strdup(device
->pulse_name
)))
187 if (RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_READ
| KEY_WOW64_64KEY
, &key
) != ERROR_SUCCESS
) {
188 WARN("No devices found in registry\n");
199 key_name_size
= ARRAY_SIZE(key_name
);
200 if (RegEnumKeyExW(key
, index
++, key_name
, &key_name_size
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
203 if (RegOpenKeyExW(key
, key_name
, 0, KEY_READ
| KEY_WOW64_64KEY
, &dev_key
) != ERROR_SUCCESS
) {
204 ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name
));
208 size
= sizeof(reg_guid
);
209 status
= RegQueryValueExW(dev_key
, L
"guid", 0, &type
, (BYTE
*)®_guid
, &size
);
210 RegCloseKey(dev_key
);
212 if (status
== ERROR_SUCCESS
&& type
== REG_BINARY
&& size
== sizeof(reg_guid
) && IsEqualGUID(®_guid
, guid
)) {
215 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name
));
217 if (key_name
[0] == '0')
219 else if (key_name
[0] == '1')
222 WARN("Unknown device type: %c\n", key_name
[0]);
226 if (!(len
= WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, NULL
, 0, NULL
, NULL
)))
229 if (!(*name
= malloc(len
)))
232 if (!WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, *name
, len
, NULL
, NULL
)) {
237 if ((device
= malloc(FIELD_OFFSET(struct device_cache
, pulse_name
[len
])))) {
238 device
->guid
= reg_guid
;
239 device
->dataflow
= *flow
;
240 memcpy(device
->pulse_name
, *name
, len
);
241 list_add_tail(&g_devices_cache
, &device
->entry
);
248 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid
));