Based on a patch by Mark Harmstone mark@harmstone.com.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/winepulse.drv/mmdevdrv.c | 57 +++++++++++++++++++++++++++++ dlls/winepulse.drv/pulse.c | 69 ++++++++++++++++++++++++++++++++--- dlls/winepulse.drv/unixlib.h | 18 +++++++++ 3 files changed, 139 insertions(+), 5 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 0e4fe9e..f07b0ca 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -2524,8 +2524,49 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, return S_OK; }
+static HRESULT get_device_path(struct get_device_info_params *params, GUID *guid, PROPVARIANT *out) +{ + UINT serial_number; + const WCHAR *fmt; + WCHAR path[128]; + int len; + + switch (params->bus_type) { + case phys_device_bus_pci: + fmt = L"{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X"; + break; + case phys_device_bus_usb: + fmt = L"{1}.USB\VID_%04X&PID_%04X\%u&%08X"; + break; + default: + return E_FAIL; + } + + /* As hardly any audio devices have serial numbers, Windows instead + appears to use a persistent random number. We emulate this here + by instead using the last 8 hex digits of the GUID. */ + serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7]; + + len = swprintf(path, ARRAY_SIZE(path), fmt, params->vendor_id, params->product_id, params->index, serial_number); + if (len < 0) + return E_FAIL; + + out->vt = VT_LPWSTR; + out->pwszVal = CoTaskMemAlloc((len + 1) * sizeof(WCHAR)); + if (!out->pwszVal) + return E_OUTOFMEMORY; + + wcscpy(out->pwszVal, path); + return S_OK; +} + HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out) { + static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */ + {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2 + }; + struct get_device_info_params params; + TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) { @@ -2535,5 +2576,21 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI return out->ulVal ? S_OK : E_FAIL; }
+ if (!get_pulse_name_by_guid(guid, params.device, ¶ms.dataflow)) + return E_FAIL; + + pulse_call(get_device_info, ¶ms); + if (params.result != S_OK) + return params.result; + + if (IsEqualPropertyKey(*prop, devicepath_key)) + return get_device_path(¶ms, guid, out); + + if (IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_FormFactor)) { + out->vt = VT_UI4; + out->ulVal = params.form; + return S_OK; + } + return E_NOTIMPL; } diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 8934387..3204c08 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -83,6 +83,10 @@ typedef struct _ACPacket
typedef struct _PhysDevice { struct list entry; + enum phys_device_bus_type bus_type; + USHORT vendor_id, product_id; + EndpointFormFactor form; + UINT index; char device[0]; } PhysDevice;
@@ -468,7 +472,33 @@ done: free(wname); }
-static void pulse_add_device(struct list *list, const char *device) +static void fill_device_info(PhysDevice *dev, pa_proplist *p) +{ + const char *buffer; + + dev->bus_type = phys_device_bus_invalid; + dev->vendor_id = 0; + dev->product_id = 0; + + if (!p) + return; + + if ((buffer = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) { + if (!strcmp(buffer, "usb")) + dev->bus_type = phys_device_bus_usb; + else if (!strcmp(buffer, "pci")) + dev->bus_type = phys_device_bus_pci; + } + + if ((buffer = pa_proplist_gets(p, PA_PROP_DEVICE_VENDOR_ID))) + dev->vendor_id = strtol(buffer, NULL, 16); + + if ((buffer = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_ID))) + dev->product_id = strtol(buffer, NULL, 16); +} + +static void pulse_add_device(struct list *list, pa_proplist *proplist, int index, EndpointFormFactor form, + const char *device) { DWORD len = strlen(device); PhysDevice *dev = malloc(FIELD_OFFSET(PhysDevice, device[len + 1])); @@ -476,6 +506,9 @@ static void pulse_add_device(struct list *list, const char *device) if (!dev) return; memcpy(dev->device, device, len + 1); + dev->form = form; + dev->index = index; + fill_device_info(dev, proplist);
list_add_tail(list, &dev->entry); } @@ -488,7 +521,7 @@ static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
store_device_info(eRender, i->name, i->description); - pulse_add_device(&g_phys_speakers, i->name); + pulse_add_device(&g_phys_speakers, i->proplist, i->index, Speakers, i->name); } }
@@ -496,7 +529,8 @@ static void pulse_phys_sources_cb(pa_context *c, const pa_source_info *i, int eo { if (i && i->name && i->name[0]) { store_device_info(eCapture, i->name, i->description); - pulse_add_device(&g_phys_sources, i->name); + pulse_add_device(&g_phys_sources, i->proplist, i->index, + (i->monitor_of_sink == PA_INVALID_INDEX) ? Microphone : LineLevel, i->name); } }
@@ -720,8 +754,8 @@ static NTSTATUS pulse_test_connect(void *args) g_phys_speakers_mask = 0;
devices_key = open_devices_key(); - pulse_add_device(&g_phys_speakers, ""); - pulse_add_device(&g_phys_sources, ""); + pulse_add_device(&g_phys_speakers, NULL, 0, Speakers, ""); + pulse_add_device(&g_phys_sources, NULL, 0, Microphone, "");
o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL); if (o) { @@ -768,6 +802,30 @@ fail: return STATUS_SUCCESS; }
+static NTSTATUS pulse_get_device_info(void *args) +{ + struct get_device_info_params *params = args; + const struct list *const list = (params->dataflow == eRender) ? &g_phys_speakers : &g_phys_sources; + const char *device = params->device; + PhysDevice *dev; + + LIST_FOR_EACH_ENTRY(dev, list, PhysDevice, entry) { + if (!strcmp(device, dev->device)) { + params->bus_type = dev->bus_type; + params->vendor_id = dev->vendor_id; + params->product_id = dev->product_id; + params->index = dev->index; + params->form = dev->form; + params->result = S_OK; + return STATUS_SUCCESS; + } + } + + WARN("Unknown device %s\n", device); + params->result = E_FAIL; + return STATUS_SUCCESS; +} + static DWORD get_channel_mask(unsigned int channels) { switch(channels) { @@ -2144,5 +2202,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = pulse_set_volumes, pulse_set_event_handle, pulse_test_connect, + pulse_get_device_info, pulse_is_started, }; diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h index 5445a0f..7383110 100644 --- a/dlls/winepulse.drv/unixlib.h +++ b/dlls/winepulse.drv/unixlib.h @@ -181,6 +181,23 @@ struct test_connect_params struct pulse_config *config; };
+enum phys_device_bus_type { + phys_device_bus_invalid = -1, + phys_device_bus_pci, + phys_device_bus_usb +}; + +struct get_device_info_params +{ + char device[256]; + EDataFlow dataflow; + enum phys_device_bus_type bus_type; + USHORT vendor_id, product_id; + EndpointFormFactor form; + UINT index; + HRESULT result; +}; + struct is_started_params { struct pulse_stream *stream; @@ -211,5 +228,6 @@ enum unix_funcs set_volumes, set_event_handle, test_connect, + get_device_info, is_started, };