/* WinRT Windows.Gaming.Input implementation * * Copyright 2021 RĂ©mi Bernon 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 */ #include #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winreg.h" #include "winuser.h" #include "dbt.h" #include "ddk/hidclass.h" #include "ddk/hidsdi.h" #include "setupapi.h" #include "initguid.h" #include "private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(input); HINSTANCE windows_gaming_input; DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 ); static LRESULT CALLBACK devnotify_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { DEV_BROADCAST_DEVICEINTERFACE_W *iface; TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); if (msg == WM_DEVICECHANGE) { switch (wparam) { case DBT_DEVICEARRIVAL: iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)lparam; provider_create( iface->dbcc_name ); break; case DBT_DEVICEREMOVECOMPLETE: iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)lparam; provider_remove( iface->dbcc_name ); break; default: break; } } return DefWindowProcW( hwnd, msg, wparam, lparam ); } static void initialize_providers( void ) { char buffer[offsetof( SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath[MAX_PATH] )]; SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer; SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; GUID guid = GUID_DEVINTERFACE_WINEXINPUT; HDEVINFO set; DWORD i = 0; set = SetupDiGetClassDevsW( NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE | DIGCF_PRESENT ); while (SetupDiEnumDeviceInterfaces( set, NULL, &guid, i++, &iface )) { detail->cbSize = sizeof(*detail); if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, NULL )) continue; provider_create( detail->DevicePath ); } HidD_GetHidGuid( &guid ); while (SetupDiEnumDeviceInterfaces( set, NULL, &guid, i++, &iface )) { detail->cbSize = sizeof(*detail); if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, NULL )) continue; provider_create( detail->DevicePath ); } SetupDiDestroyDeviceInfoList( set ); } static DWORD WINAPI monitor_thread_proc( void *param ) { DEV_BROADCAST_DEVICEINTERFACE_W filter = { .dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), .dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE, }; WNDCLASSEXW wndclass = { .cbSize = sizeof(WNDCLASSEXW), .lpszClassName = L"__wine_gaming_input_devnotify", .lpfnWndProc = devnotify_wndproc, }; HANDLE start_event = param; HDEVNOTIFY devnotify; HMODULE module; HWND hwnd; MSG msg; GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (void *)windows_gaming_input, &module ); RegisterClassExW( &wndclass ); hwnd = CreateWindowExW( 0, wndclass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL ); devnotify = RegisterDeviceNotificationW( hwnd, &filter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES ); initialize_providers(); SetEvent( start_event ); do { while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE )) { TranslateMessage( &msg ); DispatchMessageW( &msg ); } } while (!MsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE )); UnregisterDeviceNotification( devnotify ); DestroyWindow( hwnd ); UnregisterClassW( wndclass.lpszClassName, NULL ); FreeLibraryAndExitThread( module, 0 ); return 0; } static BOOL WINAPI start_monitor_thread( INIT_ONCE *once, void *param, void **context ) { HANDLE thread, start_event; start_event = CreateEventA( NULL, FALSE, FALSE, NULL ); if (!start_event) ERR( "Failed to create start event, error %lu\n", GetLastError() ); thread = CreateThread( NULL, 0, monitor_thread_proc, start_event, 0, NULL ); if (!thread) ERR( "Failed to create monitor thread, error %lu\n", GetLastError() ); else { WaitForSingleObject( start_event, INFINITE ); CloseHandle( thread ); } CloseHandle( start_event ); return !!thread; } HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) { FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); return CLASS_E_CLASSNOTAVAILABLE; } HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory **factory ) { static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; const WCHAR *buffer = WindowsGetStringRawBuffer( class_str, NULL ); TRACE( "class %s, factory %p.\n", debugstr_w(buffer), factory ); InitOnceExecuteOnce( &init_once, start_monitor_thread, NULL, NULL ); *factory = NULL; if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_RawGameController )) ICustomGameControllerFactory_QueryInterface( controller_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_Gamepad )) ICustomGameControllerFactory_QueryInterface( gamepad_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_RacingWheel )) ICustomGameControllerFactory_QueryInterface( racing_wheel_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_Custom_GameControllerFactoryManager )) IGameControllerFactoryManagerStatics2_QueryInterface( manager_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect )) IInspectable_QueryInterface( constant_effect_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect )) IInspectable_QueryInterface( ramp_effect_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect )) IInspectable_QueryInterface( periodic_effect_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect )) IInspectable_QueryInterface( condition_effect_factory, &IID_IActivationFactory, (void **)factory ); if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; } BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) { TRACE( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved ); switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( instance ); windows_gaming_input = instance; break; } return TRUE; }