diff --git a/.vscode/launch.json b/.vscode/launch.json index e9f1f8e..7cbffa8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,16 +2,28 @@ "version": "0.2.0", "configurations": [ { - "name": "Build and debug", + "name": "Build and debug stadia-vigem", "type": "cppvsdbg", "request": "launch", - "program": "bin/stadia-vigem.exe", + "program": "bin/stadia-vigem-x64.exe", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, - "preLaunchTask": "Build debug binary" + "preLaunchTask": "Build debug binary (x64)" + }, + { + "name": "Build and debug stadia-tester", + "type": "cppvsdbg", + "request": "launch", + "program": "bin/stadia-tester-x64.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "preLaunchTask": "Build debug binary (x64)" } ] } \ No newline at end of file diff --git a/Build.ps1 b/Build.ps1 index ac18fa8..fb5716a 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -9,8 +9,6 @@ $script:CommonFlags = @("/Zi", "/W4", "/EHsc", "/DWIN32", "/D_UNICODE", "/DUNICO $script:DebugFlags = @( "/Od" ) $script:ReleaseFlags = @("/GL", "/O2") -$script:OutputName = "stadia-vigem-" - function Import-Prerequisites { if ($SkipBuildToolsSetup -eq $true) { @@ -48,13 +46,61 @@ function Invoke-BuildTools { Invoke-CmdScript "$($latestVsInstallationInfo.InstallationPath)\VC\Auxiliary\Build\vcvarsall.bat" $Architecture } -function Invoke-Build { +function Invoke-Build-libstadia { + param ( + $Architecture + ) + + $OutputName = "libstadia-$Architecture.lib" + $Flags = If ($Configuration -eq "DEBUG") {$script:DebugFlags} else {$script:ReleaseFlags} + + $StopWatch = New-Object -TypeName System.Diagnostics.Stopwatch + + Write-Host "*** ${OutputName}: Build started ***" + Write-Host + + $StopWatch.Start() + + & "cl.exe" /c $Flags $CommonFlags /Ilibstadia/include /Foobj/libstadia/ libstadia/src/*.c + & "lib.exe" /out:bin/$OutputName obj/libstadia/*.obj + + $StopWatch.Stop() + + Write-Host + Write-Host "*** ${OutputName}: Build finished in $($StopWatch.Elapsed) ***" +} + +function Invoke-Build-Stadia-Tester { + param ( + $Architecture + ) + + $OutputName = "stadia-tester-$Architecture.exe" + $Flags = If ($Configuration -eq "DEBUG") {$script:DebugFlags} else {$script:ReleaseFlags} + + $StopWatch = New-Object -TypeName System.Diagnostics.Stopwatch + + Write-Host "*** ${OutputName}: Build started ***" + Write-Host + + $StopWatch.Start() + + & "cl.exe" $Flags $CommonFlags /Ilibstadia/include /Foobj/stadia-tester/ /Febin/$OutputName stadia-tester/src/*.c User32.lib + + $StopWatch.Stop() + + Write-Host + Write-Host "*** ${OutputName}: Build finished in $($StopWatch.Elapsed) ***" +} + +function Invoke-Build-Stadia-ViGEm { param ( $Architecture ) - $OutputName = "$script:OutputName$Architecture.exe" + $OutputName = "stadia-vigem-$Architecture.exe" $Flags = If ($Configuration -eq "DEBUG") {$script:DebugFlags} else {$script:ReleaseFlags} + $LibraryPath = "bin/libstadia-$Architecture.lib" $StopWatch = New-Object -TypeName System.Diagnostics.Stopwatch @@ -63,8 +109,8 @@ function Invoke-Build { $StopWatch.Start() - & "rc.exe" /foobj/stadia-vigem.res res/res.rc - & "cl.exe" $Flags $CommonFlags /IViGEmClient/include /Foobj/ /Febin/$OutputName ViGEmClient/src/*.cpp obj/stadia-vigem.res src/*.c + & "rc.exe" /foobj/stadia-vigem/stadia-vigem.res stadia-vigem/res/res.rc + & "cl.exe" $Flags $CommonFlags /Ilibstadia/include /IViGEmClient/include /Istadia-vigem/include /Foobj/stadia-vigem/ /Febin/$OutputName ViGEmClient/src/*.cpp obj/stadia-vigem/stadia-vigem.res stadia-vigem/src/*.c $LibraryPath $StopWatch.Stop() @@ -76,6 +122,9 @@ function Invoke-Build { New-Item -Path "bin" -ItemType Directory -Force > $null New-Item -Path "obj" -ItemType Directory -Force > $null +New-Item -Path "obj/libstadia" -ItemType Directory -Force > $null +New-Item -Path "obj/stadia-tester" -ItemType Directory -Force > $null +New-Item -Path "obj/stadia-vigem" -ItemType Directory -Force > $null Import-Prerequisites @@ -83,12 +132,16 @@ Write-Host "-- Build started. Configuration: $Configuration, Architecture: $Arch if ($Architecture -eq "x86" -Or $Architecture -eq "ALL") { Invoke-BuildTools -Architecture "x86" - Invoke-Build -Architecture "x86" + Invoke-Build-libstadia -Architecture "x86" + Invoke-Build-Stadia-Tester -Architecture "x86" + Invoke-Build-Stadia-ViGEm -Architecture "x86" } if ($Architecture -eq "x64" -Or $Architecture -eq "ALL") { Invoke-BuildTools -Architecture "x64" - Invoke-Build -Architecture "x64" + Invoke-Build-libstadia -Architecture "x64" + #Invoke-Build-Stadia-Tester -Architecture "x64" + Invoke-Build-Stadia-ViGEm -Architecture "x64" } Write-Host "-- Build completed. --" diff --git a/README.md b/README.md index 40eeb61..3cdd0cc 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Stadia-ViGEm -Xbox 360 controller emulation for Stadia controller. Supports multiple devices and vibration. Forked from Mi-ViGEm (https://github.com/grayver/Mi-ViGEm) by grayver. -Xbox 360 emulation driver is provided by ViGEm (https://github.com/ViGEm/ViGEmBus), by Benjamin Höglinger. +Xbox 360 controller emulation for Stadia controller. Supports controllers connected via USB & bluetooth. Supports multiple devices and vibration (wired only). Forked from Mi-ViGEm (https://github.com/grayver/Mi-ViGEm) by grayver. +Xbox 360 controller emulation driver is provided by ViGEm (https://github.com/ViGEm/ViGEmBus), by Benjamin Höglinger. ## Requirements -- Windows 10 (should work on Windows 7 and 8 also) +- Windows 11 (should work on Windows 7-10 also) - ViGEm bus installed (can be downloaded [here](https://github.com/ViGEm/ViGEmBus/releases)) ## How it works @@ -18,7 +18,7 @@ Stadia-ViGEm creates a virtual Xbox 360 controller which results in double input - Click "+" button - Browse to the Stadia-ViGEm executable you normally use (Stadia-ViGEm-x86.exe or Stadia-ViGEm-x64.exe) - On Devices tab: - - Tick box next to the Stadia controller entry (my controller is named as "Google LLC Stadia Controller rev. A") + - Tick box next to the Stadia controller entry (wired controllers are named "Google LLC Stadia Controller rev. A" & bluetooth controllers are named "HID-compliant game controller") - Tick "Enable device hiding" at the bottom of the window - Reboot your PC diff --git a/src/hid.h b/libstadia/include/hid.h similarity index 100% rename from src/hid.h rename to libstadia/include/hid.h diff --git a/src/stadia.h b/libstadia/include/stadia.h similarity index 70% rename from src/stadia.h rename to libstadia/include/stadia.h index 2c2a247..7b52fe0 100644 --- a/src/stadia.h +++ b/libstadia/include/stadia.h @@ -10,10 +10,13 @@ #define STADIA_ERROR_VIBRATION_INIT_FAILURE 0x1 #define STADIA_ERROR_THREAD_CREATE_FAILURE 0x2 -#define STADIA_HW_VENDOR_ID 0x18D1 -#define STADIA_HW_PRODUCT_ID 0x9400 +#define STADIA_USB_HW_VENDOR_ID 0x18D1 +#define STADIA_USB_HW_PRODUCT_ID 0x9400 +#define STADIA_USB_HW_FILTER TEXT("VID_18D1&PID_9400") -#define STADIA_HW_FILTER TEXT("VID_18D1&PID_9400") +#define STADIA_BLT_HW_VENDOR_ID 0x18D1 +#define STADIA_BLT_HW_PRODUCT_ID 0x9400 +#define STADIA_BLT_HW_FILTER TEXT("vid&0218d1_pid&9400") #define STADIA_BUTTON_NONE 0x00000000 #define STADIA_BUTTON_A 0x00000001 @@ -32,9 +35,6 @@ #define STADIA_BUTTON_MENU 0x00002000 #define STADIA_BUTTON_STADIA_BTN 0x00004000 -void (*stadia_update_callback)(struct stadia_controller *, struct stadia_state *); -void (*stadia_destroy_callback)(struct stadia_controller *); - struct stadia_state { DWORD buttons; @@ -49,7 +49,27 @@ struct stadia_state BYTE right_trigger; }; -struct stadia_controller; +struct stadia_controller +{ + struct hid_device *device; + + SRWLOCK state_lock; + struct stadia_state state; + + BOOL active; + HANDLE stopping_event; + HANDLE output_event; + + SRWLOCK vibration_lock; + BYTE small_motor; + BYTE big_motor; + + HANDLE input_thread; + HANDLE output_thread; +}; + +void (*stadia_update_callback)(struct stadia_controller *, struct stadia_state *); +void (*stadia_destroy_callback)(struct stadia_controller *); struct stadia_controller *stadia_controller_create(struct hid_device *device); void stadia_controller_set_vibration(struct stadia_controller *controller, BYTE small_motor, BYTE big_motor); diff --git a/src/utils.h b/libstadia/include/utils.h similarity index 100% rename from src/utils.h rename to libstadia/include/utils.h diff --git a/src/hid.c b/libstadia/src/hid.c similarity index 100% rename from src/hid.c rename to libstadia/src/hid.c diff --git a/src/stadia.c b/libstadia/src/stadia.c similarity index 94% rename from src/stadia.c rename to libstadia/src/stadia.c index 5fb143f..e0aec9d 100644 --- a/src/stadia.c +++ b/libstadia/src/stadia.c @@ -22,25 +22,6 @@ static const BYTE init_vibration[5] = {STADIA_VIBRATION_IDENTIFIER, 0x00, 0x00, 0x00, 0x00}; -struct stadia_controller -{ - struct hid_device *device; - - SRWLOCK state_lock; - struct stadia_state state; - - BOOL active; - HANDLE stopping_event; - HANDLE output_event; - - SRWLOCK vibration_lock; - BYTE small_motor; - BYTE big_motor; - - HANDLE input_thread; - HANDLE output_thread; -}; - static const DWORD dpad_map[8] = { STADIA_BUTTON_UP, @@ -144,10 +125,12 @@ static DWORD WINAPI _stadia_output_thread(LPVOID lparam) struct stadia_controller *stadia_controller_create(struct hid_device *device) { - if (hid_send_output_report(device, init_vibration, sizeof(init_vibration), STADIA_READ_TIMEOUT) <= 0) + if (!hid_send_output_report(device, init_vibration, sizeof(init_vibration), STADIA_READ_TIMEOUT) <= 0) { last_error = STADIA_ERROR_VIBRATION_INIT_FAILURE; - return NULL; + // Don't error out for now. + // TODO: Fix vibration for bluetooth controllers. + //return NULL; } SECURITY_ATTRIBUTES security = {.nLength = sizeof(SECURITY_ATTRIBUTES), diff --git a/src/utils.c b/libstadia/src/utils.c similarity index 100% rename from src/utils.c rename to libstadia/src/utils.c diff --git a/stadia-tester/src/main.c b/stadia-tester/src/main.c new file mode 100644 index 0000000..eab6c2c --- /dev/null +++ b/stadia-tester/src/main.c @@ -0,0 +1,86 @@ +#include +#include + +#include +#include + +#define DEVICE_USAGE_GAMEPAD 0x05 + +int main() { + printf("Looking for HID controller(s)...\n"); + + HWND hwnd = GetConsoleWindow(); + + RAWINPUTDEVICE devices[1]; + devices[0].dwFlags = RIDEV_INPUTSINK; + devices[0].usUsage = DEVICE_USAGE_GAMEPAD; + devices[0].usUsagePage = 1; + devices[0].hwndTarget = hwnd; + + UINT count = 0; + if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) == -1) { + printf("GetRawInputDeviceList failed. Error code: %d\n", GetLastError()); + return GetLastError(); + } + + printf("Found %d gamepad devices.\n", count); + + PRAWINPUTDEVICELIST device_list = malloc(count * sizeof(RAWINPUTDEVICELIST)); + + if (GetRawInputDeviceList(device_list, &count, sizeof(RAWINPUTDEVICELIST)) == -1) { + printf("GetRawInputDeviceList failed. Error code: %d\n", GetLastError()); + return GetLastError(); + } + + for (UINT i = 0; i < count; i++) { + DWORD device_type = device_list[i].dwType; + + if (device_type != RIM_TYPEHID) { + continue; + } + + printf("%d: ", i); + + // char device_name[1000] = {0}; + // UINT device_name_size = sizeof(device_name); + // if (GetRawInputDeviceInfo(device_list[i].hDevice, RIDI_DEVICENAME, device_name, &device_name_size) <= 0) { + // printf("GetRawInputDeviceInfo failed. Error code: %d\n", GetLastError()); + // return GetLastError(); + // } + // printf("%s\n", device_name); + + // switch (device_type) { + // case RIM_TYPEHID: + // printf("The device is an HID that is not a keyboard and not a mouse.\n"); + // break; + // case RIM_TYPEKEYBOARD: + // printf("The device is a keyboard.\n"); + // break; + // case RIM_TYPEMOUSE: + // printf("The device is a mouse.\n"); + // break; + // default: + // printf("The device is an unknown type.\n"); + // break; + // } + + // printf("-- Begin device info --\n"); + + RID_DEVICE_INFO device_info; + UINT device_info_size = sizeof(device_info); + device_info.cbSize = device_info_size; + GetRawInputDeviceInfo(device_list[i].hDevice, RIDI_DEVICEINFO, &device_info, &device_info_size); + + printf("%x %x\n", device_info.hid.dwVendorId, device_info.hid.dwProductId); + + // printf("VendorId: %x\n", device_info.hid.dwVendorId); + // printf("ProductId: %x\n", device_info.hid.dwProductId); + // printf("VersionNumber: %x\n", device_info.hid.dwVersionNumber); + + // printf("-- End device info --\n"); + } + + free(device_list); + + return 0; +} \ No newline at end of file diff --git a/src/tray.h b/stadia-vigem/include/tray.h similarity index 100% rename from src/tray.h rename to stadia-vigem/include/tray.h diff --git a/res/icon.ico b/stadia-vigem/res/icon.ico similarity index 100% rename from res/icon.ico rename to stadia-vigem/res/icon.ico diff --git a/res/res.rc b/stadia-vigem/res/res.rc similarity index 100% rename from res/res.rc rename to stadia-vigem/res/res.rc diff --git a/src/main.c b/stadia-vigem/src/main.c similarity index 99% rename from src/main.c rename to stadia-vigem/src/main.c index 4e07fec..7dbefbd 100644 --- a/src/main.c +++ b/stadia-vigem/src/main.c @@ -212,7 +212,7 @@ static BOOL remove_device(struct stadia_controller *controller) static void refresh_devices() { - LPTSTR stadia_hw_path_filters[3] = {STADIA_HW_FILTER, NULL}; + LPTSTR stadia_hw_path_filters[3] = {STADIA_USB_HW_FILTER, STADIA_BLT_HW_FILTER, NULL}; struct hid_device_info *device_info = hid_enumerate(stadia_hw_path_filters); struct hid_device_info *cur; BOOL found = FALSE; diff --git a/src/tray.c b/stadia-vigem/src/tray.c similarity index 100% rename from src/tray.c rename to stadia-vigem/src/tray.c