dmusic: Avoid swallowing collection Load failures.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob19d3d4040a36b07e655d8c1924a7650fe6bfb636
1 /*
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
22 #define COBJMACROS
24 #include <stdarg.h>
25 #include <assert.h>
26 #include <wchar.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unixlib.h"
35 #include "ole2.h"
36 #include "mimeole.h"
37 #include "dshow.h"
38 #include "dsound.h"
39 #include "propsys.h"
40 #include "propkey.h"
42 #include "initguid.h"
43 #include "ks.h"
44 #include "ksmedia.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);
59 struct device_cache {
60 struct list entry;
61 GUID guid;
62 EDataFlow dataflow;
63 char pulse_name[0];
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) {
76 WCHAR buf[MAX_PATH];
77 WCHAR *filename;
79 DisableThreadLibraryCalls(dll);
80 if (__wine_init_unix_call())
81 return FALSE;
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)
94 free(device);
96 return TRUE;
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);
103 LSTATUS status;
104 HKEY drv_key, dev_key;
106 if (!pulse_name[0]) {
107 *guid = (flow == eRender) ? pulse_render_guid : pulse_capture_guid;
108 return;
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);
115 CoCreateGuid(guid);
116 return;
119 key_name[0] = (flow == eRender) ? '0' : '1';
120 key_name[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);
128 CoCreateGuid(guid);
129 return;
132 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE*)guid, &size);
133 if (status != ERROR_SUCCESS || type != REG_BINARY || size != sizeof(*guid)) {
134 CoCreateGuid(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];
147 DWORD key_name_size;
148 DWORD index = 0;
149 HKEY key;
151 *name = NULL;
153 /* Return empty string for default PulseAudio device */
154 if (IsEqualGUID(guid, &pulse_render_guid)) {
155 *flow = eRender;
156 if (!(*name = malloc(1)))
157 return FALSE;
158 } else if (IsEqualGUID(guid, &pulse_capture_guid)) {
159 *flow = eCapture;
160 if (!(*name = malloc(1)))
161 return FALSE;
164 if (*name) {
165 *name[0] = '\0';
166 return TRUE;
169 /* Check the cache first */
170 LIST_FOR_EACH_ENTRY(device, &g_devices_cache, struct device_cache, entry) {
171 if (!IsEqualGUID(guid, &device->guid))
172 continue;
173 *flow = device->dataflow;
174 if ((*name = strdup(device->pulse_name)))
175 return TRUE;
177 return FALSE;
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");
182 return FALSE;
185 for (;;) {
186 DWORD size, type;
187 LSTATUS status;
188 GUID reg_guid;
189 HKEY dev_key;
190 int len;
192 key_name_size = ARRAY_SIZE(key_name);
193 if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
194 break;
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));
198 continue;
201 size = sizeof(reg_guid);
202 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE *)&reg_guid, &size);
203 RegCloseKey(dev_key);
205 if (status == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(&reg_guid, guid)) {
206 RegCloseKey(key);
208 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
210 if (key_name[0] == '0')
211 *flow = eRender;
212 else if (key_name[0] == '1')
213 *flow = eCapture;
214 else {
215 WARN("Unknown device type: %c\n", key_name[0]);
216 return FALSE;
219 if (!(len = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, NULL, 0, NULL, NULL)))
220 return FALSE;
222 if (!(*name = malloc(len)))
223 return FALSE;
225 if (!WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, *name, len, NULL, NULL)) {
226 free(*name);
227 return FALSE;
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);
236 return TRUE;
240 RegCloseKey(key);
241 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
242 return FALSE;