diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c index 57541aba10c..c2c3562e712 100644 --- a/dlls/mmdevapi/main.c +++ b/dlls/mmdevapi/main.c @@ -53,7 +53,22 @@ static HINSTANCE instance; 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 @@ static BOOL load_driver(const WCHAR *name) 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 e61c539d4a2..6ad1ab371c8 100644 --- a/dlls/mmdevapi/mmdevapi.h +++ b/dlls/mmdevapi/mmdevapi.h @@ -25,9 +25,24 @@ extern void MMDevEnum_Free(void) DECLSPEC_HIDDEN; 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 24dbeef1ea9..180dbc74870 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -223,6 +223,19 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) return TRUE; } +/* From */ +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 5840f46e236..c87ec17c8e3 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 8311a29d1a0..a951673ef6a 100644 --- a/dlls/winecoreaudio.drv/mmdevdrv.c +++ b/dlls/winecoreaudio.drv/mmdevdrv.c @@ -242,6 +242,19 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) return TRUE; } +/* From */ +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 14d95488f90..f1126c1a359 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 64d60fec983..fe0c1d84489 100644 --- a/dlls/wineoss.drv/mmdevdrv.c +++ b/dlls/wineoss.drv/mmdevdrv.c @@ -233,6 +233,54 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) return TRUE; } +/* From */ +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 5e5c4c1e975..4b1219655b6 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