wine/dlls/winecoreaudio.drv/mmdevdrv.c
2023-08-23 17:33:47 +02:00

223 lines
6.2 KiB
C

/*
* Copyright 2011 Andrew Eikum for CodeWeavers
*
* 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
*/
#define COBJMACROS
#include <stdarg.h>
#include <wchar.h>
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "winnls.h"
#include "winreg.h"
#include "wine/debug.h"
#include "wine/heap.h"
#include "wine/list.h"
#include "wine/unixlib.h"
#include "ole2.h"
#include "mmdeviceapi.h"
#include "devpkey.h"
#include "dshow.h"
#include "dsound.h"
#include "initguid.h"
#include "endpointvolume.h"
#include "audioclient.h"
#include "audiopolicy.h"
#include "unixlib.h"
#include "../mmdevapi/mmdevdrv.h"
WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
static WCHAR drv_key_devicesW[256];
BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
{
WCHAR buf[MAX_PATH];
WCHAR *filename;
DisableThreadLibraryCalls(dll);
if (__wine_init_unix_call())
return FALSE;
GetModuleFileNameW(dll, buf, ARRAY_SIZE(buf));
filename = wcsrchr(buf, '\\');
filename = filename ? filename + 1 : buf;
swprintf(drv_key_devicesW, ARRAY_SIZE(drv_key_devicesW),
L"Software\\Wine\\Drivers\\%s\\devices", filename);
break;
}
case DLL_PROCESS_DETACH:
if (reserved) break;
break;
}
return TRUE;
}
static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
GUID *guid)
{
HKEY key;
BOOL opened = FALSE;
LONG lr;
if(!drv_key){
lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
NULL, &drv_key, NULL);
if(lr != ERROR_SUCCESS){
ERR("RegCreateKeyEx(drv_key) failed: %lu\n", lr);
return;
}
opened = TRUE;
}
lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
NULL, &key, NULL);
if(lr != ERROR_SUCCESS){
ERR("RegCreateKeyEx(%s) failed: %lu\n", wine_dbgstr_w(key_name), lr);
goto exit;
}
lr = RegSetValueExW(key, L"guid", 0, REG_BINARY, (BYTE*)guid,
sizeof(GUID));
if(lr != ERROR_SUCCESS)
ERR("RegSetValueEx(%s\\guid) failed: %lu\n", wine_dbgstr_w(key_name), lr);
RegCloseKey(key);
exit:
if(opened)
RegCloseKey(drv_key);
}
void WINAPI get_device_guid(EDataFlow flow, const char *dev, GUID *guid)
{
HKEY key = NULL, dev_key;
DWORD type, size = sizeof(*guid);
WCHAR key_name[256];
if(flow == eCapture)
key_name[0] = '1';
else
key_name[0] = '0';
key_name[1] = ',';
MultiByteToWideChar(CP_UNIXCP, 0, dev, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
if(RegQueryValueExW(dev_key, L"guid", 0, &type,
(BYTE*)guid, &size) == ERROR_SUCCESS){
if(type == REG_BINARY){
RegCloseKey(dev_key);
RegCloseKey(key);
return;
}
ERR("Invalid type for device %s GUID: %lu; ignoring and overwriting\n",
wine_dbgstr_w(key_name), type);
}
RegCloseKey(dev_key);
}
}
CoCreateGuid(guid);
set_device_guid(flow, key, key_name, guid);
if(key)
RegCloseKey(key);
}
BOOL WINAPI get_device_name_from_guid(const GUID *guid, char **name, EDataFlow *flow)
{
HKEY devices_key;
UINT i = 0;
WCHAR key_name[256];
DWORD key_name_size;
if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
ERR("No devices in registry?\n");
return FALSE;
}
while(1){
HKEY key;
DWORD size, type;
GUID reg_guid;
key_name_size = ARRAY_SIZE(key_name);
if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
NULL, NULL, NULL) != ERROR_SUCCESS)
break;
if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
continue;
}
size = sizeof(reg_guid);
if(RegQueryValueExW(key, L"guid", 0, &type,
(BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
if(IsEqualGUID(&reg_guid, guid)){
RegCloseKey(key);
RegCloseKey(devices_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{
ERR("Unknown device type: %c\n", key_name[0]);
return FALSE;
}
if(!(size = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, NULL, 0, NULL, NULL)))
return FALSE;
if(!(*name = malloc(size)))
return FALSE;
if(!WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, *name, size, NULL, NULL)){
free(*name);
return FALSE;
}
return TRUE;
}
}
RegCloseKey(key);
}
RegCloseKey(devices_key);
WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
return FALSE;
}