include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob0617c5977c94893bfb5398733317dd4d1b741296
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 WCHAR buf[MAX_PATH];
76 WCHAR *filename;
78 switch (reason)
80 case DLL_PROCESS_ATTACH:
81 DisableThreadLibraryCalls(dll);
82 if (__wine_init_unix_call())
83 return FALSE;
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);
92 break;
93 case DLL_PROCESS_DETACH:
94 if (!reserved)
96 struct device_cache *device, *device_next;
98 LIST_FOR_EACH_ENTRY_SAFE(device, device_next, &g_devices_cache, struct device_cache, entry)
99 free(device);
101 break;
103 return TRUE;
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);
110 LSTATUS status;
111 HKEY drv_key, dev_key;
113 if (!pulse_name[0]) {
114 *guid = (flow == eRender) ? pulse_render_guid : pulse_capture_guid;
115 return;
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);
122 CoCreateGuid(guid);
123 return;
126 key_name[0] = (flow == eRender) ? '0' : '1';
127 key_name[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);
135 CoCreateGuid(guid);
136 return;
139 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE*)guid, &size);
140 if (status != ERROR_SUCCESS || type != REG_BINARY || size != sizeof(*guid)) {
141 CoCreateGuid(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];
154 DWORD key_name_size;
155 DWORD index = 0;
156 HKEY key;
158 *name = NULL;
160 /* Return empty string for default PulseAudio device */
161 if (IsEqualGUID(guid, &pulse_render_guid)) {
162 *flow = eRender;
163 if (!(*name = malloc(1)))
164 return FALSE;
165 } else if (IsEqualGUID(guid, &pulse_capture_guid)) {
166 *flow = eCapture;
167 if (!(*name = malloc(1)))
168 return FALSE;
171 if (*name) {
172 *name[0] = '\0';
173 return TRUE;
176 /* Check the cache first */
177 LIST_FOR_EACH_ENTRY(device, &g_devices_cache, struct device_cache, entry) {
178 if (!IsEqualGUID(guid, &device->guid))
179 continue;
180 *flow = device->dataflow;
181 if ((*name = strdup(device->pulse_name)))
182 return TRUE;
184 return FALSE;
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");
189 return FALSE;
192 for (;;) {
193 DWORD size, type;
194 LSTATUS status;
195 GUID reg_guid;
196 HKEY dev_key;
197 int len;
199 key_name_size = ARRAY_SIZE(key_name);
200 if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
201 break;
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));
205 continue;
208 size = sizeof(reg_guid);
209 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE *)&reg_guid, &size);
210 RegCloseKey(dev_key);
212 if (status == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(&reg_guid, guid)) {
213 RegCloseKey(key);
215 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
217 if (key_name[0] == '0')
218 *flow = eRender;
219 else if (key_name[0] == '1')
220 *flow = eCapture;
221 else {
222 WARN("Unknown device type: %c\n", key_name[0]);
223 return FALSE;
226 if (!(len = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, NULL, 0, NULL, NULL)))
227 return FALSE;
229 if (!(*name = malloc(len)))
230 return FALSE;
232 if (!WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, *name, len, NULL, NULL)) {
233 free(*name);
234 return FALSE;
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);
243 return TRUE;
247 RegCloseKey(key);
248 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
249 return FALSE;