mmdevapi: Automatically select the correct driver.
diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
index 57541ab..c2c3562 100644
--- a/dlls/mmdevapi/main.c
+++ b/dlls/mmdevapi/main.c
@@ -53,7 +53,22 @@
DriverFuncs drvs;
-static BOOL load_driver(const WCHAR *name)
+static const char *get_priority_string(int prio)
+{
+ switch(prio){
+ case Priority_Unavailable:
+ return "Unavailable";
+ case Priority_Low:
+ return "Low";
+ case Priority_Neutral:
+ return "Neutral";
+ case Priority_Preferred:
+ return "Preferred";
+ }
+ return "Invalid";
+}
+
+static BOOL load_driver(const WCHAR *name, DriverFuncs *driver)
{
WCHAR driver_module[264];
static const WCHAR wineW[] = {'w','i','n','e',0};
@@ -65,75 +80,89 @@
TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module));
- drvs.module = LoadLibraryW(driver_module);
- if(!drvs.module){
+ driver->module = LoadLibraryW(driver_module);
+ if(!driver->module){
TRACE("Unable to load %s: %u\n", wine_dbgstr_w(driver_module),
GetLastError());
return FALSE;
}
-#define LDFC(n) do { drvs.p##n = (void*)GetProcAddress(drvs.module, #n);\
- if(!drvs.p##n) { FreeLibrary(drvs.module); return FALSE; } } while(0)
+#define LDFC(n) do { driver->p##n = (void*)GetProcAddress(driver->module, #n);\
+ if(!driver->p##n) { FreeLibrary(driver->module); return FALSE; } } while(0)
+ LDFC(GetPriority);
LDFC(GetEndpointIDs);
LDFC(GetAudioEndpoint);
LDFC(GetAudioSessionManager);
#undef LDFC
- lstrcpyW(drvs.module_name, driver_module);
- TRACE("Successfully loaded %s\n", wine_dbgstr_w(driver_module));
+ driver->priority = driver->pGetPriority();
+ lstrcpyW(driver->module_name, driver_module);
+
+ TRACE("Successfully loaded %s with priority %s\n",
+ wine_dbgstr_w(driver_module), get_priority_string(driver->priority));
return TRUE;
}
static BOOL init_driver(void)
{
- static const WCHAR alsaW[] = {'a','l','s','a',0};
- static const WCHAR ossW[] = {'o','s','s',0};
- static const WCHAR coreaudioW[] = {'c','o','r','e','a','u','d','i','o',0};
- static const WCHAR *default_drivers[] = { alsaW, coreaudioW, ossW };
static const WCHAR drv_key[] = {'S','o','f','t','w','a','r','e','\\',
'W','i','n','e','\\','D','r','i','v','e','r','s',0};
static const WCHAR drv_value[] = {'A','u','d','i','o',0};
+
+ static WCHAR default_list[] = {'a','l','s','a',',','o','s','s',',',
+ 'c','o','r','e','a','u','d','i','o',0};
+
+ DriverFuncs driver;
HKEY key;
- UINT i;
+ WCHAR reg_list[256], *p, *next, *driver_list = default_list;
if(drvs.module)
return TRUE;
if(RegOpenKeyW(HKEY_CURRENT_USER, drv_key, &key) == ERROR_SUCCESS){
- WCHAR driver_name[256], *p, *next;
- DWORD size = sizeof(driver_name);
+ DWORD size = sizeof(reg_list);
- if(RegQueryValueExW(key, drv_value, 0, NULL, (BYTE*)driver_name,
+ if(RegQueryValueExW(key, drv_value, 0, NULL, (BYTE*)reg_list,
&size) == ERROR_SUCCESS){
- RegCloseKey(key);
-
- if(driver_name[0] == '\0')
+ if(reg_list[0] == '\0'){
+ TRACE("User explicitly chose no driver\n");
+ RegCloseKey(key);
return TRUE;
-
- for(next = p = driver_name; next; p = next + 1){
- next = strchrW(p, ',');
- if(next)
- *next = '\0';
-
- if(load_driver(p))
- return TRUE;
-
- TRACE("Failed to load driver: %s\n", wine_dbgstr_w(driver_name));
}
- ERR("No drivers in the registry loaded successfully!\n");
- return FALSE;
+ driver_list = reg_list;
}
RegCloseKey(key);
}
- for(i = 0; i < sizeof(default_drivers)/sizeof(*default_drivers); ++i)
- if(load_driver(default_drivers[i]))
- return TRUE;
+ TRACE("Loading driver list %s\n", wine_dbgstr_w(driver_list));
+ for(next = p = driver_list; next; p = next + 1){
+ next = strchrW(p, ',');
+ if(next)
+ *next = '\0';
- return FALSE;
+ driver.priority = Priority_Unavailable;
+ if(load_driver(p, &driver)){
+ if(driver.priority == Priority_Unavailable)
+ FreeLibrary(driver.module);
+ else if(!drvs.module || driver.priority > drvs.priority){
+ TRACE("Selecting driver %s with priority %s\n",
+ wine_dbgstr_w(p), get_priority_string(driver.priority));
+ if(drvs.module)
+ FreeLibrary(drvs.module);
+ drvs = driver;
+ }else
+ FreeLibrary(driver.module);
+ }else
+ TRACE("Failed to load driver %s\n", wine_dbgstr_w(p));
+
+ if(next)
+ *next = ',';
+ }
+
+ return drvs.module ? TRUE : FALSE;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
diff --git a/dlls/mmdevapi/mmdevapi.h b/dlls/mmdevapi/mmdevapi.h
index e61c539..6ad1ab3 100644
--- a/dlls/mmdevapi/mmdevapi.h
+++ b/dlls/mmdevapi/mmdevapi.h
@@ -25,9 +25,24 @@
extern HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv) DECLSPEC_HIDDEN;
+/* Changes to this enum must be synced in drivers. */
+enum _DriverPriority {
+ Priority_Unavailable = 0, /* driver won't work */
+ Priority_Low, /* driver may work, but unlikely */
+ Priority_Neutral, /* driver makes no judgment */
+ Priority_Preferred /* driver thinks it's correct */
+};
+
typedef struct _DriverFuncs {
HMODULE module;
WCHAR module_name[64];
+ int priority;
+
+ /* Returns a "priority" value for the driver. Highest priority wins.
+ * If multiple drivers think they are valid, they will return a
+ * priority value reflecting the likelihood that they are actually
+ * valid. See enum _DriverPriority. */
+ int WINAPI (*pGetPriority)(void);
/* ids gets an array of human-friendly endpoint names
* keys gets an array of driver-specific stuff that is used
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 24dbeef..180dbc7 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -223,6 +223,19 @@
return TRUE;
}
+/* From <dlls/mmdevapi/mmdevapi.h> */
+enum DriverPriority {
+ Priority_Unavailable = 0,
+ Priority_Low,
+ Priority_Neutral,
+ Priority_Preferred
+};
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+ return Priority_Neutral;
+}
+
static HRESULT alsa_get_card_devices(EDataFlow flow, WCHAR **ids, char **keys,
UINT *num, snd_ctl_t *ctl, int card, const WCHAR *cardnameW)
{
diff --git a/dlls/winealsa.drv/winealsa.drv.spec b/dlls/winealsa.drv/winealsa.drv.spec
index 5840f46..c87ec17 100644
--- a/dlls/winealsa.drv/winealsa.drv.spec
+++ b/dlls/winealsa.drv/winealsa.drv.spec
@@ -7,6 +7,7 @@
@ stdcall -private wodMessage(long long long long long) ALSA_wodMessage
# MMDevAPI driver functions
+@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr long ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
diff --git a/dlls/winecoreaudio.drv/mmdevdrv.c b/dlls/winecoreaudio.drv/mmdevdrv.c
index 8311a29..a951673 100644
--- a/dlls/winecoreaudio.drv/mmdevdrv.c
+++ b/dlls/winecoreaudio.drv/mmdevdrv.c
@@ -242,6 +242,19 @@
return TRUE;
}
+/* From <dlls/mmdevapi/mmdevapi.h> */
+enum DriverPriority {
+ Priority_Unavailable = 0,
+ Priority_Low,
+ Priority_Neutral,
+ Priority_Preferred
+};
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+ return Priority_Neutral;
+}
+
HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids,
AudioDeviceID ***keys, UINT *num, UINT *def_index)
{
diff --git a/dlls/winecoreaudio.drv/winecoreaudio.drv.spec b/dlls/winecoreaudio.drv/winecoreaudio.drv.spec
index 14d9548..f1126c1 100644
--- a/dlls/winecoreaudio.drv/winecoreaudio.drv.spec
+++ b/dlls/winecoreaudio.drv/winecoreaudio.drv.spec
@@ -7,6 +7,7 @@
@ stdcall -private mxdMessage(long long long long long) CoreAudio_mxdMessage
# MMDevAPI driver functions
+@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(str long ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
diff --git a/dlls/wineoss.drv/mmdevdrv.c b/dlls/wineoss.drv/mmdevdrv.c
index 64d60fe..fe0c1d8 100644
--- a/dlls/wineoss.drv/mmdevdrv.c
+++ b/dlls/wineoss.drv/mmdevdrv.c
@@ -233,6 +233,54 @@
return TRUE;
}
+/* From <dlls/mmdevapi/mmdevapi.h> */
+enum DriverPriority {
+ Priority_Unavailable = 0,
+ Priority_Low,
+ Priority_Neutral,
+ Priority_Preferred
+};
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+ int mixer_fd;
+ oss_sysinfo sysinfo;
+
+ /* Attempt to determine if we are running on OSS or ALSA's OSS
+ * compatibility layer. There is no official way to do that, so just check
+ * for validity as best as possible, without rejecting valid OSS
+ * implementations. */
+
+ mixer_fd = open("/dev/mixer", O_RDONLY, 0);
+ if(mixer_fd < 0){
+ TRACE("Priority_Unavailable: open failed\n");
+ return Priority_Unavailable;
+ }
+
+ sysinfo.version[0] = 0xFF;
+ sysinfo.versionnum = ~0;
+ if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
+ TRACE("Priority_Unavailable: ioctl failed\n");
+ close(mixer_fd);
+ return Priority_Unavailable;
+ }
+
+ close(mixer_fd);
+
+ if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
+ TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
+ return Priority_Low;
+ }
+ if(sysinfo.versionnum & 0x80000000){
+ TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
+ return Priority_Low;
+ }
+
+ TRACE("Priority_Preferred: Seems like valid OSS!\n");
+
+ return Priority_Preferred;
+}
+
static UINT get_default_index(EDataFlow flow, char **keys, UINT num)
{
int fd = -1, err, i;
diff --git a/dlls/wineoss.drv/wineoss.drv.spec b/dlls/wineoss.drv/wineoss.drv.spec
index 5e5c4c1..4b12196 100644
--- a/dlls/wineoss.drv/wineoss.drv.spec
+++ b/dlls/wineoss.drv/wineoss.drv.spec
@@ -8,6 +8,7 @@
@ stdcall -private wodMessage(long long long long long) OSS_wodMessage
# MMDevAPI driver functions
+@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr long ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager