devenum.c 46.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright 2009 Maarten Lankhorst
 *
 * 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
 */

#include <stdarg.h>

21
#define NONAMELESSUNION
22 23 24
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
25 26
#include "winnls.h"
#include "winreg.h"
27
#include "wine/debug.h"
28
#include "wine/list.h"
29

30
#include "initguid.h"
31 32
#include "ole2.h"
#include "mmdeviceapi.h"
33 34 35 36 37
#include "dshow.h"
#include "dsound.h"
#include "audioclient.h"
#include "endpointvolume.h"
#include "audiopolicy.h"
38
#include "spatialaudioclient.h"
39 40

#include "mmdevapi.h"
41
#include "devpkey.h"
42 43 44

WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);

45 46 47
static HKEY key_render;
static HKEY key_capture;

48 49
typedef struct MMDevPropStoreImpl
{
50
    IPropertyStore IPropertyStore_iface;
51 52 53 54 55
    LONG ref;
    MMDevice *parent;
    DWORD access;
} MMDevPropStore;

56 57
typedef struct MMDevEnumImpl
{
58
    IMMDeviceEnumerator IMMDeviceEnumerator_iface;
59 60 61
    LONG ref;
} MMDevEnumImpl;

62
static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
63
static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
64
static const IMMDeviceCollectionVtbl MMDevColVtbl;
65
static const IMMDeviceVtbl MMDeviceVtbl;
66
static const IPropertyStoreVtbl MMDevPropVtbl;
67
static const IMMEndpointVtbl MMEndpointVtbl;
68

69
static MMDevEnumImpl enumerator;
70
static struct list device_list = LIST_INIT(device_list);
71 72
static IMMDevice info_device;

73 74
typedef struct MMDevColImpl
{
75
    IMMDeviceCollection IMMDeviceCollection_iface;
76 77 78 79 80
    LONG ref;
    EDataFlow flow;
    DWORD state;
} MMDevColImpl;

81
typedef struct IPropertyBagImpl {
82
    IPropertyBag IPropertyBag_iface;
83 84
    GUID devguid;
} IPropertyBagImpl;
85

86 87
static const IPropertyBagVtbl PB_Vtbl;

88 89
static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
{
    return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
}

static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
{
    return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
}

static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
{
    return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
}

static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
{
    return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
}

110
static const WCHAR propkey_formatW[] = L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X},%d";
111 112 113 114 115 116 117

static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
{
    WCHAR buffer[39];
    LONG ret;
    HKEY key;
    StringFromGUID2(guid, buffer, 39);
118
    if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, &key)) != ERROR_SUCCESS)
119
    {
120
        WARN("Opening key %s failed with %lu\n", debugstr_w(buffer), ret);
121 122
        return E_FAIL;
    }
123
    ret = RegOpenKeyExW(key, L"Properties", 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, propkey);
124 125 126
    RegCloseKey(key);
    if (ret != ERROR_SUCCESS)
    {
127
        WARN("Opening key Properties failed with %lu\n", ret);
128 129 130 131 132
        return E_FAIL;
    }
    return S_OK;
}

133
static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
{
    WCHAR buffer[80];
    const GUID *id = &key->fmtid;
    DWORD type, size;
    HRESULT hr = S_OK;
    HKEY regkey;
    LONG ret;

    hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
    if (FAILED(hr))
        return hr;
    wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
               id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
               id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
    ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
    if (ret != ERROR_SUCCESS)
    {
151
        WARN("Reading %s returned %ld\n", debugstr_w(buffer), ret);
152
        RegCloseKey(regkey);
153
        pv->vt = VT_EMPTY;
154 155 156 157 158 159 160 161
        return S_OK;
    }

    switch (type)
    {
        case REG_SZ:
        {
            pv->vt = VT_LPWSTR;
162 163
            pv->pwszVal = CoTaskMemAlloc(size);
            if (!pv->pwszVal)
164 165
                hr = E_OUTOFMEMORY;
            else
166
                RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->pwszVal, &size);
167 168 169 170 171
            break;
        }
        case REG_DWORD:
        {
            pv->vt = VT_UI4;
172
            RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->ulVal, &size);
173 174 175 176 177
            break;
        }
        case REG_BINARY:
        {
            pv->vt = VT_BLOB;
178 179 180
            pv->blob.cbSize = size;
            pv->blob.pBlobData = CoTaskMemAlloc(size);
            if (!pv->blob.pBlobData)
181 182
                hr = E_OUTOFMEMORY;
            else
183
                RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->blob.pBlobData, &size);
184 185 186
            break;
        }
        default:
187
            ERR("Unknown/unhandled type: %lu\n", type);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
            PropVariantClear(pv);
            break;
    }
    RegCloseKey(regkey);
    return hr;
}

static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
{
    WCHAR buffer[80];
    const GUID *id = &key->fmtid;
    HRESULT hr;
    HKEY regkey;
    LONG ret;

    hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
    if (FAILED(hr))
        return hr;
    wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
               id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
               id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
    switch (pv->vt)
    {
        case VT_UI4:
        {
213
            ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->ulVal, sizeof(DWORD));
214 215 216 217
            break;
        }
        case VT_BLOB:
        {
218
            ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->blob.pBlobData, pv->blob.cbSize);
219
            TRACE("Blob %p %lu\n", pv->blob.pBlobData, pv->blob.cbSize);
220 221 222 223 224

            break;
        }
        case VT_LPWSTR:
        {
225
            ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->pwszVal)));
226 227 228 229 230 231 232 233 234
            break;
        }
        default:
            ret = 0;
            FIXME("Unhandled type %u\n", pv->vt);
            hr = E_INVALIDARG;
            break;
    }
    RegCloseKey(regkey);
235
    TRACE("Writing %s returned %lu\n", debugstr_w(buffer), ret);
236 237 238
    return hr;
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
static HRESULT set_driver_prop_value(GUID *id, const EDataFlow flow, const PROPERTYKEY *prop)
{
    HRESULT hr;
    PROPVARIANT pv;

    if (!drvs.pGetPropValue)
        return E_NOTIMPL;

    hr = drvs.pGetPropValue(id, prop, &pv);

    if (SUCCEEDED(hr))
    {
        MMDevice_SetPropValue(id, flow, prop, &pv);
        PropVariantClear(&pv);
    }

    return hr;
}

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
struct product_name_overrides
{
    const WCHAR *id;
    const WCHAR *product;
};

static const struct product_name_overrides product_name_overrides[] =
{
    /* Sony controllers */
    { .id = L"VID_054C&PID_0CE6", .product = L"Wireless Controller" },
};

static const WCHAR *find_product_name_override(const WCHAR *device_id)
{
    const WCHAR *match_id = wcschr( device_id, '\\' ) + 1;
    DWORD i;

    for (i = 0; i < ARRAY_SIZE(product_name_overrides); ++i)
        if (!wcsnicmp( product_name_overrides[i].id, match_id, 17 ))
            return product_name_overrides[i].product;

    return NULL;
}

282 283 284 285
/* Creates or updates the state of a device
 * If GUID is null, a random guid will be assigned
 * and the device will be created
 */
286
static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
287 288
{
    HKEY key, root;
289
    MMDevice *device, *cur = NULL;
290 291
    WCHAR guidstr[39];

292 293 294 295
    static const PROPERTYKEY deviceinterface_key = {
        {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
    };

296 297 298 299
    static const PROPERTYKEY devicepath_key = {
        {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
    };

300
    LIST_FOR_EACH_ENTRY(device, &device_list, MMDevice, entry)
301
    {
302 303 304
        if (device->flow == flow && IsEqualGUID(&device->devguid, id)){
            cur = device;
            break;
305 306 307
        }
    }

308 309 310 311 312 313 314 315 316 317 318 319
    if(!cur){
        /* No device found, allocate new one */
        cur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cur));
        if (!cur)
            return NULL;

        cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
        cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;

        InitializeCriticalSection(&cur->crst);
        cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");

320
        list_add_tail(&device_list, &cur->entry);
321 322 323
    }else if(cur->ref > 0)
        WARN("Modifying an MMDevice with postitive reference count!\n");

324
    HeapFree(GetProcessHeap(), 0, cur->drv_id);
325 326
    cur->drv_id = name;

327 328 329
    cur->flow = flow;
    cur->state = state;
    cur->devguid = *id;
330

331
    StringFromGUID2(&cur->devguid, guidstr, ARRAY_SIZE(guidstr));
332

333 334 335 336
    if (flow == eRender)
        root = key_render;
    else
        root = key_capture;
337

338
    if (RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &key, NULL) == ERROR_SUCCESS)
339 340
    {
        HKEY keyprop;
341 342
        RegSetValueExW(key, L"DeviceState", 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
        if (!RegCreateKeyExW(key, L"Properties", 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &keyprop, NULL))
343
        {
344
            PROPVARIANT pv;
345

346
            pv.vt = VT_LPWSTR;
347
            pv.pwszVal = name;
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

            if (SUCCEEDED(set_driver_prop_value(id, flow, &devicepath_key))) {
                PROPVARIANT pv2;

                PropVariantInit(&pv2);

                if (SUCCEEDED(MMDevice_GetPropValue(id, flow, &devicepath_key, &pv2)) && pv2.vt == VT_LPWSTR) {
                    const WCHAR *override;
                    if ((override = find_product_name_override(pv2.pwszVal)) != NULL)
                        pv.pwszVal = (WCHAR*) override;
                }

                PropVariantClear(&pv2);
            }

363
            MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
364
            MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_FriendlyName, &pv);
365
            MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
366

367
            pv.pwszVal = guidstr;
368 369
            MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);

370 371 372
            if (FAILED(set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_FormFactor)))
            {
                pv.vt = VT_UI4;
373
                pv.ulVal = (flow == eCapture) ? Microphone : Speakers;
374 375 376 377

                MMDevice_SetPropValue(id, flow, &PKEY_AudioEndpoint_FormFactor, &pv);
            }

378
            if (flow != eCapture)
379 380 381 382 383 384 385 386 387 388 389
            {
                PROPVARIANT pv2;

                PropVariantInit(&pv2);

                /* make read-write by not overwriting if already set */
                if (FAILED(MMDevice_GetPropValue(id, flow, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv2)) || pv2.vt != VT_UI4)
                    set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_PhysicalSpeakers);

                PropVariantClear(&pv2);
            }
390

391 392 393 394 395 396 397 398 399 400 401 402
            RegCloseKey(keyprop);
        }
        RegCloseKey(key);
    }

    if (setdefault)
    {
        if (flow == eRender)
            MMDevice_def_play = cur;
        else
            MMDevice_def_rec = cur;
    }
403 404 405
    return cur;
}

406
HRESULT load_devices_from_reg(void)
407 408 409 410 411 412
{
    DWORD i = 0;
    HKEY root, cur;
    LONG ret;
    DWORD curflow;

413 414 415
    ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
            L"Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio", 0, NULL, 0,
            KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &root, NULL);
416
    if (ret == ERROR_SUCCESS)
417
        ret = RegCreateKeyExW(root, L"Capture", 0, NULL, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, NULL, &key_capture, NULL);
418
    if (ret == ERROR_SUCCESS)
419
        ret = RegCreateKeyExW(root, L"Render", 0, NULL, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, NULL, &key_render, NULL);
420 421 422 423 424 425 426
    RegCloseKey(root);
    cur = key_capture;
    curflow = eCapture;
    if (ret != ERROR_SUCCESS)
    {
        RegCloseKey(key_capture);
        key_render = key_capture = NULL;
427
        WARN("Couldn't create key: %lu\n", ret);
428 429 430 431 432 433 434 435 436
        return E_FAIL;
    }

    do {
        WCHAR guidvalue[39];
        GUID guid;
        DWORD len;
        PROPVARIANT pv = { VT_EMPTY };

437
        len = ARRAY_SIZE(guidvalue);
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
        ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
        if (ret == ERROR_NO_MORE_ITEMS)
        {
            if (cur == key_capture)
            {
                cur = key_render;
                curflow = eRender;
                i = 0;
                continue;
            }
            break;
        }
        if (ret != ERROR_SUCCESS)
            continue;
        if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
            && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
            && pv.vt == VT_LPWSTR)
        {
456
            DWORD size_bytes = (lstrlenW(pv.pwszVal) + 1) * sizeof(WCHAR);
457
            WCHAR *name = HeapAlloc(GetProcessHeap(), 0, size_bytes);
458
            memcpy(name, pv.pwszVal, size_bytes);
459
            MMDevice_Create(name, &guid, curflow,
460
                    DEVICE_STATE_NOTPRESENT, FALSE);
461
            CoTaskMemFree(pv.pwszVal);
462 463 464 465 466 467 468 469 470 471 472 473 474
        }
    } while (1);

    return S_OK;
}

static HRESULT set_format(MMDevice *dev)
{
    HRESULT hr;
    IAudioClient *client;
    WAVEFORMATEX *fmt;
    PROPVARIANT pv = { VT_EMPTY };

475
    hr = drvs.pGetAudioEndpoint(&dev->devguid, &dev->IMMDevice_iface, &client);
476 477 478 479 480 481 482 483 484 485 486 487
    if(FAILED(hr))
        return hr;

    hr = IAudioClient_GetMixFormat(client, &fmt);
    if(FAILED(hr)){
        IAudioClient_Release(client);
        return hr;
    }

    IAudioClient_Release(client);

    pv.vt = VT_BLOB;
488 489
    pv.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
    pv.blob.pBlobData = (BYTE*)fmt;
490 491 492 493
    MMDevice_SetPropValue(&dev->devguid, dev->flow,
            &PKEY_AudioEngine_DeviceFormat, &pv);
    MMDevice_SetPropValue(&dev->devguid, dev->flow,
            &PKEY_AudioEngine_OEMFormat, &pv);
494
    CoTaskMemFree(fmt);
495 496 497 498

    return S_OK;
}

499
HRESULT load_driver_devices(EDataFlow flow)
500 501
{
    WCHAR **ids;
502
    GUID *guids;
503 504 505
    UINT num, def, i;
    HRESULT hr;

506 507 508
    if(!drvs.pGetEndpointIDs)
        return S_OK;

509
    hr = drvs.pGetEndpointIDs(flow, &ids, &guids, &num, &def);
510 511 512 513 514
    if(FAILED(hr))
        return hr;

    for(i = 0; i < num; ++i){
        MMDevice *dev;
515
        dev = MMDevice_Create(ids[i], &guids[i], flow, DEVICE_STATE_ACTIVE,
516 517 518 519
                def == i);
        set_format(dev);
    }

520
    HeapFree(GetProcessHeap(), 0, guids);
521 522 523
    HeapFree(GetProcessHeap(), 0, ids);

    return S_OK;
524 525 526
}

static void MMDevice_Destroy(MMDevice *This)
527
{
528
    TRACE("Freeing %s\n", debugstr_w(This->drv_id));
529
    list_remove(&This->entry);
530 531
    This->crst.DebugInfo->Spare[0] = 0;
    DeleteCriticalSection(&This->crst);
532
    HeapFree(GetProcessHeap(), 0, This->drv_id);
533 534 535
    HeapFree(GetProcessHeap(), 0, This);
}

536 537 538 539 540
static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
{
    return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
}

541 542
static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
{
543
    MMDevice *This = impl_from_IMMDevice(iface);
544 545 546 547 548 549 550
    TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);

    if (!ppv)
        return E_POINTER;
    *ppv = NULL;
    if (IsEqualIID(riid, &IID_IUnknown)
        || IsEqualIID(riid, &IID_IMMDevice))
551
        *ppv = &This->IMMDevice_iface;
552
    else if (IsEqualIID(riid, &IID_IMMEndpoint))
553
        *ppv = &This->IMMEndpoint_iface;
554 555 556 557 558 559 560 561 562 563 564
    if (*ppv)
    {
        IUnknown_AddRef((IUnknown*)*ppv);
        return S_OK;
    }
    WARN("Unknown interface %s\n", debugstr_guid(riid));
    return E_NOINTERFACE;
}

static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
{
565
    MMDevice *This = impl_from_IMMDevice(iface);
566 567 568
    LONG ref;

    ref = InterlockedIncrement(&This->ref);
569
    TRACE("Refcount now %li\n", ref);
570 571 572 573 574
    return ref;
}

static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
{
575
    MMDevice *This = impl_from_IMMDevice(iface);
576 577 578
    LONG ref;

    ref = InterlockedDecrement(&This->ref);
579
    TRACE("Refcount now %li\n", ref);
580 581 582 583 584 585
    return ref;
}

static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
{
    HRESULT hr = E_NOINTERFACE;
586
    MMDevice *This = impl_from_IMMDevice(iface);
587

588
    TRACE("(%p)->(%s, %lx, %p, %p)\n", iface, debugstr_guid(riid), clsctx, params, ppv);
589 590 591 592

    if (!ppv)
        return E_POINTER;

593
    if (IsEqualIID(riid, &IID_IAudioClient) ||
594 595
            IsEqualIID(riid, &IID_IAudioClient2) ||
            IsEqualIID(riid, &IID_IAudioClient3)){
596
        hr = drvs.pGetAudioEndpoint(&This->devguid, iface, (IAudioClient**)ppv);
597 598 599
    }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume) ||
            IsEqualIID(riid, &IID_IAudioEndpointVolumeEx))
        hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolumeEx**)ppv);
600 601 602
    else if (IsEqualIID(riid, &IID_IAudioSessionManager)
             || IsEqualIID(riid, &IID_IAudioSessionManager2))
    {
603
        hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
604 605 606 607 608 609 610
    }
    else if (IsEqualIID(riid, &IID_IBaseFilter))
    {
        if (This->flow == eRender)
            hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
        else
            ERR("Not supported for recording?\n");
611 612 613
        if (SUCCEEDED(hr))
        {
            IPersistPropertyBag *ppb;
614
            hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void **)&ppb);
615 616 617 618
            if (SUCCEEDED(hr))
            {
                /* ::Load cannot assume the interface stays alive after the function returns,
                 * so just create the interface on the stack, saves a lot of complicated code */
619 620
                IPropertyBagImpl bag = { { &PB_Vtbl } };
                bag.devguid = This->devguid;
621
                hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
622 623 624 625 626 627 628 629 630 631
                IPersistPropertyBag_Release(ppb);
                if (FAILED(hr))
                    IBaseFilter_Release((IBaseFilter*)*ppv);
            }
            else
            {
                FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
                hr = S_OK;
            }
        }
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
    }
    else if (IsEqualIID(riid, &IID_IDeviceTopology))
    {
        FIXME("IID_IDeviceTopology unsupported\n");
    }
    else if (IsEqualIID(riid, &IID_IDirectSound)
             || IsEqualIID(riid, &IID_IDirectSound8))
    {
        if (This->flow == eRender)
            hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
        if (SUCCEEDED(hr))
        {
            hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
            if (FAILED(hr))
                IDirectSound_Release((IDirectSound*)*ppv);
        }
    }
649
    else if (IsEqualIID(riid, &IID_IDirectSoundCapture))
650 651 652 653 654 655 656 657 658 659
    {
        if (This->flow == eCapture)
            hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
        if (SUCCEEDED(hr))
        {
            hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
            if (FAILED(hr))
                IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
        }
    }
660 661 662 663
    else if (IsEqualIID(riid, &IID_ISpatialAudioClient))
    {
        hr = SpatialAudioClient_Create(iface, (ISpatialAudioClient**)ppv);
    }
664 665 666 667 668 669
    else
        ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));

    if (FAILED(hr))
        *ppv = NULL;

670
    TRACE("Returning %08lx\n", hr);
671 672 673 674 675
    return hr;
}

static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
{
676
    MMDevice *This = impl_from_IMMDevice(iface);
677
    TRACE("(%p)->(%lx,%p)\n", This, access, ppv);
678 679 680

    if (!ppv)
        return E_POINTER;
681
    return MMDevPropStore_Create(This, access, ppv);
682 683 684 685
}

static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
{
686
    MMDevice *This = impl_from_IMMDevice(iface);
687 688
    WCHAR *str;
    GUID *id = &This->devguid;
689 690 691 692

    TRACE("(%p)->(%p)\n", This, itemid);
    if (!itemid)
        return E_POINTER;
693 694 695
    *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
    if (!str)
        return E_OUTOFMEMORY;
696 697 698 699
    wsprintfW(str, L"{0.0.%u.00000000}.{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
              This->flow, id->Data1, id->Data2, id->Data3,
              id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
              id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7]);
700
    TRACE("returning %s\n", wine_dbgstr_w(str));
701
    return S_OK;
702 703 704 705
}

static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
{
706
    MMDevice *This = impl_from_IMMDevice(iface);
707 708 709 710 711 712
    TRACE("(%p)->(%p)\n", iface, state);

    if (!state)
        return E_POINTER;
    *state = This->state;
    return S_OK;
713 714
}

715 716 717 718 719 720 721 722 723 724 725
static const IMMDeviceVtbl MMDeviceVtbl =
{
    MMDevice_QueryInterface,
    MMDevice_AddRef,
    MMDevice_Release,
    MMDevice_Activate,
    MMDevice_OpenPropertyStore,
    MMDevice_GetId,
    MMDevice_GetState
};

726
static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
727
{
728
    return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
729 730 731 732
}

static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
{
733
    MMDevice *This = impl_from_IMMEndpoint(iface);
734
    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
735
    return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
736 737 738 739
}

static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
{
740
    MMDevice *This = impl_from_IMMEndpoint(iface);
741
    TRACE("(%p)\n", This);
742
    return IMMDevice_AddRef(&This->IMMDevice_iface);
743 744 745 746
}

static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
{
747
    MMDevice *This = impl_from_IMMEndpoint(iface);
748
    TRACE("(%p)\n", This);
749
    return IMMDevice_Release(&This->IMMDevice_iface);
750 751 752 753
}

static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
{
754
    MMDevice *This = impl_from_IMMEndpoint(iface);
755
    TRACE("(%p)->(%p)\n", This, flow);
756 757 758 759 760 761 762 763 764 765 766 767 768 769
    if (!flow)
        return E_POINTER;
    *flow = This->flow;
    return S_OK;
}

static const IMMEndpointVtbl MMEndpointVtbl =
{
    MMEndpoint_QueryInterface,
    MMEndpoint_AddRef,
    MMEndpoint_Release,
    MMEndpoint_GetDataFlow
};

770
static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
771 772
{
    MMDevColImpl *This;
773

774 775 776 777
    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
    *ppv = NULL;
    if (!This)
        return E_OUTOFMEMORY;
778
    This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
779 780 781
    This->ref = 1;
    This->flow = flow;
    This->state = state;
782
    *ppv = &This->IMMDeviceCollection_iface;
783 784 785 786 787 788 789 790 791 792
    return S_OK;
}

static void MMDevCol_Destroy(MMDevColImpl *This)
{
    HeapFree(GetProcessHeap(), 0, This);
}

static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
{
793
    MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
794
    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
795 796 797 798 799

    if (!ppv)
        return E_POINTER;
    if (IsEqualIID(riid, &IID_IUnknown)
        || IsEqualIID(riid, &IID_IMMDeviceCollection))
800
        *ppv = &This->IMMDeviceCollection_iface;
801 802 803 804 805 806 807 808 809 810
    else
        *ppv = NULL;
    if (!*ppv)
        return E_NOINTERFACE;
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
{
811
    MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
812
    LONG ref = InterlockedIncrement(&This->ref);
813
    TRACE("Refcount now %li\n", ref);
814 815 816 817 818
    return ref;
}

static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
{
819
    MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
820
    LONG ref = InterlockedDecrement(&This->ref);
821
    TRACE("Refcount now %li\n", ref);
822 823 824 825 826 827 828
    if (!ref)
        MMDevCol_Destroy(This);
    return ref;
}

static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
{
829
    MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
830
    MMDevice *cur;
831

832 833 834
    TRACE("(%p)->(%p)\n", This, numdevs);
    if (!numdevs)
        return E_POINTER;
835

836
    *numdevs = 0;
837
    LIST_FOR_EACH_ENTRY(cur, &device_list, MMDevice, entry)
838 839 840 841 842
    {
        if ((cur->flow == This->flow || This->flow == eAll)
            && (cur->state & This->state))
            ++(*numdevs);
    }
843 844 845
    return S_OK;
}

846
static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
847
{
848
    MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
849 850
    MMDevice *cur;
    DWORD i = 0;
851 852

    TRACE("(%p)->(%u, %p)\n", This, n, dev);
853 854
    if (!dev)
        return E_POINTER;
855

856
    LIST_FOR_EACH_ENTRY(cur, &device_list, MMDevice, entry)
857 858 859 860 861
    {
        if ((cur->flow == This->flow || This->flow == eAll)
            && (cur->state & This->state)
            && i++ == n)
        {
862
            *dev = &cur->IMMDevice_iface;
863 864 865 866 867
            IMMDevice_AddRef(*dev);
            return S_OK;
        }
    }
    WARN("Could not obtain item %u\n", n);
868 869 870 871 872 873 874 875 876 877 878 879 880
    *dev = NULL;
    return E_INVALIDARG;
}

static const IMMDeviceCollectionVtbl MMDevColVtbl =
{
    MMDevCol_QueryInterface,
    MMDevCol_AddRef,
    MMDevCol_Release,
    MMDevCol_GetCount,
    MMDevCol_Item
};

881 882
HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
{
883
    return IMMDeviceEnumerator_QueryInterface(&enumerator.IMMDeviceEnumerator_iface, riid, ppv);
884 885 886 887
}

void MMDevEnum_Free(void)
{
888 889 890
    MMDevice *device, *next;
    LIST_FOR_EACH_ENTRY_SAFE(device, next, &device_list, MMDevice, entry)
        MMDevice_Destroy(device);
891 892 893
    RegCloseKey(key_render);
    RegCloseKey(key_capture);
    key_render = key_capture = NULL;
894 895 896 897
}

static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
{
898
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
899
    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
900 901 902 903 904

    if (!ppv)
        return E_POINTER;
    if (IsEqualIID(riid, &IID_IUnknown)
        || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
905
        *ppv = &This->IMMDeviceEnumerator_iface;
906 907 908 909 910 911 912 913 914 915
    else
        *ppv = NULL;
    if (!*ppv)
        return E_NOINTERFACE;
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
{
916
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
917
    LONG ref = InterlockedIncrement(&This->ref);
918
    TRACE("Refcount now %li\n", ref);
919 920 921 922 923
    return ref;
}

static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
{
924
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
925
    LONG ref = InterlockedDecrement(&This->ref);
926
    TRACE("Refcount now %li\n", ref);
927 928 929 930 931
    return ref;
}

static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
{
932
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
933
    TRACE("(%p)->(%u,%lu,%p)\n", This, flow, mask, devices);
934 935 936 937 938 939 940
    if (!devices)
        return E_POINTER;
    *devices = NULL;
    if (flow >= EDataFlow_enum_count)
        return E_INVALIDARG;
    if (mask & ~DEVICE_STATEMASK_ALL)
        return E_INVALIDARG;
941
    return MMDevCol_Create(devices, flow, mask);
942 943 944 945
}

static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
{
946
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
947 948 949 950
    WCHAR reg_key[256];
    HKEY key;
    HRESULT hr;

951
    TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
952 953 954

    if (!device)
        return E_POINTER;
955 956 957 958 959 960 961

    if((flow != eRender && flow != eCapture) ||
            (role != eConsole && role != eMultimedia && role != eCommunications)){
        WARN("Unknown flow (%u) or role (%u)\n", flow, role);
        return E_INVALIDARG;
    }

962 963
    *device = NULL;

964 965 966 967
    if(!drvs.module_name[0])
        return E_NOTFOUND;

    lstrcpyW(reg_key, drv_keyW);
968
    lstrcatW(reg_key, L"\\");
969 970 971 972 973
    lstrcatW(reg_key, drvs.module_name);

    if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
        const WCHAR *reg_x_name, *reg_vx_name;
        WCHAR def_id[256];
974
        DWORD size = sizeof(def_id), state;
975 976

        if(flow == eRender){
977 978
            reg_x_name = L"DefaultOutput";
            reg_vx_name = L"DefaultVoiceOutput";
979
        }else{
980 981
            reg_x_name = L"DefaultInput";
            reg_vx_name = L"DefaultVoiceInput";
982 983 984 985 986 987 988
        }

        if(role == eCommunications &&
                RegQueryValueExW(key, reg_vx_name, 0, NULL,
                    (BYTE*)def_id, &size) == ERROR_SUCCESS){
            hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
            if(SUCCEEDED(hr)){
989 990 991 992 993
                if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
                        state == DEVICE_STATE_ACTIVE){
                    RegCloseKey(key);
                    return S_OK;
                }
994 995 996 997 998 999 1000 1001 1002
            }

            TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
        }

        if(RegQueryValueExW(key, reg_x_name, 0, NULL,
                    (BYTE*)def_id, &size) == ERROR_SUCCESS){
            hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
            if(SUCCEEDED(hr)){
1003 1004 1005 1006 1007
                if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
                        state == DEVICE_STATE_ACTIVE){
                    RegCloseKey(key);
                    return S_OK;
                }
1008 1009 1010 1011 1012 1013 1014 1015
            }

            TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
        }

        RegCloseKey(key);
    }

1016
    if (flow == eRender)
1017
        *device = &MMDevice_def_play->IMMDevice_iface;
1018
    else
1019
        *device = &MMDevice_def_rec->IMMDevice_iface;
1020 1021 1022 1023 1024

    if (!*device)
        return E_NOTFOUND;
    IMMDevice_AddRef(*device);
    return S_OK;
1025
}
1026 1027 1028

static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
{
1029
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1030
    MMDevice *impl;
1031 1032
    IMMDevice *dev = NULL;

1033 1034
    TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);

1035 1036 1037
    if(!name || !device)
        return E_POINTER;

1038
    if(!lstrcmpW(name, L"Wine info device")){
1039 1040 1041 1042
        *device = &info_device;
        return S_OK;
    }

1043
    LIST_FOR_EACH_ENTRY(impl, &device_list, MMDevice, entry)
1044
    {
1045
        HRESULT hr;
1046
        WCHAR *str;
1047
        dev = &impl->IMMDevice_iface;
1048 1049 1050
        hr = IMMDevice_GetId(dev, &str);
        if (FAILED(hr))
        {
1051
            WARN("GetId failed: %08lx\n", hr);
1052 1053
            continue;
        }
1054 1055 1056 1057

        if (str && !lstrcmpW(str, name))
        {
            CoTaskMemFree(str);
1058
            IMMDevice_AddRef(dev);
1059 1060
            *device = dev;
            return S_OK;
1061 1062 1063
        }
        CoTaskMemFree(str);
    }
1064 1065
    TRACE("Could not find device %s\n", debugstr_w(name));
    return E_INVALIDARG;
1066 1067
}

1068 1069 1070 1071 1072 1073
struct NotificationClientWrapper {
    IMMNotificationClient *client;
    struct list entry;
};

static struct list g_notif_clients = LIST_INIT(g_notif_clients);
1074
static HANDLE g_notif_thread;
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084

static CRITICAL_SECTION g_notif_lock;
static CRITICAL_SECTION_DEBUG g_notif_lock_debug =
{
    0, 0, &g_notif_lock,
    { &g_notif_lock_debug.ProcessLocksList, &g_notif_lock_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": g_notif_lock") }
};
static CRITICAL_SECTION g_notif_lock = { &g_notif_lock_debug, -1, 0, 0, 0, 0 };

1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
static void notify_clients(EDataFlow flow, ERole role, const WCHAR *id)
{
    struct NotificationClientWrapper *wrapper;
    LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients,
            struct NotificationClientWrapper, entry)
        IMMNotificationClient_OnDefaultDeviceChanged(wrapper->client, flow,
                role, id);

    /* Windows 7 treats changes to eConsole as changes to eMultimedia */
    if(role == eConsole)
        notify_clients(flow, eMultimedia, id);
}

1098 1099
static BOOL notify_if_changed(EDataFlow flow, ERole role, HKEY key,
                              const WCHAR *val_name, WCHAR *old_val, IMMDevice *def_dev)
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
{
    WCHAR new_val[64], *id;
    DWORD size;
    HRESULT hr;

    size = sizeof(new_val);
    if(RegQueryValueExW(key, val_name, 0, NULL,
                (BYTE*)new_val, &size) != ERROR_SUCCESS){
        if(old_val[0] != 0){
            /* set by user -> system default */
            if(def_dev){
                hr = IMMDevice_GetId(def_dev, &id);
                if(FAILED(hr)){
1113
                    ERR("GetId failed: %08lx\n", hr);
1114
                    return FALSE;
1115 1116 1117 1118 1119 1120 1121 1122
                }
            }else
                id = NULL;

            notify_clients(flow, role, id);
            old_val[0] = 0;
            CoTaskMemFree(id);

1123
            return TRUE;
1124 1125 1126
        }

        /* system default -> system default, noop */
1127
        return FALSE;
1128 1129 1130 1131
    }

    if(!lstrcmpW(old_val, new_val)){
        /* set by user -> same value */
1132
        return FALSE;
1133 1134 1135 1136 1137 1138
    }

    if(new_val[0] != 0){
        /* set by user -> different value */
        notify_clients(flow, role, new_val);
        memcpy(old_val, new_val, sizeof(new_val));
1139
        return TRUE;
1140 1141 1142 1143 1144 1145
    }

    /* set by user -> system default */
    if(def_dev){
        hr = IMMDevice_GetId(def_dev, &id);
        if(FAILED(hr)){
1146
            ERR("GetId failed: %08lx\n", hr);
1147
            return FALSE;
1148 1149 1150 1151 1152 1153 1154 1155
        }
    }else
        id = NULL;

    notify_clients(flow, role, id);
    old_val[0] = 0;
    CoTaskMemFree(id);

1156
    return TRUE;
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
}

static DWORD WINAPI notif_thread_proc(void *user)
{
    HKEY key;
    WCHAR reg_key[256];
    WCHAR out_name[64], vout_name[64], in_name[64], vin_name[64];
    DWORD size;

    lstrcpyW(reg_key, drv_keyW);
1167
    lstrcatW(reg_key, L"\\");
1168 1169 1170 1171
    lstrcatW(reg_key, drvs.module_name);

    if(RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0,
                MAXIMUM_ALLOWED, NULL, &key, NULL) != ERROR_SUCCESS){
1172
        ERR("RegCreateKeyEx failed: %lu\n", GetLastError());
1173 1174 1175 1176
        return 1;
    }

    size = sizeof(out_name);
1177
    if(RegQueryValueExW(key, L"DefaultOutput", 0, NULL, (BYTE*)out_name, &size) != ERROR_SUCCESS)
1178 1179 1180
        out_name[0] = 0;

    size = sizeof(vout_name);
1181
    if(RegQueryValueExW(key, L"DefaultVoiceOutput", 0, NULL, (BYTE*)vout_name, &size) != ERROR_SUCCESS)
1182 1183 1184
        vout_name[0] = 0;

    size = sizeof(in_name);
1185
    if(RegQueryValueExW(key, L"DefaultInput", 0, NULL, (BYTE*)in_name, &size) != ERROR_SUCCESS)
1186 1187 1188
        in_name[0] = 0;

    size = sizeof(vin_name);
1189
    if(RegQueryValueExW(key, L"DefaultVoiceInput", 0, NULL, (BYTE*)vin_name, &size) != ERROR_SUCCESS)
1190 1191 1192 1193 1194
        vin_name[0] = 0;

    while(1){
        if(RegNotifyChangeKeyValue(key, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
                    NULL, FALSE) != ERROR_SUCCESS){
1195
            ERR("RegNotifyChangeKeyValue failed: %lu\n", GetLastError());
1196 1197 1198 1199 1200 1201 1202
            RegCloseKey(key);
            g_notif_thread = NULL;
            return 1;
        }

        EnterCriticalSection(&g_notif_lock);

1203
        notify_if_changed(eRender, eConsole, key, L"DefaultOutput",
1204
                out_name, &MMDevice_def_play->IMMDevice_iface);
1205
        notify_if_changed(eRender, eCommunications, key, L"DefaultVoiceOutput",
1206
                vout_name, &MMDevice_def_play->IMMDevice_iface);
1207
        notify_if_changed(eCapture, eConsole, key, L"DefaultInput",
1208
                in_name, &MMDevice_def_rec->IMMDevice_iface);
1209
        notify_if_changed(eCapture, eCommunications, key, L"DefaultVoiceInput",
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
                vin_name, &MMDevice_def_rec->IMMDevice_iface);

        LeaveCriticalSection(&g_notif_lock);
    }

    RegCloseKey(key);

    g_notif_thread = NULL;

    return 0;
}

1222 1223
static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
{
1224
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1225 1226
    struct NotificationClientWrapper *wrapper;

1227
    TRACE("(%p)->(%p)\n", This, client);
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241

    if(!client)
        return E_POINTER;

    wrapper = HeapAlloc(GetProcessHeap(), 0, sizeof(*wrapper));
    if(!wrapper)
        return E_OUTOFMEMORY;

    wrapper->client = client;

    EnterCriticalSection(&g_notif_lock);

    list_add_tail(&g_notif_clients, &wrapper->entry);

1242 1243 1244
    if(!g_notif_thread){
        g_notif_thread = CreateThread(NULL, 0, notif_thread_proc, NULL, 0, NULL);
        if(!g_notif_thread)
1245
            ERR("CreateThread failed: %lu\n", GetLastError());
1246 1247
    }

1248 1249
    LeaveCriticalSection(&g_notif_lock);

1250
    return S_OK;
1251 1252 1253 1254
}

static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
{
1255
    MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1256
    struct NotificationClientWrapper *wrapper;
1257

1258
    TRACE("(%p)->(%p)\n", This, client);
1259 1260 1261 1262 1263 1264

    if(!client)
        return E_POINTER;

    EnterCriticalSection(&g_notif_lock);

1265
    LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients, struct NotificationClientWrapper, entry){
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
        if(wrapper->client == client){
            list_remove(&wrapper->entry);
            HeapFree(GetProcessHeap(), 0, wrapper);
            LeaveCriticalSection(&g_notif_lock);
            return S_OK;
        }
    }

    LeaveCriticalSection(&g_notif_lock);

    return E_NOTFOUND;
1277 1278
}

1279
static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
{
    MMDevEnum_QueryInterface,
    MMDevEnum_AddRef,
    MMDevEnum_Release,
    MMDevEnum_EnumAudioEndpoints,
    MMDevEnum_GetDefaultAudioEndpoint,
    MMDevEnum_GetDevice,
    MMDevEnum_RegisterEndpointNotificationCallback,
    MMDevEnum_UnregisterEndpointNotificationCallback
};
1290

1291 1292 1293
static MMDevEnumImpl enumerator =
{
    {&MMDevEnumVtbl},
1294
    1,
1295 1296
};

1297 1298 1299 1300 1301 1302 1303
static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
{
    MMDevPropStore *This;
    if (access != STGM_READ
        && access != STGM_WRITE
        && access != STGM_READWRITE)
    {
1304
        WARN("Invalid access %08lx\n", access);
1305 1306 1307
        return E_INVALIDARG;
    }
    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1308
    *ppv = &This->IPropertyStore_iface;
1309 1310
    if (!This)
        return E_OUTOFMEMORY;
1311
    This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
    This->ref = 1;
    This->parent = parent;
    This->access = access;
    return S_OK;
}

static void MMDevPropStore_Destroy(MMDevPropStore *This)
{
    HeapFree(GetProcessHeap(), 0, This);
}

static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
{
1325
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1326
    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1327 1328 1329 1330 1331

    if (!ppv)
        return E_POINTER;
    if (IsEqualIID(riid, &IID_IUnknown)
        || IsEqualIID(riid, &IID_IPropertyStore))
1332
        *ppv = &This->IPropertyStore_iface;
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
    else
        *ppv = NULL;
    if (!*ppv)
        return E_NOINTERFACE;
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
{
1343
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1344
    LONG ref = InterlockedIncrement(&This->ref);
1345
    TRACE("Refcount now %li\n", ref);
1346 1347 1348 1349 1350
    return ref;
}

static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
{
1351
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1352
    LONG ref = InterlockedDecrement(&This->ref);
1353
    TRACE("Refcount now %li\n", ref);
1354 1355 1356 1357 1358 1359 1360
    if (!ref)
        MMDevPropStore_Destroy(This);
    return ref;
}

static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
{
1361
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
    WCHAR buffer[50];
    DWORD i = 0;
    HKEY propkey;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", iface, nprops);
    if (!nprops)
        return E_POINTER;
    hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
    if (FAILED(hr))
        return hr;
    *nprops = 0;
    do {
1375
        DWORD len = ARRAY_SIZE(buffer);
1376
        if (RegEnumValueW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1377 1378
            break;
        i++;
1379
    } while (1);
1380
    RegCloseKey(propkey);
1381
    TRACE("Returning %li\n", i);
1382 1383 1384 1385 1386 1387
    *nprops = i;
    return S_OK;
}

static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
{
1388
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1389
    WCHAR buffer[50];
1390
    DWORD len = ARRAY_SIZE(buffer);
1391 1392 1393
    HRESULT hr;
    HKEY propkey;

1394
    TRACE("(%p)->(%lu,%p)\n", iface, prop, key);
1395 1396 1397 1398 1399 1400 1401
    if (!key)
        return E_POINTER;

    hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
    if (FAILED(hr))
        return hr;

1402 1403
    if (RegEnumValueW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
        || len <= 39)
1404
    {
1405
        WARN("GetAt %lu failed\n", prop);
1406 1407 1408
        return E_INVALIDARG;
    }
    RegCloseKey(propkey);
1409
    buffer[38] = 0;
1410
    CLSIDFromString(buffer, &key->fmtid);
1411
    key->pid = wcstol(&buffer[39], NULL, 10);
1412 1413 1414 1415 1416
    return S_OK;
}

static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
{
1417
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1418
    HRESULT hres;
1419
    TRACE("(%p)->(\"%s,%lu\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429

    if (!key || !pv)
        return E_POINTER;
    if (This->access != STGM_READ
        && This->access != STGM_READWRITE)
        return STG_E_ACCESSDENIED;

    /* Special case */
    if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
    {
1430
        pv->vt = VT_LPWSTR;
1431 1432
        pv->pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
        if (!pv->pwszVal)
1433
            return E_OUTOFMEMORY;
1434
        StringFromGUID2(&This->parent->devguid, pv->pwszVal, 39);
1435 1436 1437
        return S_OK;
    }

1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
    hres = MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
    if (FAILED(hres))
        return hres;

    if (WARN_ON(mmdevapi))
    {
        if ((IsEqualPropertyKey(*key, DEVPKEY_Device_FriendlyName) ||
             IsEqualPropertyKey(*key, DEVPKEY_DeviceInterface_FriendlyName) ||
             IsEqualPropertyKey(*key, DEVPKEY_Device_DeviceDesc)) &&
            pv->vt == VT_LPWSTR && wcslen(pv->pwszVal) > 62)
            WARN("Returned name exceeds length limit of some broken apps/libs, might crash: %s\n", debugstr_w(pv->pwszVal));
    }
    return hres;
1451 1452 1453 1454
}

static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
{
1455
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
1456
    TRACE("(%p)->(\"%s,%lu\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468

    if (!key || !pv)
        return E_POINTER;

    if (This->access != STGM_WRITE
        && This->access != STGM_READWRITE)
        return STG_E_ACCESSDENIED;
    return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
}

static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
{
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479
    MMDevPropStore *This = impl_from_IPropertyStore(iface);
    TRACE("(%p)\n", iface);

    if (This->access != STGM_WRITE
        && This->access != STGM_READWRITE)
        return STG_E_ACCESSDENIED;

    /* Does nothing - for mmdevapi, the propstore values are written on SetValue,
     * not on Commit. */

    return S_OK;
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492
}

static const IPropertyStoreVtbl MMDevPropVtbl =
{
    MMDevPropStore_QueryInterface,
    MMDevPropStore_AddRef,
    MMDevPropStore_Release,
    MMDevPropStore_GetCount,
    MMDevPropStore_GetAt,
    MMDevPropStore_GetValue,
    MMDevPropStore_SetValue,
    MMDevPropStore_Commit
};
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516


/* Property bag for IBaseFilter activation */
static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
{
    ERR("Should not be called\n");
    *ppv = NULL;
    return E_NOINTERFACE;
}

static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
{
    ERR("Should not be called\n");
    return 2;
}

static ULONG WINAPI PB_Release(IPropertyBag *iface)
{
    ERR("Should not be called\n");
    return 1;
}

static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
{
1517
    IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1518
    TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1519
    if (!lstrcmpW(name, L"DSGuid"))
1520 1521
    {
        WCHAR guidstr[39];
1522
        StringFromGUID2(&This->devguid, guidstr,ARRAY_SIZE(guidstr));
1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544
        var->n1.n2.vt = VT_BSTR;
        var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
        return S_OK;
    }
    ERR("Unknown property '%s' queried\n", debugstr_w(name));
    return E_FAIL;
}

static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
{
    ERR("Should not be called\n");
    return E_FAIL;
}

static const IPropertyBagVtbl PB_Vtbl =
{
    PB_QueryInterface,
    PB_AddRef,
    PB_Release,
    PB_Read,
    PB_Write
};
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558

static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
{
    return 2;
}

static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
{
    return 1;
}

static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
        REFPROPERTYKEY key, PROPVARIANT *pv)
{
1559
    TRACE("(static)->(\"%s,%lu\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1560 1561 1562 1563 1564 1565

    if (!key || !pv)
        return E_POINTER;

    if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
    {
1566
        INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1567
        pv->vt = VT_LPWSTR;
1568 1569
        pv->pwszVal = CoTaskMemAlloc(size);
        if (!pv->pwszVal)
1570
            return E_OUTOFMEMORY;
1571
        memcpy(pv->pwszVal, drvs.module_name, size);
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
        return S_OK;
    }

    return E_INVALIDARG;
}

static const IPropertyStoreVtbl info_device_ps_Vtbl =
{
    NULL,
    info_device_ps_AddRef,
    info_device_ps_Release,
    NULL,
    NULL,
    info_device_ps_GetValue,
    NULL,
    NULL
};

static IPropertyStore info_device_ps = {
    &info_device_ps_Vtbl
};

static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
{
    return 2;
}

static ULONG WINAPI info_device_Release(IMMDevice *iface)
{
    return 1;
}

static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
        DWORD access, IPropertyStore **ppv)
{
1607
    TRACE("(static)->(%lx, %p)\n", access, ppv);
1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625
    *ppv = &info_device_ps;
    return S_OK;
}

static const IMMDeviceVtbl info_device_Vtbl =
{
    NULL,
    info_device_AddRef,
    info_device_Release,
    NULL,
    info_device_OpenPropertyStore,
    NULL,
    NULL
};

static IMMDevice info_device = {
    &info_device_Vtbl
};