From fc14b8f0daa1457ab54da729d2dbf61355159528 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 5 Dec 2022 01:00:06 +1000 Subject: [PATCH] USB: Move to Qt (with *significant* refactoring) --- PCSX2_suite.sln | 15 - pcsx2-gsrunner/Main.cpp | 6 +- pcsx2-qt/CMakeLists.txt | 2 + pcsx2-qt/DisplayWidget.cpp | 35 +- pcsx2-qt/DisplayWidget.h | 7 +- pcsx2-qt/MainWindow.cpp | 48 +- pcsx2-qt/MainWindow.h | 3 + pcsx2-qt/QtHost.cpp | 5 + pcsx2-qt/QtHost.h | 1 + .../Settings/ControllerBindingWidgets.cpp | 609 ++++++- pcsx2-qt/Settings/ControllerBindingWidgets.h | 85 +- .../Settings/ControllerSettingsDialog.cpp | 56 +- pcsx2-qt/Settings/ControllerSettingsDialog.h | 8 +- pcsx2-qt/Settings/ControllerSettingsDialog.ui | 12 +- pcsx2-qt/Settings/HotkeySettingsWidget.cpp | 3 +- pcsx2-qt/Settings/InputBindingDialog.cpp | 17 +- pcsx2-qt/Settings/InputBindingDialog.h | 7 +- pcsx2-qt/Settings/InputBindingWidget.cpp | 28 +- pcsx2-qt/Settings/InputBindingWidget.h | 9 +- .../Settings/USBBindingWidget_DrivingForce.ui | 992 +++++++++++ pcsx2-qt/Settings/USBBindingWidget_GTForce.ui | 683 ++++++++ pcsx2-qt/Settings/USBDeviceWidget.ui | 142 ++ pcsx2-qt/pcsx2-qt.vcxproj | 7 +- pcsx2-qt/pcsx2-qt.vcxproj.filters | 15 +- .../resources/icons/black/svg/usb-fill.svg | 1 + .../resources/icons/white/svg/usb-fill.svg | 1 + pcsx2-qt/resources/images/DrivingForce.png | Bin 0 -> 62533 bytes pcsx2-qt/resources/images/GTForce.png | Bin 0 -> 54814 bytes pcsx2-qt/resources/resources.qrc | 4 + pcsx2/CMakeLists.txt | 190 +- pcsx2/Config.h | 37 +- pcsx2/Frontend/DInputSource.cpp | 6 +- pcsx2/Frontend/FullscreenUI.cpp | 2 +- pcsx2/Frontend/ImGuiOverlays.cpp | 60 + pcsx2/Frontend/InputManager.cpp | 284 ++- pcsx2/Frontend/InputManager.h | 24 +- pcsx2/Frontend/SDLInputSource.cpp | 4 +- pcsx2/Frontend/XInputSource.cpp | 5 +- pcsx2/GS/GS.h | 5 + pcsx2/GS/Renderers/Common/GSRenderer.cpp | 25 + pcsx2/Hw.cpp | 8 +- pcsx2/IopCounters.cpp | 4 + pcsx2/IopIrq.cpp | 4 + pcsx2/PAD/Host/PAD.cpp | 6 - pcsx2/PAD/Host/PAD.h | 3 - pcsx2/Pcsx2Config.cpp | 64 +- pcsx2/R5900.cpp | 4 - pcsx2/SaveState.cpp | 11 +- pcsx2/USB/USB.cpp | 1071 +++++++----- pcsx2/USB/USB.h | 90 +- pcsx2/USB/USBNull.cpp | 13 +- pcsx2/USB/{linux/util.h => USBNull.h} | 28 +- pcsx2/USB/Win32/Config_usb.cpp | 272 --- pcsx2/USB/Win32/Config_usb.h | 45 - pcsx2/USB/Win32/USBDialog.rc | 206 --- pcsx2/USB/Win32/guid.cpp | 19 - pcsx2/USB/Win32/resource_usb.h | 41 - pcsx2/USB/configuration.cpp | 278 --- pcsx2/USB/configuration.h | 162 -- pcsx2/USB/device_init.cpp | 62 - pcsx2/USB/deviceproxy.cpp | 88 +- pcsx2/USB/deviceproxy.h | 210 +-- pcsx2/USB/gtk.h | 19 - pcsx2/USB/helpers.h | 28 - pcsx2/USB/icon_buzz_24.cpp | 85 - pcsx2/USB/icon_buzz_24.h | 17 - pcsx2/USB/linux/actualfile.c | 152 -- pcsx2/USB/linux/actualfile.h | 42 - pcsx2/USB/linux/config-gtk.cpp | 345 ---- pcsx2/USB/linux/config.cpp | 31 - pcsx2/USB/linux/config.h | 21 - pcsx2/USB/linux/ini.c | 594 ------- pcsx2/USB/linux/ini.h | 61 - pcsx2/USB/linux/util.cpp | 31 - pcsx2/USB/platcompat.h | 132 -- pcsx2/USB/proxybase.h | 25 - pcsx2/USB/qemu-usb/USBinternal.h | 13 +- pcsx2/USB/qemu-usb/bus.cpp | 4 +- pcsx2/USB/qemu-usb/core.cpp | 14 +- pcsx2/USB/qemu-usb/desc.cpp | 6 +- pcsx2/USB/qemu-usb/desc.h | 9 +- pcsx2/USB/qemu-usb/glib.cpp | 160 -- pcsx2/USB/qemu-usb/glib.h | 47 - pcsx2/USB/qemu-usb/hid.cpp | 6 +- pcsx2/USB/qemu-usb/hid.h | 7 +- .../qemu-usb/input-keymap-linux-to-qcode.cpp | 544 ------ .../qemu-usb/input-keymap-linux-to-qcode.h | 18 - .../qemu-usb/input-keymap-qcode-to-qnum.cpp | 2 +- .../qemu-usb/input-keymap-win32-to-qcode.cpp | 276 --- .../qemu-usb/input-keymap-win32-to-qcode.h | 24 - pcsx2/USB/qemu-usb/input-keymap.h | 2 - pcsx2/USB/qemu-usb/iov.cpp | 319 +--- pcsx2/USB/qemu-usb/iov.h | 130 +- pcsx2/USB/qemu-usb/qusb.h | 8 +- pcsx2/USB/qemu-usb/usb-hub.cpp | 594 ------- pcsx2/USB/qemu-usb/usb-ohci.cpp | 197 ++- pcsx2/USB/qemu-usb/vl.cpp | 43 - pcsx2/USB/qemu-usb/vl.h | 79 - pcsx2/USB/shared/hidapi.cpp | 75 - pcsx2/USB/shared/hidapi.h | 476 ----- pcsx2/USB/shared/inifile_usb.cpp | 1058 ----------- pcsx2/USB/shared/inifile_usb.h | 568 ------ pcsx2/USB/shared/rawinput_usb.cpp | 231 --- pcsx2/USB/shared/rawinput_usb.h | 38 - pcsx2/USB/shared/ringbuffer.cpp | 2 - pcsx2/USB/shared/ringbuffer.h | 18 +- pcsx2/USB/shared/shared_usb.cpp | 42 - pcsx2/USB/shared/shared_usb.h | 21 - pcsx2/USB/usb-eyetoy/api_init_linux.cpp | 23 - .../USB/usb-eyetoy/api_init_win32_eyetoy.cpp | 24 - pcsx2/USB/usb-eyetoy/cam-linux.cpp | 95 +- pcsx2/USB/usb-eyetoy/cam-linux.h | 18 +- pcsx2/USB/usb-eyetoy/cam-noop.cpp | 67 + pcsx2/USB/usb-eyetoy/cam-windows.cpp | 126 +- pcsx2/USB/usb-eyetoy/cam-windows.h | 25 +- pcsx2/USB/usb-eyetoy/jo_mpeg.cpp | 1 - pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp | 272 ++- pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.h | 45 +- pcsx2/USB/usb-eyetoy/videodev.h | 23 +- pcsx2/USB/usb-eyetoy/videodeviceproxy.h | 90 - pcsx2/USB/usb-hid/api_init_linux.cpp | 25 - pcsx2/USB/usb-hid/api_init_win32_hid.cpp | 26 - pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp | 190 -- pcsx2/USB/usb-hid/evdev/evdev.cpp | 305 ---- pcsx2/USB/usb-hid/evdev/evdev.h | 89 - pcsx2/USB/usb-hid/hidproxy.h | 93 - pcsx2/USB/usb-hid/noop.h | 52 - pcsx2/USB/usb-hid/raw/rawinput.cpp | 300 ---- pcsx2/USB/usb-hid/raw/rawinput.h | 54 - pcsx2/USB/usb-hid/usb-buzzer.cpp | 936 ---------- pcsx2/USB/usb-hid/usb-hid.cpp | 924 +++++----- pcsx2/USB/usb-hid/usb-hid.h | 112 +- pcsx2/USB/usb-lightgun/guncon2.cpp | 557 ++++++ pcsx2/USB/usb-lightgun/guncon2.h | 34 + pcsx2/USB/usb-mic/api_init_linux.cpp | 29 - pcsx2/USB/usb-mic/api_init_win32_mic.cpp | 26 - pcsx2/USB/usb-mic/audiodev-cubeb.cpp | 286 +++ pcsx2/USB/usb-mic/audiodev-cubeb.h | 64 + pcsx2/USB/usb-mic/audiodev-noop.h | 68 +- pcsx2/USB/usb-mic/audiodev-pulse.cpp | 895 ---------- pcsx2/USB/usb-mic/audiodev-pulse.h | 168 -- pcsx2/USB/usb-mic/audiodev-wasapi.cpp | 1019 ----------- pcsx2/USB/usb-mic/audiodev-wasapi.h | 161 -- pcsx2/USB/usb-mic/audiodev.h | 74 +- pcsx2/USB/usb-mic/audiodeviceproxy.h | 109 -- pcsx2/USB/usb-mic/usb-headset.cpp | 697 ++++---- pcsx2/USB/usb-mic/usb-headset.h | 30 +- pcsx2/USB/usb-mic/usb-mic-logitech.cpp | 24 +- pcsx2/USB/usb-mic/usb-mic-singstar.cpp | 576 +++--- pcsx2/USB/usb-mic/usb-mic-singstar.h | 62 +- pcsx2/USB/usb-msd/usb-msd-gtk.cpp | 125 -- pcsx2/USB/usb-msd/usb-msd-win32.cpp | 106 -- pcsx2/USB/usb-msd/usb-msd.cpp | 265 ++- pcsx2/USB/usb-msd/usb-msd.h | 38 +- pcsx2/USB/usb-pad/api_init_linux.cpp | 23 - pcsx2/USB/usb-pad/api_init_win32_pad.cpp | 26 - pcsx2/USB/usb-pad/bitjuggling.cpp | 152 -- pcsx2/USB/usb-pad/dx/dinput-config.cpp | 1479 ---------------- pcsx2/USB/usb-pad/dx/dinput.cpp | 1100 ------------ pcsx2/USB/usb-pad/dx/dx.h | 231 --- pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp | 211 --- pcsx2/USB/usb-pad/dx/usb-pad-dx.h | 53 - pcsx2/USB/usb-pad/dx/versionproxy.h | 204 --- pcsx2/USB/usb-pad/dx/versionproxy.rc | 464 ----- pcsx2/USB/usb-pad/evdev/evdev-ff.cpp | 296 ---- pcsx2/USB/usb-pad/evdev/evdev-ff.h | 56 - pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp | 754 -------- pcsx2/USB/usb-pad/evdev/evdev.cpp | 677 ------- pcsx2/USB/usb-pad/evdev/evdev.h | 163 -- pcsx2/USB/usb-pad/evdev/shared-gtk.cpp | 1117 ------------ pcsx2/USB/usb-pad/evdev/shared.h | 214 --- pcsx2/USB/usb-pad/padproxy.h | 88 - pcsx2/USB/usb-pad/raw/raw-config-res.h | 51 - pcsx2/USB/usb-pad/raw/raw-config.cpp | 925 ---------- pcsx2/USB/usb-pad/raw/raw-config.rc | 146 -- pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp | 519 ------ pcsx2/USB/usb-pad/raw/usb-pad-raw.h | 144 -- pcsx2/USB/usb-pad/usb-pad-ff.cpp | 45 +- pcsx2/USB/usb-pad/usb-pad-sdl-ff.cpp | 337 ++++ pcsx2/USB/usb-pad/usb-pad-sdl-ff.h | 68 + pcsx2/USB/usb-pad/usb-pad.cpp | 1548 ++++++++--------- pcsx2/USB/usb-pad/usb-pad.h | 903 ++-------- pcsx2/USB/usb-pad/usb-seamic.cpp | 165 +- pcsx2/USB/usb-printer/usb-printer.cpp | 237 +-- pcsx2/USB/usb-printer/usb-printer.h | 47 +- pcsx2/VMManager.cpp | 12 +- pcsx2/gui/AppCoreThread.cpp | 2 +- pcsx2/gui/MainMenuClicks.cpp | 2 +- pcsx2/gui/SysCoreThread.cpp | 2 +- pcsx2/gui/SysState.cpp | 2 +- pcsx2/pcsx2.vcxproj | 105 +- pcsx2/pcsx2.vcxproj.filters | 348 +--- pcsx2/pcsx2core.vcxproj | 56 +- pcsx2/pcsx2core.vcxproj.filters | 217 ++- pcsx2/ps2/Iop/IopHwRead.cpp | 4 + pcsx2/ps2/Iop/IopHwWrite.cpp | 4 + 196 files changed, 8511 insertions(+), 26760 deletions(-) create mode 100644 pcsx2-qt/Settings/USBBindingWidget_DrivingForce.ui create mode 100644 pcsx2-qt/Settings/USBBindingWidget_GTForce.ui create mode 100644 pcsx2-qt/Settings/USBDeviceWidget.ui create mode 100644 pcsx2-qt/resources/icons/black/svg/usb-fill.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/usb-fill.svg create mode 100644 pcsx2-qt/resources/images/DrivingForce.png create mode 100644 pcsx2-qt/resources/images/GTForce.png rename pcsx2/USB/{linux/util.h => USBNull.h} (60%) delete mode 100644 pcsx2/USB/Win32/Config_usb.cpp delete mode 100644 pcsx2/USB/Win32/Config_usb.h delete mode 100644 pcsx2/USB/Win32/USBDialog.rc delete mode 100644 pcsx2/USB/Win32/guid.cpp delete mode 100644 pcsx2/USB/Win32/resource_usb.h delete mode 100644 pcsx2/USB/configuration.cpp delete mode 100644 pcsx2/USB/configuration.h delete mode 100644 pcsx2/USB/device_init.cpp delete mode 100644 pcsx2/USB/gtk.h delete mode 100644 pcsx2/USB/helpers.h delete mode 100644 pcsx2/USB/icon_buzz_24.cpp delete mode 100644 pcsx2/USB/icon_buzz_24.h delete mode 100644 pcsx2/USB/linux/actualfile.c delete mode 100644 pcsx2/USB/linux/actualfile.h delete mode 100644 pcsx2/USB/linux/config-gtk.cpp delete mode 100644 pcsx2/USB/linux/config.cpp delete mode 100644 pcsx2/USB/linux/config.h delete mode 100644 pcsx2/USB/linux/ini.c delete mode 100644 pcsx2/USB/linux/ini.h delete mode 100644 pcsx2/USB/linux/util.cpp delete mode 100644 pcsx2/USB/platcompat.h delete mode 100644 pcsx2/USB/proxybase.h delete mode 100644 pcsx2/USB/qemu-usb/glib.cpp delete mode 100644 pcsx2/USB/qemu-usb/glib.h delete mode 100644 pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.cpp delete mode 100644 pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.h delete mode 100644 pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.cpp delete mode 100644 pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.h delete mode 100644 pcsx2/USB/qemu-usb/usb-hub.cpp delete mode 100644 pcsx2/USB/qemu-usb/vl.cpp delete mode 100644 pcsx2/USB/qemu-usb/vl.h delete mode 100644 pcsx2/USB/shared/hidapi.cpp delete mode 100644 pcsx2/USB/shared/hidapi.h delete mode 100644 pcsx2/USB/shared/inifile_usb.cpp delete mode 100644 pcsx2/USB/shared/inifile_usb.h delete mode 100644 pcsx2/USB/shared/rawinput_usb.cpp delete mode 100644 pcsx2/USB/shared/rawinput_usb.h delete mode 100644 pcsx2/USB/shared/shared_usb.cpp delete mode 100644 pcsx2/USB/shared/shared_usb.h delete mode 100644 pcsx2/USB/usb-eyetoy/api_init_linux.cpp delete mode 100644 pcsx2/USB/usb-eyetoy/api_init_win32_eyetoy.cpp create mode 100644 pcsx2/USB/usb-eyetoy/cam-noop.cpp delete mode 100644 pcsx2/USB/usb-eyetoy/videodeviceproxy.h delete mode 100644 pcsx2/USB/usb-hid/api_init_linux.cpp delete mode 100644 pcsx2/USB/usb-hid/api_init_win32_hid.cpp delete mode 100644 pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp delete mode 100644 pcsx2/USB/usb-hid/evdev/evdev.cpp delete mode 100644 pcsx2/USB/usb-hid/evdev/evdev.h delete mode 100644 pcsx2/USB/usb-hid/hidproxy.h delete mode 100644 pcsx2/USB/usb-hid/noop.h delete mode 100644 pcsx2/USB/usb-hid/raw/rawinput.cpp delete mode 100644 pcsx2/USB/usb-hid/raw/rawinput.h delete mode 100644 pcsx2/USB/usb-hid/usb-buzzer.cpp create mode 100644 pcsx2/USB/usb-lightgun/guncon2.cpp create mode 100644 pcsx2/USB/usb-lightgun/guncon2.h delete mode 100644 pcsx2/USB/usb-mic/api_init_linux.cpp delete mode 100644 pcsx2/USB/usb-mic/api_init_win32_mic.cpp create mode 100644 pcsx2/USB/usb-mic/audiodev-cubeb.cpp create mode 100644 pcsx2/USB/usb-mic/audiodev-cubeb.h delete mode 100644 pcsx2/USB/usb-mic/audiodev-pulse.cpp delete mode 100644 pcsx2/USB/usb-mic/audiodev-pulse.h delete mode 100644 pcsx2/USB/usb-mic/audiodev-wasapi.cpp delete mode 100644 pcsx2/USB/usb-mic/audiodev-wasapi.h delete mode 100644 pcsx2/USB/usb-mic/audiodeviceproxy.h delete mode 100644 pcsx2/USB/usb-msd/usb-msd-gtk.cpp delete mode 100644 pcsx2/USB/usb-msd/usb-msd-win32.cpp delete mode 100644 pcsx2/USB/usb-pad/api_init_linux.cpp delete mode 100644 pcsx2/USB/usb-pad/api_init_win32_pad.cpp delete mode 100644 pcsx2/USB/usb-pad/bitjuggling.cpp delete mode 100644 pcsx2/USB/usb-pad/dx/dinput-config.cpp delete mode 100644 pcsx2/USB/usb-pad/dx/dinput.cpp delete mode 100644 pcsx2/USB/usb-pad/dx/dx.h delete mode 100644 pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp delete mode 100644 pcsx2/USB/usb-pad/dx/usb-pad-dx.h delete mode 100644 pcsx2/USB/usb-pad/dx/versionproxy.h delete mode 100644 pcsx2/USB/usb-pad/dx/versionproxy.rc delete mode 100644 pcsx2/USB/usb-pad/evdev/evdev-ff.cpp delete mode 100644 pcsx2/USB/usb-pad/evdev/evdev-ff.h delete mode 100644 pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp delete mode 100644 pcsx2/USB/usb-pad/evdev/evdev.cpp delete mode 100644 pcsx2/USB/usb-pad/evdev/evdev.h delete mode 100644 pcsx2/USB/usb-pad/evdev/shared-gtk.cpp delete mode 100644 pcsx2/USB/usb-pad/evdev/shared.h delete mode 100644 pcsx2/USB/usb-pad/padproxy.h delete mode 100644 pcsx2/USB/usb-pad/raw/raw-config-res.h delete mode 100644 pcsx2/USB/usb-pad/raw/raw-config.cpp delete mode 100644 pcsx2/USB/usb-pad/raw/raw-config.rc delete mode 100644 pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp delete mode 100644 pcsx2/USB/usb-pad/raw/usb-pad-raw.h create mode 100644 pcsx2/USB/usb-pad/usb-pad-sdl-ff.cpp create mode 100644 pcsx2/USB/usb-pad/usb-pad-sdl-ff.h diff --git a/PCSX2_suite.sln b/PCSX2_suite.sln index b582d7d5a0..ad779c85e8 100644 --- a/PCSX2_suite.sln +++ b/PCSX2_suite.sln @@ -42,8 +42,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "3rdparty\xz\libl EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "3rdparty\fmt\fmt.vcxproj", "{449AD25E-424A-4714-BABC-68706CDCC33B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsamplerate", "3rdparty\libsamplerate\libsamplerate.vcxproj", "{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libchdr", "3rdparty\libchdr\libchdr.vcxproj", "{A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpgd", "3rdparty\jpgd\jpgd.vcxproj", "{ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}" @@ -248,18 +246,6 @@ Global {449AD25E-424A-4714-BABC-68706CDCC33B}.Release AVX2|x64.Build.0 = Release|x64 {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|x64.ActiveCfg = Release|x64 {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|x64.Build.0 = Release|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug AVX2|x64.Build.0 = Debug|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|x64.ActiveCfg = Debug|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|x64.Build.0 = Debug|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel AVX2|x64.Build.0 = Devel|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel|x64.ActiveCfg = Devel|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel|x64.Build.0 = Devel|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release AVX2|x64.ActiveCfg = Release|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release AVX2|x64.Build.0 = Release|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|x64.ActiveCfg = Release|x64 - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|x64.Build.0 = Release|x64 {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug AVX2|x64.ActiveCfg = Debug|x64 {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug AVX2|x64.Build.0 = Debug|x64 {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug|x64.ActiveCfg = Debug|x64 @@ -434,7 +420,6 @@ Global {27F17499-A372-4408-8AFA-4F9F4584FBD3} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {12728250-16EC-4DC6-94D7-E21DD88947F8} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {449AD25E-424A-4714-BABC-68706CDCC33B} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} diff --git a/pcsx2-gsrunner/Main.cpp b/pcsx2-gsrunner/Main.cpp index 90c5e8d524..5bfc57be4e 100644 --- a/pcsx2-gsrunner/Main.cpp +++ b/pcsx2-gsrunner/Main.cpp @@ -258,6 +258,10 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier) { } +void Host::SetRelativeMouseMode(bool enabled) +{ +} + bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail) { const std::optional wi(GSRunner::GetPlatformWindowInfo()); @@ -772,4 +776,4 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return DefWindowProcW(hwnd, msg, wParam, lParam); } -#endif // _WIN32 \ No newline at end of file +#endif // _WIN32 diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt index 9e3c5eae20..6bbe206307 100644 --- a/pcsx2-qt/CMakeLists.txt +++ b/pcsx2-qt/CMakeLists.txt @@ -117,6 +117,8 @@ target_sources(pcsx2-qt PRIVATE Settings/SettingsDialog.cpp Settings/SettingsDialog.h Settings/SettingsDialog.ui + Settings/USBBindingWidget_DrivingForce.ui + Settings/USBBindingWidget_GTForce.ui Tools/InputRecording/NewInputRecordingDlg.cpp Tools/InputRecording/NewInputRecordingDlg.h Tools/InputRecording/NewInputRecordingDlg.ui diff --git a/pcsx2-qt/DisplayWidget.cpp b/pcsx2-qt/DisplayWidget.cpp index 245ea728d4..8ca26267a3 100644 --- a/pcsx2-qt/DisplayWidget.cpp +++ b/pcsx2-qt/DisplayWidget.cpp @@ -84,17 +84,15 @@ std::optional DisplayWidget::getWindowInfo() return ret; } -void DisplayWidget::updateRelativeMode(bool master_enable) +void DisplayWidget::updateRelativeMode(bool enabled) { - bool relative_mode = master_enable && InputManager::HasPointerAxisBinds(); - #ifdef _WIN32 // prefer ClipCursor() over warping movement when we're using raw input - bool clip_cursor = relative_mode && false /*InputManager::IsUsingRawInput()*/; - if (m_relative_mouse_enabled == relative_mode && m_clip_mouse_enabled == clip_cursor) + bool clip_cursor = enabled && false /*InputManager::IsUsingRawInput()*/; + if (m_relative_mouse_enabled == enabled && m_clip_mouse_enabled == clip_cursor) return; - DevCon.WriteLn("updateRelativeMode(): relative=%s, clip=%s", relative_mode ? "yes" : "no", clip_cursor ? "yes" : "no"); + DevCon.WriteLn("updateRelativeMode(): relative=%s, clip=%s", enabled ? "yes" : "no", clip_cursor ? "yes" : "no"); if (!clip_cursor && m_clip_mouse_enabled) { @@ -102,13 +100,13 @@ void DisplayWidget::updateRelativeMode(bool master_enable) ClipCursor(nullptr); } #else - if (m_relative_mouse_enabled == relative_mode) + if (m_relative_mouse_enabled == enabled) return; - DevCon.WriteLn("updateRelativeMode(): relative=%s", relative_mode ? "yes" : "no"); + DevCon.WriteLn("updateRelativeMode(): relative=%s", enabled ? "yes" : "no"); #endif - if (relative_mode) + if (enabled) { #ifdef _WIN32 m_relative_mouse_enabled = !clip_cursor; @@ -129,21 +127,22 @@ void DisplayWidget::updateRelativeMode(bool master_enable) } -void DisplayWidget::updateCursor(bool master_enable) +void DisplayWidget::updateCursor(bool hidden) { -#ifdef _WIN32 - const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled || m_clip_mouse_enabled); -#else - const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled); -#endif - if (m_cursor_hidden == hide) + if (m_cursor_hidden == hidden) return; - m_cursor_hidden = hide; - if (hide) + m_cursor_hidden = hidden; + if (hidden) + { + DevCon.WriteLn("updateCursor(): Cursor is now hidden"); setCursor(Qt::BlankCursor); + } else + { + DevCon.WriteLn("updateCursor(): Cursor is now shown"); unsetCursor(); + } } void DisplayWidget::handleCloseEvent(QCloseEvent* event) diff --git a/pcsx2-qt/DisplayWidget.h b/pcsx2-qt/DisplayWidget.h index 76b14bcce7..2d943a7c84 100644 --- a/pcsx2-qt/DisplayWidget.h +++ b/pcsx2-qt/DisplayWidget.h @@ -32,15 +32,13 @@ public: QPaintEngine* paintEngine() const override; - __fi void setShouldHideCursor(bool hide) { m_should_hide_cursor = hide; } - int scaledWindowWidth() const; int scaledWindowHeight() const; std::optional getWindowInfo(); - void updateRelativeMode(bool master_enable); - void updateCursor(bool master_enable); + void updateRelativeMode(bool enabled); + void updateCursor(bool hidden); void handleCloseEvent(QCloseEvent* event); @@ -60,7 +58,6 @@ private: #ifdef _WIN32 bool m_clip_mouse_enabled = false; #endif - bool m_should_hide_cursor = false; bool m_cursor_hidden = false; std::vector m_keys_pressed_with_modifiers; diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 5dadf59df3..662f4eeb19 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -409,6 +409,7 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread) connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplay, Qt::BlockingQueuedConnection); connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection); connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested); + connect(thread, &EmuThread::onRelativeMouseModeRequested, this, &MainWindow::relativeMouseModeRequested); connect(thread, &EmuThread::onVMStarting, this, &MainWindow::onVMStarting); connect(thread, &EmuThread::onVMStarted, this, &MainWindow::onVMStarted); connect(thread, &EmuThread::onVMPaused, this, &MainWindow::onVMPaused); @@ -1089,7 +1090,7 @@ bool MainWindow::isRenderingToMain() const bool MainWindow::shouldHideMouseCursor() const { - return isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false); + return (isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false)) || m_relative_mouse_mode; } bool MainWindow::shouldHideMainWindow() const @@ -1250,7 +1251,7 @@ void MainWindow::requestExit() void MainWindow::checkForSettingChanges() { if (m_display_widget) - m_display_widget->updateRelativeMode(s_vm_valid && !s_vm_paused); + updateDisplayWidgetCursor(); updateWindowState(); } @@ -1802,10 +1803,7 @@ void MainWindow::onVMPaused() m_last_fps_status = m_status_verbose_widget->text(); m_status_verbose_widget->setText(tr("Paused")); if (m_display_widget) - { - m_display_widget->updateRelativeMode(false); - m_display_widget->updateCursor(false); - } + updateDisplayWidgetCursor(); } void MainWindow::onVMResumed() @@ -1824,8 +1822,7 @@ void MainWindow::onVMResumed() m_last_fps_status = QString(); if (m_display_widget) { - m_display_widget->updateRelativeMode(true); - m_display_widget->updateCursor(true); + updateDisplayWidgetCursor(); m_display_widget->setFocus(); } } @@ -1842,14 +1839,9 @@ void MainWindow::onVMStopped() updateInputRecordingActions(false); if (m_display_widget) - { - m_display_widget->updateRelativeMode(false); - m_display_widget->updateCursor(false); - } + updateDisplayWidgetCursor(); else - { switchToGameListView(); - } // reload played time if (m_game_list_widget->isShowingGameList()) @@ -2035,9 +2027,7 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main) m_ui.actionStartFullscreenUI->setEnabled(false); m_ui.actionStartFullscreenUI2->setEnabled(false); - m_display_widget->setShouldHideCursor(shouldHideMouseCursor()); - m_display_widget->updateRelativeMode(s_vm_valid && !s_vm_paused); - m_display_widget->updateCursor(s_vm_valid && !s_vm_paused); + updateDisplayWidgetCursor(); m_display_widget->setFocus(); g_host_display->DoneCurrent(); @@ -2083,10 +2073,8 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b container->showNormal(); } + updateDisplayWidgetCursor(); m_display_widget->setFocus(); - m_display_widget->setShouldHideCursor(shouldHideMouseCursor()); - m_display_widget->updateRelativeMode(s_vm_valid && !s_vm_paused); - m_display_widget->updateCursor(s_vm_valid && !s_vm_paused); updateWindowState(); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); @@ -2122,10 +2110,8 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b updateWindowTitle(); updateWindowState(); + updateDisplayWidgetCursor(); m_display_widget->setFocus(); - m_display_widget->setShouldHideCursor(shouldHideMouseCursor()); - m_display_widget->updateRelativeMode(s_vm_valid && !s_vm_paused); - m_display_widget->updateCursor(s_vm_valid && !s_vm_paused); return m_display_widget; } @@ -2221,6 +2207,16 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height) QtUtils::ResizePotentiallyFixedSizeWindow(this, width, height + extra_height); } +void MainWindow::relativeMouseModeRequested(bool enabled) +{ + if (m_relative_mouse_mode == enabled) + return; + + m_relative_mouse_mode = enabled; + if (s_vm_valid && !s_vm_paused) + updateDisplayWidgetCursor(); +} + void MainWindow::destroyDisplay() { // Now we can safely destroy the display window. @@ -2284,6 +2280,12 @@ void MainWindow::destroyDisplayWidget(bool show_game_list) updateDisplayRelatedActions(false, false, false); } +void MainWindow::updateDisplayWidgetCursor() +{ + m_display_widget->updateRelativeMode(s_vm_valid && !s_vm_paused && m_relative_mouse_mode); + m_display_widget->updateCursor(s_vm_valid && !s_vm_paused && shouldHideMouseCursor()); +} + void MainWindow::focusDisplayWidget() { if (!m_display_widget || centralWidget() != m_display_widget) diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 23321810db..8d75adccc2 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -129,6 +129,7 @@ private Q_SLOTS: DisplayWidget* createDisplay(bool fullscreen, bool render_to_main); DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless); void displayResizeRequested(qint32 width, qint32 height); + void relativeMouseModeRequested(bool enabled); void destroyDisplay(); void focusDisplayWidget(); @@ -231,6 +232,7 @@ private: void restoreDisplayWindowGeometryFromConfig(); void createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen); void destroyDisplayWidget(bool show_game_list); + void updateDisplayWidgetCursor(); void setDisplayFullscreen(const std::string& fullscreen_mode); SettingsDialog* getSettingsDialog(); @@ -283,6 +285,7 @@ private: quint32 m_current_game_crc; bool m_display_created = false; + bool m_relative_mouse_mode = false; bool m_save_states_invalidated = false; bool m_was_paused_on_surface_loss = false; bool m_was_disc_change_request = false; diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index de5da54118..10b3e84bc7 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -1486,6 +1486,11 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier) emit g_emu_thread->onInputDeviceDisconnected(identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size())); } +void Host::SetRelativeMouseMode(bool enabled) +{ + emit g_emu_thread->onRelativeMouseModeRequested(enabled); +} + ////////////////////////////////////////////////////////////////////////// // Hotkeys ////////////////////////////////////////////////////////////////////////// diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index d0bee4fee8..65b2885af6 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -120,6 +120,7 @@ Q_SIGNALS: DisplayWidget* onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless); void onResizeDisplayRequested(qint32 width, qint32 height); void onDestroyDisplayRequested(); + void onRelativeMouseModeRequested(bool enabled); /// Called when the VM is starting initialization, but has not been completed yet. void onVMStarting(); diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp index 92262f36b4..a711c65be2 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -15,10 +15,18 @@ #include "PrecompiledHeader.h" +#include #include #include #include +#include #include +#include "fmt/format.h" + +#include "common/StringUtil.h" + +#include "pcsx2/HostSettings.h" +#include "pcsx2/PAD/Host/PAD.h" #include "Settings/ControllerBindingWidgets.h" #include "Settings/ControllerSettingsDialog.h" @@ -28,9 +36,8 @@ #include "QtUtils.h" #include "SettingWidgetBinder.h" -#include "common/StringUtil.h" -#include "pcsx2/HostSettings.h" -#include "pcsx2/PAD/Host/PAD.h" +#include "ui_USBBindingWidget_DrivingForce.h" +#include "ui_USBBindingWidget_GTForce.h" ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port) : QWidget(parent) @@ -42,8 +49,8 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett populateControllerTypes(); onTypeChanged(); - ControllerSettingWidgetBinder::BindWidgetToInputProfileString(m_dialog->getProfileSettingsInterface(), - m_ui.controllerType, m_config_section, "Type", PAD::GetDefaultPadType(port)); + ControllerSettingWidgetBinder::BindWidgetToInputProfileString( + m_dialog->getProfileSettingsInterface(), m_ui.controllerType, m_config_section, "Type", PAD::GetDefaultPadType(port)); connect(m_ui.controllerType, QOverload::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged); connect(m_ui.bindings, &QPushButton::clicked, this, &ControllerBindingWidget::onBindingsClicked); @@ -106,7 +113,10 @@ void ControllerBindingWidget::onTypeChanged() if (has_settings) { - m_settings_widget = new ControllerCustomSettingsWidget(this, m_ui.stackedWidget); + const QString settings_title(tr("%1 Settings").arg(qApp->translate("PAD", cinfo->display_name))); + const gsl::span settings(cinfo->settings, cinfo->num_settings); + m_settings_widget = new ControllerCustomSettingsWidget( + settings, m_config_section, std::string(), settings_title, cinfo->name, getDialog(), m_ui.stackedWidget); m_ui.stackedWidget->addWidget(m_settings_widget); } @@ -172,9 +182,7 @@ void ControllerBindingWidget::onAutomaticBindingClicked() // we set it as data, because the device list could get invalidated while the menu is up QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.first).arg(dev.second)); action->setData(dev.first); - connect(action, &QAction::triggered, this, [this, action]() { - doDeviceAutomaticBinding(action->data().toString()); - }); + connect(action, &QAction::triggered, this, [this, action]() { doDeviceAutomaticBinding(action->data().toString()); }); added = true; } @@ -266,8 +274,7 @@ ControllerMacroWidget::~ControllerMacroWidget() = default; void ControllerMacroWidget::updateListItem(u32 index) { - m_ui.portList->item(static_cast(index)) - ->setText(tr("Macro %1\n%2").arg(index + 1).arg(m_macros[index]->getSummary())); + m_ui.portList->item(static_cast(index))->setText(tr("Macro %1\n%2").arg(index + 1).arg(m_macros[index]->getSummary())); } void ControllerMacroWidget::createWidgets(ControllerBindingWidget* parent) @@ -291,8 +298,7 @@ void ControllerMacroWidget::createWidgets(ControllerBindingWidget* parent) ////////////////////////////////////////////////////////////////////////// -ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* parent, ControllerBindingWidget* bwidget, - u32 index) +ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* parent, ControllerBindingWidget* bwidget, u32 index) : QWidget(parent) , m_parent(parent) , m_bwidget(bwidget) @@ -310,8 +316,7 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare } // load binds (single string joined by &) - const std::string binds_string( - dialog->getStringValue(section.c_str(), fmt::format("Macro{}Binds", index + 1u).c_str(), "")); + const std::string binds_string(dialog->getStringValue(section.c_str(), fmt::format("Macro{}Binds", index + 1u).c_str(), "")); const std::vector buttons_split(StringUtil::SplitString(binds_string, '&', true)); for (const std::string_view& button : buttons_split) @@ -335,15 +340,15 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare QListWidgetItem* item = new QListWidgetItem(); item->setText(QString::fromUtf8(bi.display_name)); - item->setCheckState((std::find(m_binds.begin(), m_binds.end(), &bi) != m_binds.end()) ? Qt::Checked : - Qt::Unchecked); + item->setCheckState((std::find(m_binds.begin(), m_binds.end(), &bi) != m_binds.end()) ? Qt::Checked : Qt::Unchecked); m_ui.bindList->addItem(item); } m_frequency = dialog->getIntValue(section.c_str(), fmt::format("Macro{}Frequency", index + 1u).c_str(), 0); updateFrequencyText(); - m_ui.trigger->initialize(dialog->getProfileSettingsInterface(), section, fmt::format("Macro{}", index + 1u)); + m_ui.trigger->initialize( + dialog->getProfileSettingsInterface(), InputBindingInfo::Type::Macro, section, fmt::format("Macro{}", index + 1u)); connect(m_ui.increaseFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(1); }); connect(m_ui.decreateFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(-1); }); @@ -368,8 +373,8 @@ QString ControllerMacroEditWidget::getSummary() const void ControllerMacroEditWidget::onSetFrequencyClicked() { bool okay; - int new_freq = QInputDialog::getInt(this, tr("Set Frequency"), tr("Frequency: "), static_cast(m_frequency), 0, - std::numeric_limits::max(), 1, &okay); + int new_freq = QInputDialog::getInt( + this, tr("Set Frequency"), tr("Frequency: "), static_cast(m_frequency), 0, std::numeric_limits::max(), 1, &okay); if (!okay) return; @@ -388,9 +393,8 @@ void ControllerMacroEditWidget::modFrequency(s32 delta) void ControllerMacroEditWidget::updateFrequency() { - m_bwidget->getDialog()->setIntValue(m_bwidget->getConfigSection().c_str(), - fmt::format("Macro{}Frequency", m_index + 1u).c_str(), - static_cast(m_frequency)); + m_bwidget->getDialog()->setIntValue( + m_bwidget->getConfigSection().c_str(), fmt::format("Macro{}Frequency", m_index + 1u).c_str(), static_cast(m_frequency)); updateFrequencyText(); } @@ -453,17 +457,21 @@ void ControllerMacroEditWidget::updateBinds() ////////////////////////////////////////////////////////////////////////// -ControllerCustomSettingsWidget::ControllerCustomSettingsWidget(ControllerBindingWidget* parent, QWidget* parent_widget) +ControllerCustomSettingsWidget::ControllerCustomSettingsWidget(gsl::span settings, std::string config_section, + std::string config_prefix, const QString& group_title, const char* translation_ctx, ControllerSettingsDialog* dialog, + QWidget* parent_widget) : QWidget(parent_widget) - , m_parent(parent) + , m_settings(settings) + , m_config_section(std::move(config_section)) + , m_config_prefix(std::move(config_prefix)) + , m_dialog(dialog) { - const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(parent->getControllerType()); - if (!cinfo || cinfo->num_settings == 0) + if (settings.empty()) return; - QGroupBox* gbox = new QGroupBox(tr("%1 Settings").arg(qApp->translate("PAD", cinfo->display_name)), this); + QGroupBox* gbox = new QGroupBox(group_title, this); QGridLayout* gbox_layout = new QGridLayout(gbox); - createSettingWidgets(parent, gbox, gbox_layout, cinfo); + createSettingWidgets(translation_ctx, gbox, gbox_layout); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -481,26 +489,65 @@ ControllerCustomSettingsWidget::ControllerCustomSettingsWidget(ControllerBinding ControllerCustomSettingsWidget::~ControllerCustomSettingsWidget() = default; -void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidget* parent, QWidget* widget_parent, - QGridLayout* layout, const PAD::ControllerInfo* cinfo) +static std::tuple getPrefixAndSuffixForIntFormat(const QString& format) { - const std::string& section = parent->getConfigSection(); - SettingsInterface* sif = parent->getDialog()->getProfileSettingsInterface(); + QString prefix, suffix; + QRegularExpression re(QStringLiteral("(.*)%.*d(.*)")); + QRegularExpressionMatch match(re.match(format)); + if (match.isValid()) + { + prefix = match.captured(1).replace(QStringLiteral("%%"), QStringLiteral("%")); + suffix = match.captured(2).replace(QStringLiteral("%%"), QStringLiteral("%")); + } + + return std::tie(prefix, suffix); +} + +static std::tuple getPrefixAndSuffixForFloatFormat(const QString& format) +{ + QString prefix, suffix; + int decimals = -1; + + QRegularExpression re(QStringLiteral("(.*)%.*([0-9]+)f(.*)")); + QRegularExpressionMatch match(re.match(format)); + if (match.isValid()) + { + prefix = match.captured(1).replace(QStringLiteral("%%"), QStringLiteral("%")); + suffix = match.captured(3).replace(QStringLiteral("%%"), QStringLiteral("%")); + + bool decimals_ok; + decimals = match.captured(2).toInt(&decimals_ok); + if (!decimals_ok) + decimals = -1; + } + else + { + re = QRegularExpression(QStringLiteral("(.*)%.*f(.*)")); + match = re.match(format); + prefix = match.captured(1).replace(QStringLiteral("%%"), QStringLiteral("%")); + suffix = match.captured(2).replace(QStringLiteral("%%"), QStringLiteral("%")); + } + + return std::tie(prefix, suffix, decimals); +} + +void ControllerCustomSettingsWidget::createSettingWidgets(const char* translation_ctx, QWidget* widget_parent, QGridLayout* layout) +{ + SettingsInterface* sif = m_dialog->getProfileSettingsInterface(); int current_row = 0; - for (u32 i = 0; i < cinfo->num_settings; i++) + for (const SettingInfo& si : m_settings) { - const SettingInfo& si = cinfo->settings[i]; - std::string key_name = si.name; + std::string key_name = m_config_prefix + si.name; switch (si.type) { case SettingInfo::Type::Boolean: { - QCheckBox* cb = new QCheckBox(qApp->translate(cinfo->name, si.display_name), widget_parent); + QCheckBox* cb = new QCheckBox(qApp->translate(translation_ctx, si.display_name), widget_parent); cb->setObjectName(QString::fromUtf8(si.name)); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, cb, section, std::move(key_name), - si.BooleanDefaultValue()); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool( + sif, cb, m_config_section, std::move(key_name), si.BooleanDefaultValue()); layout->addWidget(cb, current_row, 0, 1, 4); current_row++; } @@ -513,8 +560,14 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge sb->setMinimum(si.IntegerMinValue()); sb->setMaximum(si.IntegerMaxValue()); sb->setSingleStep(si.IntegerStepValue()); - SettingWidgetBinder::BindWidgetToIntSetting(sif, sb, section, std::move(key_name), si.IntegerDefaultValue()); - layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); + if (si.format) + { + const auto [prefix, suffix] = getPrefixAndSuffixForIntFormat(QString::fromUtf8(si.format)); + sb->setPrefix(prefix); + sb->setSuffix(suffix); + } + SettingWidgetBinder::BindWidgetToIntSetting(sif, sb, m_config_section, std::move(key_name), si.IntegerDefaultValue()); + layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0); layout->addWidget(sb, current_row, 1, 1, 3); current_row++; } @@ -525,9 +578,10 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge QComboBox* cb = new QComboBox(widget_parent); cb->setObjectName(QString::fromUtf8(si.name)); for (u32 i = 0; si.options[i] != nullptr; i++) - cb->addItem(qApp->translate(cinfo->name, si.options[i])); - SettingWidgetBinder::BindWidgetToIntSetting(sif, cb, section, std::move(key_name), si.IntegerDefaultValue(), si.IntegerMinValue()); - layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); + cb->addItem(qApp->translate(translation_ctx, si.options[i])); + SettingWidgetBinder::BindWidgetToIntSetting( + sif, cb, m_config_section, std::move(key_name), si.IntegerDefaultValue(), si.IntegerMinValue()); + layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0); layout->addWidget(cb, current_row, 1, 1, 3); current_row++; } @@ -540,8 +594,19 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge sb->setMinimum(si.FloatMinValue()); sb->setMaximum(si.FloatMaxValue()); sb->setSingleStep(si.FloatStepValue()); - SettingWidgetBinder::BindWidgetToFloatSetting(sif, sb, section, std::move(key_name), si.FloatDefaultValue()); - layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); +#if 0 + // We can't use this until we handle multiplier. + if (si.format) + { + const auto [prefix, suffix, decimals] = getPrefixAndSuffixForFloatFormat(QString::fromUtf8(si.format)); + sb->setPrefix(prefix); + if (decimals >= 0) + sb->setDecimals(decimals); + sb->setSuffix(suffix); + } +#endif + SettingWidgetBinder::BindWidgetToFloatSetting(sif, sb, m_config_section, std::move(key_name), si.FloatDefaultValue()); + layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0); layout->addWidget(sb, current_row, 1, 1, 3); current_row++; } @@ -551,8 +616,8 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge { QLineEdit* le = new QLineEdit(widget_parent); le->setObjectName(QString::fromUtf8(si.name)); - SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue()); - layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); + SettingWidgetBinder::BindWidgetToStringSetting(sif, le, m_config_section, std::move(key_name), si.StringDefaultValue()); + layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0); layout->addWidget(le, current_row, 1, 1, 3); current_row++; } @@ -571,10 +636,10 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge else if (si.options) { for (u32 i = 0; si.options[i] != nullptr; i++) - cb->addItem(qApp->translate(cinfo->name, si.options[i])); + cb->addItem(qApp->translate(translation_ctx, si.options[i])); } - SettingWidgetBinder::BindWidgetToStringSetting(sif, cb, section, std::move(key_name), si.StringDefaultValue()); - layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); + SettingWidgetBinder::BindWidgetToStringSetting(sif, cb, m_config_section, std::move(key_name), si.StringDefaultValue()); + layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0); layout->addWidget(cb, current_row, 1, 1, 3); current_row++; } @@ -585,9 +650,9 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge QLineEdit* le = new QLineEdit(widget_parent); le->setObjectName(QString::fromUtf8(si.name)); QPushButton* browse_button = new QPushButton(tr("Browse..."), widget_parent); - SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue()); + SettingWidgetBinder::BindWidgetToStringSetting(sif, le, m_config_section, std::move(key_name), si.StringDefaultValue()); connect(browse_button, &QPushButton::clicked, [this, le]() { - QString path = QFileDialog::getOpenFileName(this, tr("Select File")); + const QString path(QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Select File")))); if (!path.isEmpty()) le->setText(path); }); @@ -596,14 +661,14 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge hbox->addWidget(le, 1); hbox->addWidget(browse_button); - layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); + layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0); layout->addLayout(hbox, current_row, 1, 1, 3); current_row++; } break; } - QLabel* label = new QLabel(si.description ? qApp->translate(cinfo->name, si.description) : QString(), widget_parent); + QLabel* label = new QLabel(si.description ? qApp->translate(translation_ctx, si.description) : QString(), widget_parent); label->setWordWrap(true); layout->addWidget(label, current_row++, 0, 1, 4); @@ -613,13 +678,8 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge void ControllerCustomSettingsWidget::restoreDefaults() { - const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_parent->getControllerType()); - if (!cinfo || cinfo->num_settings == 0) - return; - - for (u32 i = 0; i < cinfo->num_settings; i++) + for (const SettingInfo& si : m_settings) { - const SettingInfo& si = cinfo->settings[i]; const QString key(QString::fromStdString(si.name)); switch (si.type) @@ -664,6 +724,21 @@ void ControllerCustomSettingsWidget::restoreDefaults() } break; + case SettingInfo::Type::StringList: + { + QComboBox* widget = findChild(QString::fromStdString(si.name)); + if (widget) + { + const QString default_value(QString::fromUtf8(si.StringDefaultValue())); + int index = widget->findData(default_value); + if (index < 0) + index = widget->findText(default_value); + if (index >= 0) + widget->setCurrentIndex(index); + } + } + break; + case SettingInfo::Type::Path: { QLineEdit* widget = findChild(QString::fromStdString(si.name)); @@ -694,26 +769,32 @@ QIcon ControllerBindingWidget_Base::getIcon() const void ControllerBindingWidget_Base::initBindingWidgets() { - const std::string& type = getControllerType(); + const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(getControllerType()); + if (!cinfo) + return; + const std::string& config_section = getConfigSection(); - std::vector bindings(PAD::GetControllerBinds(type)); SettingsInterface* sif = getDialog()->getProfileSettingsInterface(); - for (std::string& binding : bindings) + for (u32 i = 0; i < cinfo->num_bindings; i++) { - InputBindingWidget* widget = findChild(QString::fromStdString(binding)); - if (!widget) + const InputBindingInfo& bi = cinfo->bindings[i]; + if (bi.bind_type == InputBindingInfo::Type::Axis || bi.bind_type == InputBindingInfo::Type::HalfAxis || + bi.bind_type == InputBindingInfo::Type::Button || bi.bind_type == InputBindingInfo::Type::Pointer || + bi.bind_type == InputBindingInfo::Type::Device) { - Console.Error("(ControllerBindingWidget_Base) No widget found for '%s' (%.*s)", - binding.c_str(), static_cast(type.size()), type.data()); - continue; - } + InputBindingWidget* widget = findChild(QString::fromStdString(bi.name)); + if (!widget) + { + Console.Error("(ControllerBindingWidget_Base) No widget found for '%s' (%s)", bi.name, cinfo->name); + continue; + } - widget->initialize(sif, config_section, std::move(binding)); + widget->initialize(sif, bi.bind_type, config_section, bi.name); + } } - const PAD::VibrationCapabilities vibe_caps = PAD::GetControllerVibrationCapabilities(type); - switch (vibe_caps) + switch (cinfo->vibration_caps) { case PAD::VibrationCapabilities::LargeSmallMotors: { @@ -763,3 +844,385 @@ ControllerBindingWidget_Base* ControllerBindingWidget_DualShock2::createInstance } ////////////////////////////////////////////////////////////////////////// + +USBDeviceWidget::USBDeviceWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port) + : QWidget(parent) + , m_dialog(dialog) + , m_config_section(StringUtil::StdStringFromFormat("USB%u", port + 1u)) + , m_port_number(port) +{ + m_ui.setupUi(this); + populateDeviceTypes(); + populatePages(); + + ControllerSettingWidgetBinder::BindWidgetToInputProfileString( + m_dialog->getProfileSettingsInterface(), m_ui.deviceType, m_config_section, "Type", "None"); + + connect(m_ui.deviceType, QOverload::of(&QComboBox::currentIndexChanged), this, &USBDeviceWidget::onTypeChanged); + connect(m_ui.deviceSubtype, QOverload::of(&QComboBox::currentIndexChanged), this, &USBDeviceWidget::onSubTypeChanged); + connect(m_ui.bindings, &QPushButton::clicked, this, &USBDeviceWidget::onBindingsClicked); + connect(m_ui.settings, &QPushButton::clicked, this, &USBDeviceWidget::onSettingsClicked); + connect(m_ui.automaticBinding, &QPushButton::clicked, this, &USBDeviceWidget::onAutomaticBindingClicked); + connect(m_ui.clearBindings, &QPushButton::clicked, this, &USBDeviceWidget::onClearBindingsClicked); +} + +USBDeviceWidget::~USBDeviceWidget() = default; + +QIcon USBDeviceWidget::getIcon() const +{ + return QIcon::fromTheme("usb-fill"); +} + +void USBDeviceWidget::populateDeviceTypes() +{ + m_ui.deviceType->addItem(qApp->translate("USB", USB::GetDeviceName("None")), QStringLiteral("None")); + for (const auto& [name, display_name] : USB::GetDeviceTypes()) + m_ui.deviceType->addItem(QString::fromStdString(display_name), QString::fromStdString(name)); +} + +void USBDeviceWidget::populatePages() +{ + m_device_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", "None"); + m_device_subtype = m_dialog->getIntValue(m_config_section.c_str(), fmt::format("{}_subtype", m_device_type).c_str(), 0); + + { + QSignalBlocker sb(m_ui.deviceSubtype); + m_ui.deviceSubtype->clear(); + for (const std::string& subtype : USB::GetDeviceSubtypes(m_device_type)) + m_ui.deviceSubtype->addItem(qApp->translate("USB", subtype.c_str())); + m_ui.deviceSubtype->setCurrentIndex(m_device_subtype); + m_ui.deviceSubtype->setVisible(m_ui.deviceSubtype->count() > 0); + } + + if (m_bindings_widget) + { + m_ui.stackedWidget->removeWidget(m_bindings_widget); + delete m_bindings_widget; + m_bindings_widget = nullptr; + } + if (m_settings_widget) + { + m_ui.stackedWidget->removeWidget(m_settings_widget); + delete m_settings_widget; + m_settings_widget = nullptr; + } + + const gsl::span bindings(USB::GetDeviceBindings(m_device_type, m_device_subtype)); + const gsl::span settings(USB::GetDeviceSettings(m_device_type, m_device_subtype)); + m_ui.bindings->setEnabled(!bindings.empty()); + m_ui.settings->setEnabled(!settings.empty()); + + if (!bindings.empty()) + { + m_bindings_widget = USBBindingWidget::createInstance(m_device_type, m_device_subtype, bindings, this); + if (m_bindings_widget) + { + m_ui.stackedWidget->addWidget(m_bindings_widget); + m_ui.stackedWidget->setCurrentWidget(m_bindings_widget); + } + } + + if (!settings.empty()) + { + const QString settings_title(tr("Device Settings")); + m_settings_widget = new ControllerCustomSettingsWidget( + settings, m_config_section, m_device_type + "_", settings_title, m_device_type.c_str(), m_dialog, m_ui.stackedWidget); + m_ui.stackedWidget->addWidget(m_settings_widget); + } + + updateHeaderToolButtons(); +} + +void USBDeviceWidget::onTypeChanged() +{ + populatePages(); + m_dialog->updateListDescription(m_port_number, this); +} + +void USBDeviceWidget::onSubTypeChanged(int new_index) +{ + m_dialog->setIntValue(m_config_section.c_str(), fmt::format("{}_subtype", m_device_type).c_str(), new_index); + onTypeChanged(); +} + +void USBDeviceWidget::updateHeaderToolButtons() +{ + const QWidget* current_widget = m_ui.stackedWidget->currentWidget(); + const QSignalBlocker bindings_sb(m_ui.bindings); + const QSignalBlocker settings_sb(m_ui.settings); + + const bool is_bindings = (m_bindings_widget && current_widget == m_bindings_widget); + const bool is_settings = (m_settings_widget && current_widget == m_settings_widget); + m_ui.bindings->setChecked(is_bindings); + m_ui.automaticBinding->setEnabled(is_bindings); + m_ui.clearBindings->setEnabled(is_bindings); + m_ui.settings->setChecked(is_settings); +} + +void USBDeviceWidget::onBindingsClicked() +{ + if (!m_bindings_widget) + return; + + m_ui.stackedWidget->setCurrentWidget(m_bindings_widget); + updateHeaderToolButtons(); +} + +void USBDeviceWidget::onSettingsClicked() +{ + if (!m_settings_widget) + return; + + m_ui.stackedWidget->setCurrentWidget(m_settings_widget); + updateHeaderToolButtons(); +} + +void USBDeviceWidget::onAutomaticBindingClicked() +{ + QMenu menu(this); + bool added = false; + + for (const QPair& dev : m_dialog->getDeviceList()) + { + // we set it as data, because the device list could get invalidated while the menu is up + QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.first).arg(dev.second)); + action->setData(dev.first); + connect(action, &QAction::triggered, this, [this, action]() { doDeviceAutomaticBinding(action->data().toString()); }); + added = true; + } + + if (!added) + { + QAction* action = menu.addAction(tr("No devices available")); + action->setEnabled(false); + } + + menu.exec(QCursor::pos()); +} + +void USBDeviceWidget::onClearBindingsClicked() +{ + if (QMessageBox::question(QtUtils::GetRootWidget(this), tr("Clear Bindings"), + tr("Are you sure you want to clear all bindings for this controller? This action cannot be undone.")) != QMessageBox::Yes) + { + return; + } + + if (m_dialog->isEditingGlobalSettings()) + { + { + auto lock = Host::GetSettingsLock(); + USB::ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number); + } + Host::CommitBaseSettingChanges(); + } + else + { + USB::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number); + m_dialog->getProfileSettingsInterface()->Save(); + } + + // force a refresh after clearing + g_emu_thread->applySettings(); + onTypeChanged(); +} + +void USBDeviceWidget::doDeviceAutomaticBinding(const QString& device) +{ + std::vector> mapping = InputManager::GetGenericBindingMapping(device.toStdString()); + if (mapping.empty()) + { + QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Automatic Binding"), + tr("No generic bindings were generated for device '%1'. The controller/source may not support automatic mapping.").arg(device)); + return; + } + + bool result; + if (m_dialog->isEditingGlobalSettings()) + { + { + auto lock = Host::GetSettingsLock(); + result = USB::MapDevice(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping); + } + if (result) + Host::CommitBaseSettingChanges(); + } + else + { + result = USB::MapDevice(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping); + if (result) + { + m_dialog->getProfileSettingsInterface()->Save(); + g_emu_thread->reloadInputBindings(); + } + } + + // force a refresh after mapping + if (result) + { + g_emu_thread->applySettings(); + onTypeChanged(); + } +} + +////////////////////////////////////////////////////////////////////////// + +USBBindingWidget::USBBindingWidget(USBDeviceWidget* parent) + : QWidget(parent) +{ +} + +USBBindingWidget::~USBBindingWidget() +{ +} + +QIcon USBBindingWidget::getIcon() const +{ + //return QIcon::fromTheme("gamepad-line"); + + return QIcon::fromTheme("artboard-2-line"); +} + +std::string USBBindingWidget::getBindingKey(const char* binding_name) const +{ + return USB::GetConfigBindKey(getDeviceType(), binding_name); +} + +void USBBindingWidget::createWidgets(gsl::span bindings) +{ + QGroupBox* axis_gbox = nullptr; + QGridLayout* axis_layout = nullptr; + QGroupBox* button_gbox = nullptr; + QGridLayout* button_layout = nullptr; + SettingsInterface* sif = getDialog()->getProfileSettingsInterface(); + + QScrollArea* scrollarea = new QScrollArea(this); + QWidget* scrollarea_widget = new QWidget(scrollarea); + scrollarea->setWidget(scrollarea_widget); + scrollarea->setWidgetResizable(true); + scrollarea->setFrameShape(QFrame::StyledPanel); + scrollarea->setFrameShadow(QFrame::Plain); + + // We do axes and buttons separately, so we can figure out how many columns to use. + constexpr int NUM_AXIS_COLUMNS = 2; + int column = 0; + int row = 0; + for (const InputBindingInfo& bi : bindings) + { + if (bi.bind_type == InputBindingInfo::Type::Axis || bi.bind_type == InputBindingInfo::Type::HalfAxis || + bi.bind_type == InputBindingInfo::Type::Pointer || bi.bind_type == InputBindingInfo::Type::Device) + { + if (!axis_gbox) + { + axis_gbox = new QGroupBox(tr("Axes"), scrollarea_widget); + axis_layout = new QGridLayout(axis_gbox); + } + + QGroupBox* gbox = new QGroupBox(qApp->translate("USB", bi.display_name), axis_gbox); + QVBoxLayout* temp = new QVBoxLayout(gbox); + InputBindingWidget* widget = new InputBindingWidget(gbox, sif, bi.bind_type, getConfigSection(), getBindingKey(bi.name)); + temp->addWidget(widget); + axis_layout->addWidget(gbox, row, column); + if ((++column) == NUM_AXIS_COLUMNS) + { + column = 0; + row++; + } + } + } + if (axis_gbox) + axis_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding), ++row, 0); + + const int num_button_columns = axis_layout ? 2 : 4; + row = 0; + column = 0; + for (const InputBindingInfo& bi : bindings) + { + if (bi.bind_type == InputBindingInfo::Type::Button) + { + if (!button_gbox) + { + button_gbox = new QGroupBox(tr("Buttons"), scrollarea_widget); + button_layout = new QGridLayout(button_gbox); + } + + QGroupBox* gbox = new QGroupBox(qApp->translate("USB", bi.display_name), button_gbox); + QVBoxLayout* temp = new QVBoxLayout(gbox); + InputBindingWidget* widget = new InputBindingWidget(gbox, sif, bi.bind_type, getConfigSection(), getBindingKey(bi.name)); + temp->addWidget(widget); + button_layout->addWidget(gbox, row, column); + if ((++column) == num_button_columns) + { + column = 0; + row++; + } + } + } + + if (button_gbox) + button_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding), ++row, 0); + + if (!axis_gbox && !button_gbox) + return; + + QHBoxLayout* layout = new QHBoxLayout(scrollarea_widget); + if (axis_gbox) + layout->addWidget(axis_gbox); + if (button_gbox) + layout->addWidget(button_gbox); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + QHBoxLayout* main_layout = new QHBoxLayout(this); + main_layout->addWidget(scrollarea); +} + + +void USBBindingWidget::bindWidgets(gsl::span bindings) +{ + SettingsInterface* sif = getDialog()->getProfileSettingsInterface(); + + for (const InputBindingInfo& bi : bindings) + { + if (bi.bind_type == InputBindingInfo::Type::Axis || bi.bind_type == InputBindingInfo::Type::HalfAxis || + bi.bind_type == InputBindingInfo::Type::Button || bi.bind_type == InputBindingInfo::Type::Pointer || + bi.bind_type == InputBindingInfo::Type::Device) + { + InputBindingWidget* widget = findChild(QString::fromUtf8(bi.name)); + if (!widget) + { + Console.Error("(USBBindingWidget) No widget found for '%s'.", bi.name); + continue; + } + + widget->initialize(sif, bi.bind_type, getConfigSection(), getBindingKey(bi.name)); + } + } +} + +USBBindingWidget* USBBindingWidget::createInstance( + const std::string& type, u32 subtype, gsl::span bindings, USBDeviceWidget* parent) +{ + USBBindingWidget* widget = new USBBindingWidget(parent); + bool has_template = false; + + if (type == "Pad") + { + if (subtype == 0) // Generic or Driving Force + { + Ui::USBBindingWidget_DrivingForce().setupUi(widget); + has_template = true; + } + else if (subtype == 3) // GT Force + { + Ui::USBBindingWidget_GTForce().setupUi(widget); + has_template = true; + } + } + + if (has_template) + widget->bindWidgets(bindings); + else + widget->createWidgets(bindings); + + return widget; +} diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.h b/pcsx2-qt/Settings/ControllerBindingWidgets.h index e12dd2992f..04d33614c0 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.h +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.h @@ -19,10 +19,13 @@ #include +#include "gsl/span" + #include "ui_ControllerBindingWidget.h" #include "ui_ControllerBindingWidget_DualShock2.h" #include "ui_ControllerMacroWidget.h" #include "ui_ControllerMacroEditWidget.h" +#include "ui_USBDeviceWidget.h" class InputBindingWidget; class ControllerSettingsDialog; @@ -31,6 +34,8 @@ class ControllerMacroWidget; class ControllerMacroEditWidget; class ControllerBindingWidget_Base; +class USBBindingWidget; + class ControllerBindingWidget final : public QWidget { Q_OBJECT @@ -133,16 +138,20 @@ class ControllerCustomSettingsWidget : public QWidget Q_OBJECT public: - ControllerCustomSettingsWidget(ControllerBindingWidget* parent, QWidget* parent_widget); + ControllerCustomSettingsWidget(gsl::span settings, std::string config_section, std::string config_prefix, + const QString& group_title, const char* translation_ctx, ControllerSettingsDialog* dialog, QWidget* parent_widget); ~ControllerCustomSettingsWidget(); - void createSettingWidgets(ControllerBindingWidget* parent, QWidget* widget_parent, QGridLayout* layout, const PAD::ControllerInfo* cinfo); - private Q_SLOTS: void restoreDefaults(); private: - ControllerBindingWidget* m_parent; + void createSettingWidgets(const char* translation_ctx, QWidget* widget_parent, QGridLayout* layout); + + gsl::span m_settings; + std::string m_config_section; + std::string m_config_prefix; + ControllerSettingsDialog* m_dialog; }; ////////////////////////////////////////////////////////////////////////// @@ -182,3 +191,71 @@ public: private: Ui::ControllerBindingWidget_DualShock2 m_ui; }; + +////////////////////////////////////////////////////////////////////////// + +class USBDeviceWidget final : public QWidget +{ + Q_OBJECT + +public: + USBDeviceWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port); + ~USBDeviceWidget(); + + QIcon getIcon() const; + + __fi ControllerSettingsDialog* getDialog() const { return m_dialog; } + __fi const std::string& getConfigSection() const { return m_config_section; } + __fi const std::string& getDeviceType() const { return m_device_type; } + __fi u32 getPortNumber() const { return m_port_number; } + +private Q_SLOTS: + void onTypeChanged(); + void onSubTypeChanged(int new_index); + void onAutomaticBindingClicked(); + void onClearBindingsClicked(); + void onBindingsClicked(); + void onSettingsClicked(); + +private: + void populateDeviceTypes(); + void populatePages(); + void updateHeaderToolButtons(); + void doDeviceAutomaticBinding(const QString& device); + + Ui::USBDeviceWidget m_ui; + + ControllerSettingsDialog* m_dialog; + + std::string m_config_section; + std::string m_device_type; + u32 m_device_subtype; + u32 m_port_number; + + USBBindingWidget* m_bindings_widget = nullptr; + ControllerCustomSettingsWidget* m_settings_widget = nullptr; +}; + +class USBBindingWidget : public QWidget +{ + Q_OBJECT + +public: + USBBindingWidget(USBDeviceWidget* parent); + ~USBBindingWidget() override; + + __fi ControllerSettingsDialog* getDialog() const { return static_cast(parent())->getDialog(); } + __fi const std::string& getConfigSection() const { return static_cast(parent())->getConfigSection(); } + __fi const std::string& getDeviceType() const { return static_cast(parent())->getDeviceType(); } + __fi u32 getPortNumber() const { return static_cast(parent())->getPortNumber(); } + + QIcon getIcon() const; + + static USBBindingWidget* createInstance(const std::string& type, u32 subtype, gsl::span bindings, USBDeviceWidget* parent); + +protected: + std::string getBindingKey(const char* binding_name) const; + + void createWidgets(gsl::span bindings); + void bindWidgets(gsl::span bindings); +}; diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp index edb33401ec..a32fe3c203 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp @@ -114,7 +114,8 @@ void ControllerSettingsDialog::onNewProfileClicked() } const int res = QMessageBox::question(this, tr("Create Input Profile"), - tr("Do you want to copy all bindings from the currently-selected profile to the new profile? Selecting No will create a completely empty profile."), + tr("Do you want to copy all bindings from the currently-selected profile to the new profile? Selecting No will create a completely " + "empty profile."), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (res == QMessageBox::Cancel) return; @@ -140,7 +141,8 @@ void ControllerSettingsDialog::onNewProfileClicked() if (!temp_si.Save()) { - QMessageBox::critical(this, tr("Error"), tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetFileName()))); + QMessageBox::critical( + this, tr("Error"), tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetFileName()))); return; } @@ -252,7 +254,7 @@ void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QList mtap_enabled = {{getBoolValue("Pad", "MultitapPort1", false), - getBoolValue("Pad", "MultitapPort2", false)}}; + const std::array mtap_enabled = {{getBoolValue("Pad", "MultitapPort1", false), getBoolValue("Pad", "MultitapPort2", false)}}; // we reorder things a little to make it look less silly for mtap static constexpr const std::array mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}}; @@ -401,14 +402,29 @@ void ControllerSettingsDialog::createWidgets() const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown")); QListWidgetItem* item = new QListWidgetItem(); - item->setText(mtap_enabled[port] ? - (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) : - tr("Controller Port %1\n%2").arg(port + 1).arg(display_name)); + item->setText(mtap_enabled[port] ? (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) : + tr("Controller Port %1\n%2").arg(port + 1).arg(display_name)); item->setIcon(m_port_bindings[global_slot]->getIcon()); item->setData(Qt::UserRole, QVariant(global_slot)); m_ui.settingsCategory->addItem(item); } + // USB ports + for (u32 port = 0; port < USB::NUM_PORTS; port++) + { + m_usb_bindings[port] = new USBDeviceWidget(m_ui.settingsContainer, this, port); + m_ui.settingsContainer->addWidget(m_usb_bindings[port]); + + const std::string dtype(getStringValue(fmt::format("USB{}", port + 1).c_str(), "Type", "None")); + const QString display_name(qApp->translate("USB", USB::GetDeviceName(dtype))); + + QListWidgetItem* item = new QListWidgetItem(); + item->setText(tr("USB Port %1\n%2").arg(port + 1).arg(display_name)); + item->setIcon(m_usb_bindings[port]->getIcon()); + item->setData(Qt::UserRole, QVariant(MAX_PORTS + port)); + m_ui.settingsCategory->addItem(item); + } + // only add hotkeys if we're editing global settings if (!m_profile_interface || m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false)) { @@ -439,14 +455,32 @@ void ControllerSettingsDialog::updateListDescription(u32 global_slot, Controller const PAD::ControllerInfo* ci = PAD::GetControllerInfo(widget->getControllerType()); const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown")); - item->setText(mtap_enabled ? - (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) : - tr("Controller Port %1\n%2").arg(port + 1).arg(display_name)); + item->setText(mtap_enabled ? (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) : + tr("Controller Port %1\n%2").arg(port + 1).arg(display_name)); item->setIcon(widget->getIcon()); break; } } } + +void ControllerSettingsDialog::updateListDescription(u32 port, USBDeviceWidget* widget) +{ + for (int i = 0; i < m_ui.settingsCategory->count(); i++) + { + QListWidgetItem* item = m_ui.settingsCategory->item(i); + const QVariant data(item->data(Qt::UserRole)); + if (data.type() == QVariant::UInt && data.toUInt() == (MAX_PORTS + port)) + { + const std::string dtype(getStringValue(fmt::format("USB{}", port + 1).c_str(), "Type", "None")); + const QString display_name(qApp->translate("USB", USB::GetDeviceName(dtype))); + + item->setText(tr("USB Port %1\n%2").arg(port + 1).arg(display_name)); + item->setIcon(widget->getIcon()); + break; + } + } +} + void ControllerSettingsDialog::refreshProfileList() { const std::vector names(PAD::GetInputProfileNames()); diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.h b/pcsx2-qt/Settings/ControllerSettingsDialog.h index 1569ebb099..79ac51bc55 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.h +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.h @@ -15,7 +15,8 @@ #pragma once #include "ui_ControllerSettingsDialog.h" -#include "Frontend/InputManager.h" +#include "pcsx2/Frontend/InputManager.h" +#include "pcsx2/USB/USB.h" #include #include #include @@ -27,6 +28,7 @@ class ControllerGlobalSettingsWidget; class ControllerBindingWidget; class HotkeySettingsWidget; +class USBDeviceWidget; class SettingsInterface; @@ -61,6 +63,7 @@ public: __fi SettingsInterface* getProfileSettingsInterface() { return m_profile_interface.get(); } void updateListDescription(u32 global_slot, ControllerBindingWidget* widget); + void updateListDescription(u32 port, USBDeviceWidget* widget); // Helper functions for updating setting values globally or in the profile. bool getBoolValue(const char* section, const char* key, bool default_value) const; @@ -93,8 +96,6 @@ private Q_SLOTS: void createWidgets(); private: - static QIcon getIconForType(const std::string& type); - void refreshProfileList(); void switchProfile(const QString& name); @@ -102,6 +103,7 @@ private: ControllerGlobalSettingsWidget* m_global_settings = nullptr; std::array m_port_bindings{}; + std::array m_usb_bindings{}; HotkeySettingsWidget* m_hotkey_settings = nullptr; QList> m_device_list; diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.ui b/pcsx2-qt/Settings/ControllerSettingsDialog.ui index 4b4fdedede..6d459157e9 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.ui +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.ui @@ -9,7 +9,7 @@ 0 0 - 1300 + 1305 680 @@ -33,13 +33,13 @@ - 150 + 170 0 - 150 + 170 16777215 @@ -92,7 +92,8 @@ Load Profile - + + .. @@ -113,7 +114,8 @@ Restore Defaults - + + .. diff --git a/pcsx2-qt/Settings/HotkeySettingsWidget.cpp b/pcsx2-qt/Settings/HotkeySettingsWidget.cpp index 4c25ba7b7f..a344fa8b32 100644 --- a/pcsx2-qt/Settings/HotkeySettingsWidget.cpp +++ b/pcsx2-qt/Settings/HotkeySettingsWidget.cpp @@ -88,7 +88,8 @@ void HotkeySettingsWidget::createButtons() QLabel* label = new QLabel(qApp->translate("Hotkeys", hotkey->display_name), m_container); layout->addWidget(label, target_row, 0); - InputBindingWidget* bind = new InputBindingWidget(m_container, m_dialog->getProfileSettingsInterface(), "Hotkeys", hotkey->name); + InputBindingWidget* bind = new InputBindingWidget( + m_container, m_dialog->getProfileSettingsInterface(), InputBindingInfo::Type::Button, "Hotkeys", hotkey->name); bind->setMinimumWidth(300); layout->addWidget(bind, target_row, 1); } diff --git a/pcsx2-qt/Settings/InputBindingDialog.cpp b/pcsx2-qt/Settings/InputBindingDialog.cpp index 3a6ebeddda..af336b3416 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.cpp +++ b/pcsx2-qt/Settings/InputBindingDialog.cpp @@ -27,17 +27,17 @@ // _BitScanForward() #include "pcsx2/GS/GSIntrin.h" -InputBindingDialog::InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name, - std::vector bindings, QWidget* parent) +InputBindingDialog::InputBindingDialog(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, + std::string key_name, std::vector bindings, QWidget* parent) : QDialog(parent) , m_sif(sif) + , m_bind_type(bind_type) , m_section_name(std::move(section_name)) , m_key_name(std::move(key_name)) , m_bindings(std::move(bindings)) { m_ui.setupUi(this); - m_ui.title->setText( - tr("Bindings for %1 %2").arg(QString::fromStdString(m_section_name)).arg(QString::fromStdString(m_key_name))); + m_ui.title->setText(tr("Bindings for %1 %2").arg(QString::fromStdString(m_section_name)).arg(QString::fromStdString(m_key_name))); m_ui.buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); connect(m_ui.addBinding, &QPushButton::clicked, this, &InputBindingDialog::onAddBindingButtonClicked); @@ -159,8 +159,7 @@ void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds) m_input_listen_timer->setSingleShot(false); m_input_listen_timer->start(1000); - m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this, - &InputBindingDialog::onInputListenTimerTimeout); + m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this, &InputBindingDialog::onInputListenTimerTimeout); m_input_listen_remaining_seconds = timeout_in_seconds; m_ui.status->setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds)); m_ui.addBinding->setEnabled(false); @@ -198,8 +197,7 @@ void InputBindingDialog::addNewBinding() if (m_new_bindings.empty()) return; - const std::string new_binding( - InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size())); + const std::string new_binding(InputManager::ConvertInputBindingKeysToString(m_bind_type, m_new_bindings.data(), m_new_bindings.size())); if (!new_binding.empty()) { if (std::find(m_bindings.begin(), m_bindings.end(), new_binding) != m_bindings.end()) @@ -299,8 +297,7 @@ void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float val void InputBindingDialog::hookInputManager() { InputManager::SetHook([this](InputBindingKey key, float value) { - QMetaObject::invokeMethod(this, "inputManagerHookCallback", Qt::QueuedConnection, Q_ARG(InputBindingKey, key), - Q_ARG(float, value)); + QMetaObject::invokeMethod(this, "inputManagerHookCallback", Qt::QueuedConnection, Q_ARG(InputBindingKey, key), Q_ARG(float, value)); return InputInterceptHook::CallbackResult::StopProcessingEvent; }); } diff --git a/pcsx2-qt/Settings/InputBindingDialog.h b/pcsx2-qt/Settings/InputBindingDialog.h index 510e0f12d2..c50dd08033 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.h +++ b/pcsx2-qt/Settings/InputBindingDialog.h @@ -15,7 +15,8 @@ #pragma once #include "ui_InputBindingDialog.h" -#include "Frontend/InputManager.h" +#include "pcsx2/Config.h" +#include "pcsx2/Frontend/InputManager.h" #include #include #include @@ -28,7 +29,8 @@ class InputBindingDialog : public QDialog Q_OBJECT public: - InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name, std::vector bindings, QWidget* parent); + InputBindingDialog(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, std::string key_name, + std::vector bindings, QWidget* parent); ~InputBindingDialog(); protected Q_SLOTS: @@ -61,6 +63,7 @@ protected: Ui::InputBindingDialog m_ui; SettingsInterface* m_sif; + InputBindingInfo::Type m_bind_type; std::string m_section_name; std::string m_key_name; std::vector m_bindings; diff --git a/pcsx2-qt/Settings/InputBindingWidget.cpp b/pcsx2-qt/Settings/InputBindingWidget.cpp index eecc820552..4832783710 100644 --- a/pcsx2-qt/Settings/InputBindingWidget.cpp +++ b/pcsx2-qt/Settings/InputBindingWidget.cpp @@ -40,7 +40,8 @@ InputBindingWidget::InputBindingWidget(QWidget* parent) connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked); } -InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name) +InputBindingWidget::InputBindingWidget( + QWidget* parent, SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, std::string key_name) : QPushButton(parent) { setMinimumWidth(225); @@ -48,7 +49,7 @@ InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif, connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked); - initialize(sif, std::move(section_name), std::move(key_name)); + initialize(sif, bind_type, std::move(section_name), std::move(key_name)); } InputBindingWidget::~InputBindingWidget() @@ -61,9 +62,11 @@ bool InputBindingWidget::isMouseMappingEnabled() return Host::GetBaseBoolSettingValue("UI", "EnableMouseMapping", false); } -void InputBindingWidget::initialize(SettingsInterface* sif, std::string section_name, std::string key_name) +void InputBindingWidget::initialize( + SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, std::string key_name) { m_sif = sif; + m_bind_type = bind_type; m_section_name = std::move(section_name); m_key_name = std::move(key_name); reloadBinding(); @@ -223,8 +226,7 @@ void InputBindingWidget::setNewBinding() if (m_new_bindings.empty()) return; - const std::string new_binding( - InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size())); + std::string new_binding(InputManager::ConvertInputBindingKeysToString(m_bind_type, m_new_bindings.data(), m_new_bindings.size())); if (!new_binding.empty()) { if (m_sif) @@ -265,9 +267,8 @@ void InputBindingWidget::clearBinding() void InputBindingWidget::reloadBinding() { - m_bindings = m_sif ? - m_sif->GetStringList(m_section_name.c_str(), m_key_name.c_str()) : - Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); + m_bindings = m_sif ? m_sif->GetStringList(m_section_name.c_str(), m_key_name.c_str()) : + Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); updateText(); } @@ -306,8 +307,7 @@ void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds) m_input_listen_timer->setSingleShot(false); m_input_listen_timer->start(1000); - m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this, - &InputBindingWidget::onInputListenTimerTimeout); + m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this, &InputBindingWidget::onInputListenTimerTimeout); m_input_listen_remaining_seconds = timeout_in_seconds; setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds)); @@ -365,8 +365,7 @@ void InputBindingWidget::inputManagerHookCallback(InputBindingKey key, float val void InputBindingWidget::hookInputManager() { InputManager::SetHook([this](InputBindingKey key, float value) { - QMetaObject::invokeMethod(this, "inputManagerHookCallback", Qt::QueuedConnection, Q_ARG(InputBindingKey, key), - Q_ARG(float, value)); + QMetaObject::invokeMethod(this, "inputManagerHookCallback", Qt::QueuedConnection, Q_ARG(InputBindingKey, key), Q_ARG(float, value)); return InputInterceptHook::CallbackResult::StopProcessingEvent; }); } @@ -378,7 +377,7 @@ void InputBindingWidget::unhookInputManager() void InputBindingWidget::openDialog() { - InputBindingDialog binding_dialog(m_sif, m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this)); + InputBindingDialog binding_dialog(m_sif, m_bind_type, m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this)); binding_dialog.exec(); reloadBinding(); } @@ -388,7 +387,8 @@ InputVibrationBindingWidget::InputVibrationBindingWidget(QWidget* parent) connect(this, &QPushButton::clicked, this, &InputVibrationBindingWidget::onClicked); } -InputVibrationBindingWidget::InputVibrationBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, std::string section_name, std::string key_name) +InputVibrationBindingWidget::InputVibrationBindingWidget( + QWidget* parent, ControllerSettingsDialog* dialog, std::string section_name, std::string key_name) { setMinimumWidth(225); setMaximumWidth(225); diff --git a/pcsx2-qt/Settings/InputBindingWidget.h b/pcsx2-qt/Settings/InputBindingWidget.h index c8e744b730..826b75f120 100644 --- a/pcsx2-qt/Settings/InputBindingWidget.h +++ b/pcsx2-qt/Settings/InputBindingWidget.h @@ -16,6 +16,7 @@ #pragma once #include "common/Pcsx2Defs.h" #include "pcsx2/Frontend/InputManager.h" +#include "pcsx2/Config.h" #include #include @@ -30,12 +31,13 @@ class InputBindingWidget : public QPushButton public: InputBindingWidget(QWidget* parent); - InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name); + InputBindingWidget( + QWidget* parent, SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, std::string key_name); ~InputBindingWidget(); static bool isMouseMappingEnabled(); - void initialize(SettingsInterface* sif, std::string section_name, std::string key_name); + void initialize(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, std::string key_name); public Q_SLOTS: void clearBinding(); @@ -69,6 +71,7 @@ protected: void unhookInputManager(); SettingsInterface* m_sif = nullptr; + InputBindingInfo::Type m_bind_type = InputBindingInfo::Type::Unknown; std::string m_section_name; std::string m_key_name; std::vector m_bindings; @@ -103,6 +106,6 @@ private: std::string m_section_name; std::string m_key_name; std::string m_binding; - + ControllerSettingsDialog* m_dialog; }; diff --git a/pcsx2-qt/Settings/USBBindingWidget_DrivingForce.ui b/pcsx2-qt/Settings/USBBindingWidget_DrivingForce.ui new file mode 100644 index 0000000000..04cb1024fc --- /dev/null +++ b/pcsx2-qt/Settings/USBBindingWidget_DrivingForce.ui @@ -0,0 +1,992 @@ + + + USBBindingWidget_DrivingForce + + + + 0 + 0 + 1102 + 476 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Hints + + + + + + To bind steering for most modern 900 degree wheels, turn the wheel one rotation in the desired direction, then back again to center. + + + true + + + + + + + + + + Force Feedback + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + + + + + D-Pad + + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + + + + L1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + L2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Brake + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + + Steering Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + Steering Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 400 + 266 + + + + + 400 + 266 + + + + + + + :/images/DrivingForce.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Select + + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + + 200 + 0 + + + + Start + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Face Buttons + + + + + + Circle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + Cross + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + Triangle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + Square + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 125 + 0 + + + + + 125 + 16777215 + + + + PushButton + + + + + + + + + + + + + R1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + R2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Accelerator + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + InputBindingWidget + QPushButton +
Settings/InputBindingWidget.h
+
+
+ + + + +
diff --git a/pcsx2-qt/Settings/USBBindingWidget_GTForce.ui b/pcsx2-qt/Settings/USBBindingWidget_GTForce.ui new file mode 100644 index 0000000000..e5912ed9a0 --- /dev/null +++ b/pcsx2-qt/Settings/USBBindingWidget_GTForce.ui @@ -0,0 +1,683 @@ + + + USBBindingWidget_GTForce + + + + 0 + 0 + 1389 + 521 + + + + + 0 + 0 + + + + + 1100 + 500 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Hints + + + + + + To bind steering for most modern 900 degree wheels, turn the wheel one rotation in the desired direction, then back again to center. + + + true + + + + + + + + + + Force Feedback + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + X + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + A + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Brake + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Steering Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + Steering Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 400 + 266 + + + + + + + :/images/GTForce.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Left Paddle + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + + 200 + 0 + + + + Right Paddle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + PushButton + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Y + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + B + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Accelerator + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + InputBindingWidget + QPushButton +
Settings/InputBindingWidget.h
+
+
+ + + + +
diff --git a/pcsx2-qt/Settings/USBDeviceWidget.ui b/pcsx2-qt/Settings/USBDeviceWidget.ui new file mode 100644 index 0000000000..0bb31796a7 --- /dev/null +++ b/pcsx2-qt/Settings/USBDeviceWidget.ui @@ -0,0 +1,142 @@ + + + USBDeviceWidget + + + + 0 + 0 + 833 + 617 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Device Type + + + + + + + + + + + + + + Bindings + + + + .. + + + true + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Settings + + + + .. + + + true + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + + + Qt::Horizontal + + + + 460 + 20 + + + + + + + + + + Automatic Mapping + + + + .. + + + Qt::ToolButtonTextBesideIcon + + + + + + + Clear Mapping + + + + .. + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + + + + + + + + + + diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index 34e7ef0e55..54ef848b3c 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -361,6 +361,11 @@ + + + + Document + Document @@ -372,4 +377,4 @@ - + \ No newline at end of file diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index 88777df26c..7b126738ce 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -269,9 +269,6 @@ Settings - - Tools\Input Recording - @@ -368,6 +365,7 @@ Settings + @@ -462,10 +460,19 @@ Tools\Input Recording + + Settings + Settings + + Settings + + + Settings + - + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/black/svg/usb-fill.svg b/pcsx2-qt/resources/icons/black/svg/usb-fill.svg new file mode 100644 index 0000000000..a88de34035 --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/usb-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/white/svg/usb-fill.svg b/pcsx2-qt/resources/icons/white/svg/usb-fill.svg new file mode 100644 index 0000000000..027465a994 --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/usb-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcsx2-qt/resources/images/DrivingForce.png b/pcsx2-qt/resources/images/DrivingForce.png new file mode 100644 index 0000000000000000000000000000000000000000..5c03762368715c2914056c91142febe7a72994a2 GIT binary patch literal 62533 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¬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�b z(=4MkSZZPH;_<@euyScu`#*2H=Qatv7K%3f;Ui$EfAe;yc#347K#a!D9dBOiaedrp zcPe~`NBS9uOatEa%KFqt4Ah>aJxj?ee(%@Wq_m^M#4#dGXf#Bb_Kf<+KTCoQY9VhT z!{r+0PsN_d+2X`9%lo=C*{x|(;?~aX%EMA9y!>;rv&GO%{87DnRep_1e&oV}C!Bh| z24`pQ2*Hf5DuX_?QG&^;r>#vzakVuA{TVk<+c{E0WaQ-9uY_`9QoH(8xo0OaSsQV5 zd@`q&A5v1XJJ+;SY6)%NOoys=^491g-6oa$KDxw#j2W;%hf0Sg@S2nd4^|f#z3suA z$cO?z=_`6vnQ#ug zqpioy%@yxia-HPq9@&W6mkSLrLeMJ&LF;|n)2_Y(AYU_tLURjU6$6^X0Guy=DcWvw zi_-nrn3zz~F#|!>4A&v!X?)m|fTg;A>y{WgIUDlVn(PZl1byJAsskEfdNy|)wMr)z zJc>TiV(wS4K_foi1`X!%uOJcEppVgpo+0A6fx$sPwsI{4gZ2NuG>pLzF%l*DnbD zWs10ZpDj8)e7k^A4Qg+V038X4C%JfdprMJyRb+c&*0t(n=#2;jSvufC=mqAhT zb7Bzf@ZyhbZ!3Ah?fqUnyi(+3WNPy*zy1|b7d)<%f4!xpB@x+4PUD8oBHQptDx4Q= z930%qt(>CU(>}lIC-;h#LJ#GbLD$Ng3xop={?hJ9JXbf2tUkPYbto9QUG->~UQQG4 zHFe!ABjYZ-T^ZYDiM2Xc3GamvAtH8L$%0)BO&Ob0{ck2lRgz1zg&P;n`;=Yisx9Xp0RS zv-Gug*i15icYYL7cY43xmyc*~(@RP?QNV`4JjC?yO&dAjvgIA0J~@O(N6SQ)ux*Lf z1Q`JV{bFF_w~~&|&eo2sJJR0Ei(z;R??H1QQ-m!V!u`K5$vyOYrSMQMg$DOt`lt=l z;_|(1loSMH{;bp`{3NrJt*zu-zdx%u6wcuS0eWZ+Dp&JgaZb(5tn*>=JumS0ZTQt7 zw&*DGyfx8QkG+JqPkKgeQ5C<1uq5QGe=-n3EtUkJdgYwABCVCp&v0VYp||KAXB zeP%7b9Ufeb#}}CdlbTTDdXpIFkwF_rofeywm$x)V9Kp&##kldCY3_;wxq33lat;P^ z?AA=~G+Fxl2-Y}*R%D?tFE5lgDSX~15X3*t+KDzvDpc2+ZL3)Dp3H_iKT_}VrieWbx;aWH8?g39TVArU2Aiw+6_Fa6L9p#+ht;AEl75PbgA#nzxv=jx~o#*h~i&!UhNv0Y(u+g!LS|9U|Yp1^17*I1{ z^Nn9>RX{h$JpR}tpEPbXNm{>V%QY!x0QJs-D+pHo%1(L*|NbxTAUYho1YnsX;L;92 zW;IakGmv<+g`>epPwnsCOTSX&Y9y}6%gdA3fErcs*gj1|!#lhNYq~pxv_D~wX@v+>qn+aF3JSu z(psO)ZSmCWg;urD_jCAMrQD`^wkqYD@qM2itM;jxnI*{u*OXoZ0+0JG18GOdh42Ue zqsbxLo`gBqynw{Qe~M?kmu9wK^H&gMqt!;Jakq2S zkbu^+d)z)xsjeKbX-SCy53!C684)0(S{QKj;|L(J3q!1)y^4w}k14yUE`9Hv(%;@9 zndJ4eGz3E{+0`r?Q>s3S>zbQ~VQ|h00YD(8OijQ8<>0$@E>fzS6>t2dMHTh){(cJ% z|4KACFy1&HF4)41`sxeps1{JA{R)N^wD-#2KByM9T9*01zFbFJJM^-L$F%6@#Z6*= z&1ooCC?|0roXlu?n1Tw3ryBj|@-Ya!ga75N%I8-4*8b-jkIZ|??3^5PORiVD_wJ3W z9i~QxMoSRuWKc$a(G!EL^W-YSBB6-kLzsr&NF4-kNp%ayXSiv`BIKZX-n4SK&vnB0d8?9)P!Mp*Kw>*#m_ zBG}(ZArmJ`@EayyobTNZT@<_u$Ndmcxi*k%=JeaX?q)xs%!%YEpGxrVeT^LOrW`jfxTxIR;aTw;ty%Za)YCe%R)kl`YyzQThAnN~(iQ*&NTGggMi zf?S2g@ic@9^b;h$-Ird8Nybrcsspx!}q)%4+k!gn&x`7)gfEWFHq67jGJZk>DVe1@IWmCAS_cQ^u?> z0>)mU9J{hum6A=KmEBHC;=>=_NqJ*NkMUs9)h`q6**J7pVgLh&u*S&I+*3N~lnI7qYpetsJ@LLU`fnRg`$j+;_y_f)H-At>?^%i32$bD>*Q`>x=T=o}X6eUI&VAUs z@%Bz1+XH+~4o+LYIE36d;>)sm|I>cViY|SNIiV3QUj5Cr0A8OMJNa00U2|{{1CICq zRP`qCRJL98@G)jiWZv+oaran?tlKH< z$JDOp7Mnlm=i}7bOsAeuS9j6@W{o~aK_z=3Rw&` zmTBwkl+vv{5A7K6sLPO{rXYXKSijxBX<{==Q(s?S3z~Df4hg>^U;~=_=g%K+IYrt* zs@th1`Q5ur>(=dhl5bhWpuC$1X!UVoB#!kkX?5egXc4D~ExpyN^#ZF!K)Jag}HW_=f!Nu>g>)4X90QgpQL@4Bv9kTaL8L9(iyl(3&dgM&fmG7 zA84?8y!6-4iWk3rfqcE?C$K7ywXwCWBgW_)xev9CLPAiqpSg2Ko}Wetotzv`v@fjX zyQs+tgDrX&2c1n`j1NCGZiKj`WKVC_WtCTrqNhLai zmfpU;)fjzoJnKopr)I!xtcZum5!P{X-u(&;-Jh%AN)NJ}c*O9GT+hhymv7&mC;$HC zeZ98Oo;zc}6O_?erKJftzE|WWB*q*qw?C9oP&k!~p{k&VB|qu1z;CcS zaoV$g|1y11_7FOgk3w+()K7l-LIC%&%1ZIwA*}$y>g;AGpAL-`Fxh^jnk(F!RdF@z zqbC*0C3A940cB!?Gq1Q}NGsHi^F6R1B?N4T2AQg+7CEX>e)C0 zA`Tf=%dwrYY-}Gj_q|C;w#=6;<4}ldM16frohEO=*4^C=$drOK8P=#& zGC6;NdL<)|VBoowk|Kd8>FeYaiSg1L28@E+0?(^_;?Sw`Rv+L(aLoAuazr2Q<;O%0 z&6`Mw&&J17>#RzjWCGePxn|ICobI zs_8gOQz2Xmp5hy9}xpF3Kva|ZjJcGomc!7w0NA^HO)9_Uq zzSe@P6n_=6`1^4DjISuNxTN2>VU91Mudk1KRAApmUZguPEzsxm*=uBL`}!e{;43OB z);s&SJUu-@*mAm-lCs@lm6s?0&ueX)STdiDIfEjxNm*HZ_hp@s5Ouzo@>(2i_|Z~e zoknJ}QS_+zJdTpxUBW3KP_r4!JjOswRPk~HhSXsUGF!wrb7cldc1aBWr3@;$YKCOY zG+1Z2Ux=C_c>NAI3z@4v1xaERwZ+e2k1LcNaZjCTc3y}xqw1Ci$xe$32!The<>BE^ zpH>McNM$MneabkYN;-DyjrpgdLV73nulYO*w>~L;}mNFo@1_?^a>GBiu324p;lvkRFc=hq$`kJ!vOZKtX#E$ff#mIbfUN(YP2LLbX2v?TM?%!+tOV7UBek_Lg?J_WARq-62g`F^F z7ChT2o5JQCT*yhGmEFMTyM>l|?#T8V|4xRQRjJRsw}#A3m|N;S08P1ZXvk5^g~+Z> zCCa8HL`T!b>CDGZYzOK)zq+3#5@a6?c{S#e@Pjc=2ArUxr;b&ZK3A~V!BLVJra-r; z;NgbZBj?F;^-fDmivb?-0QMj8Pb`$s2|5Fj3J!FPXV{Jd-8-9~=0%KcCLV2V6Wl5@ zpna;wP(Q#{O(%{WdyK7O%qKxGI`SzhDk>gNc+A_48dEaD?kbW^a1XVm2U-m8cMGW7 zZbS&ThYcGpj|{N53A|J;Y~}C#D4vpJvifCr4&0uL#wI3dOF+R})&Wr)M(Ub@*O&VY zsjHvUX+tn{L3wHj!MT94j8C-qk|ta1=s>^0b?iPrH9;xLcj;{lt}bTrd|iVX6LPQ= zXxfS*4XCO*w`JSJal> z3d25!fp*pvQSPKmm!cp`sV}CwODiNftRbvGy*l*!iQ{V51t}LOB_{cnI}|6In=-=W zW{#fgw4mft_+_UrocDdjuxmI>2T@XZ6zp=yM<6ohFavVR?1+@+9g)7c$8?f8Q?I6m*Cgph!aF z)5`ccGJc$rgz<=sA~dWPMK>1@5BS5>tgOY8szE^0;RMHujOnP_b>RcwVgCwVXZM~s z1EnkPo_>`?wr{9vaRk)6H*f6E3u~sG*}Vqad|L=`-fV)$*x0c!X^;9+w}L}kSy@r> zxmzVzW5467mD{s<4jGCthYiL~KH+AZ|FT(1Ge(xq;2%!3;eSQ5MU{>UK2`wp1@VOv zr_5+6A}$R+05)$Q@lLtR9pLCF1kfrJiM!$0vG@0n(GYgoK1auym!=8m0Af_e zcCi-l-M*a;KppX!bwUdsZeP9$C4K0k0m|L}{oo75f5jt~7ydEg<35I({et(}5T28by0R3zhgNsjkoJZ<>{aLHmgiMp_VvX^QU&ik3?q6VJ%?~+wcnG=zsvF_L1}Mn zo}~=zE@Jm9(kkHsNP5`Y+w1>j0ozS2;Pd5n)<`jsupeW3Qp640M@2)XcUdBFGU2pu zV=qiZA{+)N!VnpegQ4dH8(ECI9UK<>$Wji$`DbXR>*cj!?_Oaam)n56!^6WlZmq%< zIHRx$nyTvi*es%<5w-x8YiqDaxeOVDC(dD9+Vbw@OX_m_w|73pKXqQ{Jn1WzWONib zj}qP9cD$CQv8gHb8Ovh~w2wh!Q)dtN)|}Dparkm5G>+w|55_P6Msk(7ZH5ijf!uf7 zQV5cwy$%jz^pVfTF1Zhx*+afFj2Rx-gxHLZpTMBO7F0^=bz0Z1#$qZIqXQqKJ*8T^ z_B87Z&7Ze`SD6eaE`Kw&O3g(lzB=^D@eyrLWcW9xZaDC^r>Cb0#Q^BVb8*SZR$4{+ z1rZ|ZPhm5$lHkNQh*?QT!fhMhqbkHbQ(_o;($&R|Gjlt*8Swf0=C9Zvi6P?ol$~?ipX#0dx5%1d9C61^-jdlm)j{uwKrFfJA) zuB9#(_P+@KZ{jE3)z_E0?)Z*7248jgZ*`!A5fu&Htx(KW77OpaI1HtUyTl)b;CV@O zLPCEN7Ud=4RPzG`S-7F>q@=Zs$mK?u+TRbvzKMcqkMj}~O?`KUbPD%&1ru@)flRQk z=#Xup((=#8GKMM3exuKYsl5AxvRnFy*hd5l4HlLhm7F2)i}Gw+7GVkqLD* zmVqWS@N=FX@G4E(B&$k`O=4f}OlMoVUx=HZe<8ob{qg9`MaupDZgr{%M(6UNtdggs zS;Pe0hCHeOFWB+^;7Dk>8;WAPCXBVnnh8s4;i%G#KaveZwCR4g%&~2FtECf=~VpC6j6zc991*4NlCmbbH(tT)LgEfbTUke#Q%Odu=3{!4%s1f+M=o| z8<};pH3k+I?6|Kq6herWEWvK3E6Q0Ypaj`}J`orgqa%`~Pc^8xX%qY~!56>#@}ym& zjD-RgoP+3QT5$1n@&34%%rV5>6%LutccD2{I>qfOoN!O5A*9G!;BTe)(#)BCpzTl$ zsMu$M9;FZPFeCdPBP8Q4B#o;9MxOYx;QwJy-!+&W18$lTX=;3Sz z-lRPdA^N7fpY~Di8Nmjh!{Vby;-iseZGgk$+xK9Lt2J$khC{>t)XZHpEko-?L?|fX z41hWO1s+3#DGy;rDeqjMqF?9t;4P1fi%ZJBm1y4m4d?kc!^-te@*hM;7tFShY%bLQVP$(u>y+V%3Kvo?x5*oGuZ*B)dLSi80yI&YB4*Zl!&L|a_HYg?OI zx-$^NIaz#`3}>9d4+X~>>!rz?nSl!ny|Ue!h}^^QXhP8Vub~o>fbd)oiIPugd*4LQ zO)>>33f;`&uPOlFLVgBk5M40niSZ`)JXJvb15r|2=ymV87!h11Qky!kUTYloNY=PA zApRZ2JY2$!;YHWJHXN&d=RmP;==+li$H{OUUXDB{sOW4&7XJCbMi%`4K}D*hPq9WA3efoWU|%#QZ8zFOS@T| z)8}9Z1aBaopeMliK0h~c6^0$ z%*3#6iV4cjYQ1%k;4OMu$bk~@lar4x$Q4TggJ4=r$-q&ps!~5+3mTe%3x%;{OyHZ^ z&@BM^qtPWyT7e#7_NlX?0$jv>P%O^(ONvE@$r*dXvo8WwKd7c;D9_L~ zAvXlW9&Bz;^qnhQRqpN$~YQ68<`L9-@oj>IDdO2P>8bn7M&oQ z)GD1bH7<{vJRfg%ne26A{M*W2`xL{XzIA47+p+)*S2_qSyPK!7*J!ptP=ICM00&L5 zC1UaOw)EjRj~a-(Y#I=tf~4YNLsMWjcz}1n`8jE|Yu9Q3{0%s7TiNHRE3%is#Xw`@ zAiy&__~k#qE#3P5^I6*BBYoW;$wK_9hn6L@7*=*23RzReDTYXpFn1f2XPNgI6QFRq z&0#>$AL^nWzZ$>zExPmO>4eC3;5TTf{PKg2t#DOE4v+aUlE*@>UAn}9`5F8OO#Pty z+%jGR4#Z=1SlX|c<4zod6y&DxuDf*YhfxTV2XH+j1L%S_(2miFh=^LTBzM^&{WyuC zZ8;C2Ix2!!P$&p+TCRLC-@kr6k8;8VZ^R9596s7LFc44Zc3460S8&4RXdYnvd6k+i zR{vD*QtT81ZIl>PuDMVr@gn4AE(D>*(k?P-hISu(cH*y3`2fn1Z#XKUb(Y(9&zIl5 z`%pJ7-!+{Y(u{;@zp^do3B94O?7dD7BCBvwy$pO*P>R1J2_yb9o^F=uwV z`I}MuvBkRFa=!3_isg{L97TPt>GyQ%s0kK$2AoI2*psdqitbhkHJPf5+^D8Q5O%Oi#WV!>8;EcBoL_6B>BJ$>5yLUxbQFMO9Uulyk^9Bl9J~Ki*2->NgIu?1y#` z@U(lMem@|`v-;yqP?OD)(*FUn;Fn6EDL|24zsY#i$tm| zfc2(T@tH!w!Am+;np$6?Cinp!>jwuv2*N%-5#Y4geRC{JkgcWqZI%jnmlDJ|3z z)oevCxugZ66x)v~7{tzv%>*$ItQ=T>0TZJh=fPTWACN9>bQ^X!Bo?pPn0gM@9K+ZV zE$AVBjLo2;@SpDRLo%#HClJ(m_8UJndOo?sqgB&|6OznO!qoilJSFct#9*)I?k>$2 zBSStr_pOlzdxQPq)eF^jjQjO@2Li0QO+)L3^6h(_m;_td3&&n)@3R`^Ws1%L>U;)F zl^l%j2wd8cA6d2kDdIAI05ShU&Et0HP#C+A&|k(W^CuT+H>0!g?b|nyS!T6C_&Tx> zd{TTP!ak@25WUydigGc9hK1t~l;~>B(1_fH=C~9n%j*DR!TO~U-w6rU&XF=Q{}I-J zd2rX3)0O6mH(EnkZ8t%al5+muT577AdrCM@Ltb%7dw+=R5`v1(9dmXh02Fbww6r9P z$Tgs1OiD^Rd+{R16_r2o)M?hKWpstX+pt@r80d^jcTZ#k#NZ)eo_uxjE3 zo+G`)H(D>aHC;XA>^$3oV~ZNAgmk~6>CDoU8Hqs^0uPybNm_-V?6yFeGW7#v8%>!s zO&|v|DLsXZ3}6sg7DmgySa=Cbj;?}-Ffb8r4mL?CDRl6TflHs^{uru`B`!b+eKaaAOM6c(;bKW!u70 zKMLnS2H(?LkJ0by$lT()-`U;0kWkD%-sJBwiwr?j)rXG{E(vmu$4Hqze8SK3d0$t% z5u`|I8CtT0X|rZ~9B{vZf}DeNMonFPdGZUE4RMQri}Nt1t#4s~tQNlW5hgC60P5pg zB4>1Rb^X=aho?m{3bk$9)+;G-P%Q8v@aI8-f@$+>fJdORi-KMiKq*Dz`sqY|h7V4Q z#95L}SfjA={^9xO*dWB@CAI4zKioO4N^&vTwFdI0!Epf(8@3r{BV}hvI3vqYM#e*{ zXy*B$w;l=7?mtR^LqSXya*67Eahw zeJ|O2=eg7=G*=IOM~3$v~_XIRApwLAJWLt%zoE4 zeDrwN$=AuwmO!>0Muu%OvxDy6sY|N3bX4`}?9CrP^EWR8~q3)kQhMfobf+h#-L-QVm864JxAJ=x!~v#z z_wJ?tGO`#%53Q~9{VWXq5Hh{Vlzl4e1mm zL}qym5DmFh|Eg-`yC!8DmGt=0__i(9*COTvmD}Xj#5QeZ5=m?smA8mi?5Ye>NrIRuGA^!d9cWyq(f4O;R3dXwzYaa*MW66~ z&`i#w3^MY)ggR2hw9j16$J_fA#vUCnt+ti!U;t1JW36`1FH#bJ<2OtfH^QUn#+)Jc z4Q>v0pHPQ#>)v!I<*`v1fQnb$F~a}5Kr;8HF8pDKcfghF*EP0o*kCHMnT9fr5d|NT zcWDuvli?R&n@9-fo}s;vYJ8z2yUcy)Uiee3k@8r+pRZlFq6+6O(AmTIYqU)@wyA&U zWO%;hKpIb}q^ApPRiK>tFcM%S9;SNZk>OTWutvkVT6==xBlBV@40!t>W)Bcw2 zPEVX8fZN;(Q;nI>0sz~B_69R44QVa)J&dpoMe`Oz|9XD^ejLh~W0hauM&dG@Fyapkdr|#{<5;#& zLFPVO`Byqyr9dZzv09b`@yN~r$tMS^~cAL;cayopN4sZi1rvvJc zBzy!Mw&?X`UVbp3@Sg28I%+D=o`tsxg2c@NVk7-@*{K7n)6Yg{K79MO-0GuM+z2}{ z1nf6eUa1<@aM6TIOz^g(+tEh9eu8us{ER)J<@B{{{J0G_{-{?xKHTrewR6X=q#^x& z%~v}>C;YBWekxE7g~MoRqriniiul9&E0xpZE;7X#IOk9K(Vhvhc$Rf+G4WdnWoxY> z*PvF{T81o#J6kJ)Y&;l31WKz}1vZRe1b85XLTi9Wlv=G#a_s`80XW1#U@hIROviZe$ z&ILo&;DRoTA_jF63J?Y=z38JLxUY_J>R?@`^@xmY(5;yb-w@hmZ#VfotGq^*uKq(e zqgP5!8Fjst)60jZGrz8gT39~XOI#OEV`4K=AwB)^smJzt8YT7FcC;+v4hq!C6=NL2 z%v@c^7YPysvCUdo$ldfz7A53X{VX~HBY+z_CE01XR+b)xnbqH=B~_S>?Z8&AJFtJh zmMh>Y$eA~Rp47_jo+v78>B+v* z%nT#(93k{=!HEI(05G6)Bl!5Sc*P>HM>FNAMRX(Ay(fn-G}PLcU$CM}P0XQy@5KTe z8al$N_p4uOQBIuU!WO!Uy0AItln;s}In00m)G8WgJrAnNK-Qm#1vx_ajQUM{;GdE} z0TM8ox_I2L4D}l;vM4X}OfBzHnfK>POMQ31TVh>i=RH#pdNMMEh7QU%VR|WU*oPkr zK;0Hj0@>$J<5a+ok|MB&(*=QsVmNBnd^7u2L(_?>R2elI_^5cmd%Sy?aXmzWp?2N;?N}mb2jt?z}cvW z%H^bw&&Czh5!wXd0${Ncs^u^Wl1A+c!jmCp5|J^o{`ysK`QT$MhUuw9di3Dr-;fmm zp*QcUyd$vN6nf`GwOM)qh6Ou6IOP*#r|H?*DA4kTtXU6%^o-dc?;are<{iioz*0;7^VD5nY^pMe$y?iykCTn`iog8yP$v`vx7ic zl*0rjfyV0JfJYba;j&j8XKqu$Uxv8h_Vw`sL-FTS;lmttYa1I!Tl8 z7(Vi)?IO>~!7ZWJ=nn#dpM7_JyNkK9EzO0XH@DuWj=a*qgXbb5Dg(TIl@fj0IdHDQ zX&@ulX&^G!_4F*hedlZ@2x=I7RCaaesw{}YHq<ry8H`*8IDJ5y3s{679{(Fc z5D+uVL`yER3B#Jiz{rRJh%_78yfwjl4>F)#8IPh!482;Z4@v`W>e~g`K1ma<;I7H> zh@IuqIk$d3xz%+$BV1X2e+p`jOMhWsPyhiPPnVBDS^`L=r1cI`1xZ&22L2?`bipe| z)n~AJN?xV@lh$Cli(w%IN{)yCL&`z~p@x%3AgHkn5YjA|Wt1NwFV)trzYxDFVUL3Y zNrAyQj6t~Kr8T_$7HR z2+k?@xWSk3Yy{EB7klkka{T)PnSdnHT06u@uPNhqanmC(r$J?Yil+V{H@=29Z|=iq zFCbs`zzK9l&SAxDwJ6`|-kK;1D62WoxN=**s@N?KW|2D9DRIBgeV~Pjod@`<37lYeP-9yW&^R&V@Dj*tX($>}nF*?d8;tHpr;CVFe zdDm&5ylyW4ZEL-BsKdgqLmJFF(lyl!&lF_ejO-6=^|m982zM!%K9frSDL7=m@?w5% zc7DP@Ko*$!{O&xpd>VCuz8p`229!cZ6gWwZg@e@cjo``JP~)xz6t*$=%bk1XM6$Y# zLL`Wbu*u}>9rm@0w%Jq6#@?BeTdaHRSa~g;TTk~WCQp^LJb7}2UYL#f6!uawIP&J>gFO9(=#N<8+g{0pMAJ5SY1f%$@(0vXCrW^XSNrRP#Gs zcV)2A&b(gF5!VRDq60b;XiBmIGetIHh-M|mj&4HHG`Ps zBaQ)W=n%sfj`TxEu)+`Wk+B06@zj(L_^4*><(bti>$C(3_8=Dd`Lics2wF>6FmcP) zowT!^=;va-7S%F*RBfC>Z~17T3HCf3n%J?R16ahuTLX;si#Wr-e*L-$8w##h+=Ts7 zT`lRvkb))|@oplBztBlXv81f*Y$_sYDQDQ4ot+KZ;6~W%IVu-*Z?=A0LZh=^zLknS z`8fqtFbrjAU|=uD>7X-&ttTpCPYibXsYiD8 zzJ8s9^ZLYOmQsFJd!W$Hi_vC}Im>7_naJ_1wRkiD7{t>E4XGLAHpVPbA`E)#VF99w zMDHA)lD-kEo}F3bR*FNwsU}DFr;4ABs-pS=b)E4(_S=$TV$|3! zyanbNe^iRHm96UvpHQ*%(-F%D9rR!BtT&y6{ zDdIrKV=XVj5GC@)9G8N9?sg14ioI=?!vA5<{(-NPcb1p6aA{m|-(}U5{v`VrV|Bq( zQw`9~^CIalGU5wYqsn21i!8mR-sFSvquljoH(fRHaKD&EM(Mb8pL-Sc%Vxf>W__C6 zyLY#F=EV0?}B;6#U(?r zA>MU5#^DRKst0uZXf+LSvTPMJ9|VKih}`q2l(!F0tQKUux%1zVlk)Le@#onI&xdU3 z&Q6&!6L(IAAO1P!wf7P2CbVMpal7d9aPMdz?|_DdnQ#HlUbfhCqiAkp!Z-ipg=M1# zla3N0o^?*Lrj;of2l2)lbE5hH$xYJVm5RJm6;0I(NR*P%@!Dm?nv&hSF+n)+c$o~( zFDz<~sd+WQi?@`XpmjeEB$}~dTf2Fgiu``Zn|JQ)M8u3cLvFhLc3|)5)Xo{j?Qc^I zy?%b)fA|V+_iHX`25u^mJ5p`GT-pLOBJ6F%-=L1(=;GwG`&E}tx;lCCUvseL|54Lv z7cWqy@9nl3vdL+=l>U0Y{Ln+wD4DZ)SNF}^*hJm&nE5OVkY#5Hw7*$AV&rzoBSNl4 zaz{u0Tv8?E6`#L{s2z8P;F0+JroJST{PtRr*dMA9KvCb`d)SBbaSPQ~+Qgo=n=DV7 z>SU{KSKbxf4^jZPjv7UJ@rI`oygm9bIOVrxNQnBsCt4_kOzrKX;Jbi^4{s{!E_!77 ztN#C@m6_kLUIo5f-D`#qa9cjimF%nWw!6hr)NUg0k>G%t$y`XuYhIlL(P2}< z5gCGPhR1a66c=pwlr$IE+xs$c(e_ksS^HQCO_$uAX#U&ucSxK-y15ApIKB5^if)#D z!zEppC<`(LY5@xejAs%YGQiZ&^n)% zucn^tIPUd!K1T=fK}E%-YP>5-BqRgC2VQciT2unPzB4sz;tcfIVtZ^M75*0#{;7o# zUebX9ToM2~JIZ1+a)7s1j*gCi$IbqGksf-2|JFYmh_Std4o=IAaex5b8kFMcEzR(#k918+49DW8Jp?h)e{QaJa^h662S_I#2klyP6P|pA{Na5 zU&Zr}B=QLp)^}cxjcxg|yuADiwT3tY-kI%>uf7{IJRiXgP}=fzfa?cdid4R(N5|*^ zM9XSQ0ca-W#pbR3T<3ONjk|F+fKS=9V6_I?jk0K7cK12kn1 zr5*?%%+<ozcI$=&>CFeM zT3-du20c>Wc>!NQhdU1FkSfeqyu{l;4M9+zAh!@1f zHjr#J-OH}3EaiL+yQ69MjQZ%Vp>oQEkK9)kx?;1+lxs)5Os*TuUjXaz%LkOEV^D1I z?0yx2_!GqQ?%g{ex$*+g20#q|<)9Mn*R1fv%X#q|PjNfiXQLf)Ta~VT%V}q`o}XItkAHo!7|Pvt22T0d*ERU<_#~VkiF1`>M+bQ+UBjbO~>mCo{x8O zk0+Si;VFD*Ic@4IJ|HCi)-k{)jYBG%mK06|E(FN9;!BeKDFb}ond=yqM}|!D!VY+4 zihRj`6&CmF-Pt2wP8^QDfdJjZRe5jp`q!zxsp6)d%W8~cVpxmB>pcNJJcm+oDcHox z()c*_i}uj)9$s|5c%>_O789P0)Zc@sx6ILRw7?I z?Q%sYtnu!SHFM(TdOrcoI6?XzhTS|eIT?Jhm>ySDa~fBKebZ?ye(4f=9}D1<8Wpn- zoK=^l+w#hTLD=-Z-A(OIp(S9T>fSY>FiZ+OTz4`I6dt0!@$s5Pw=9HWeOw9y1e-2gB`<1 zkJv0w<9rls&{2Itopl!lSP;F*(EF5;URHGdjHdSA&R*`8mZV%Q80kAoVP|SEIt|9vw6b!(O6=aVZ(@&}5bKY0L*Gz#W^PWy za4?kvSN~7XbMxk>2cXBLw6cli4n-?-mfNFairnSAM8s}fy^a-OZn4)kK$bQBY@8KO z(BmCeuHNbCU%z$$9uD(~U=!JwSF5QsD3NNR`mkXll` zSc|(}whE($$SPM>R^ErJ&;nIL{c1J;tBnJLfz7g^(RH;Z(*iYJCAq~|RwiBfga>j) z08sJ5M+iN$Y5jOi4&_{2;!>dgr~gq?uRc1mZM?8uO4Bx#r;6` zfvod~_K7kcU^-}KXE#pre4Id_?27h7?OK8{Lh}%sazlg16C4!u@4;G>^G{Dt>i}|1 zx^?T;zu-e&>QCY4rwJepxG`hCiP`l0wVWThq`JLnsdbZdL>NqW?hJr8Ku-?rrOqEe zal#i0l6543v2#afpKi}Nj>bij&sSK`!>(s)VvYWsjzow@P!LQ_>sKF zz+D6(Qc&G3B1li^9k0&&UuQI^8#JTy_Qg2Qvf4;|K;QBo7ek)1Bg6NidF4l<+!*Y* z9MCH$l}*>G^#szmA^cCD_+eB+r}wg{sp&DeNx=3*k`QgCkMd^YSVxV^QH&I7$T(9KCqCMmS4`yw-g?GY==n@0s#dz1 zr_FUWcD@h-w-7WI4P(dK@Qt#QcVsI+P&}U;zOE-1@&G^~*m?>v!8kh*b7+NsGUAK@+|EooDCd zx0&8FcByO!dLlD&f)&v@m>IdmNtx7EJBSDH6k)GsWRy}q?WgR*R=iPN&G7KAWnaL|r`v>;4KY!LP& z+9NbvB1FMEcUVD=q+?}el>{&B;WiZRF~!uh*{2J1vNcx~o83=2rcovN#CSq7erh|V z3^M}qL8I!C;Ao@l!6XrOhNy!y74Z?bRa!~L0k_R0gls;`SdrdyH)?1se(ETsU%V-) zuQNvvrj;U$U5MJFtEXq% zm(QPN)$y(tbeZ9zM?YCP0=`$+$gOXjwuLw84lKEJz|ED~Ypa30-y zUi3bS4Z<$5av@A69g!~kCQwyKq`)Zoxq&aA%eP1TO8Lz|@u0jLJ*rkRv?mFv=l?1< z$_UjH+0ETZLm2heV9W^&4r)w+fFJ0~0JKX1xAvnd9Slf$QO)46eld-E7P*lx?qz z=e<4ANSsE}{JOg@Mtb*~@2GqwqszXl5mV)kwTKWc1i+L(JT^280ZFN;#6AhFxCgqM zptC)t36c)_yc7x=?L-(4ZYjh(24KhFF8{Az+5ip(dZ_G7(Xhv%rr<7!0AYp&`l*m9 zP&Lp8<>KLKFRjN=S^OQmG;~GS5l!iF8o|)$jE+bH{0h&&H zLFc{Br0^V297yCIBjZA&65spsiVy{OGjRupH9$juKR*^dVi>t@&{trY$Iiud2HzQl z8)5+nD%>b?iz$R_4IWS1%0@0G zjHf1?K4iOM%F0}L61Zcy6CX1NVzD_uZjnro(9jUXkd%ZcVw*`nb?9Z{D@Kpd)u(8M ztH1bP*?%CQ4~uQYqtcH3O55^{w6xDx&>*(7x1R`toX-@4975sZ*JXQ?Hrt`KnynVL z^kksp5f6_&5E@wX0bVz8h~+p!#!X?-Jq7#YW86u!o1#uBDg=`Fc2^$XNPXF$ud;EoijWI*Oa=3bFGm_Yq@1F0U4 zprY})Ahhk5hLGfrvXrl1kKnr6l21nJMBoP!8uJN;&xIKUhzL!QACc*`?Q9xt-3~N5a_+oWG6#9>C#y zvE>sxmSt%-;0UtG=&$dqLYN=*bMnKEZNJ zwUgl5u=caThCw^Hnk?<)5D<|4zG5k;D$0 zZRlhn$25#1uG9m&RUsm{;51edW!7t=i4t#=kXjH%Oz=HJLpLUDaQ`>e>m5=Q;^4A_|Yx#Ris zGf>0gkMO}O*4`FjJu^WX3)Vs4bD>KIArM*`SPR8AvG(i+Zm=xhj_UszR@x9wpEtyh z4yU0h^zwl}CwruB{_jM`xp7r1T-`g|guRyJKIC9WrAKDo;1z|tGPSk>TzTm1;*v_- zPOiXV4eiDu$j?%fMDbnwOY{@$XtCxK5rD9Pz^Ed$j2nrc#>PN8Y{V+YO@`M&^4eHb zGw8$9V=DjxnWf!V-MA7|g4ao`TTU6fjwdv$J9blIp<>BeasF}@eVa2HtuVLO#hn0S zZS0{~n=9|-Xxr5_=;MhY%27vtSo0~6+~YAKra|1Ctp@`JQ|y)}FTkdm{J;hO5I4ye zjCyvvZ;R&@6x3iGp$DdKT_biD=Lwy3c|6@$m7kAK?Wmey@Uu0n3Y-R=*R;ZG9&sB% z7m8h0SX9(D{ovD$TR8ta!`H4&uRvG1r6*!jwtVZ}*h8lqGmO3hq>jUW3paW_H)vg4Q#?2Xf9gI+I)TiT;G?b)uQFY)LmDm zLyJ9H-rsHeKd%s7AX*;p=kbF%sUvlk3y@IiASPloh0LjvuYW40azZ%&KWLHTOa*bM z3qkoWgMljwKmC-UDPO>mD=Q!MY(y(>;MMDDx?b^5lUMoY( z^{?_;&cB5N#=78>b~jAt2vV>h7xA2esT#c6D@#9Yh&pZoeaDO4W|ZtU>80hPbcspV zuJr?yi$60d2J+VOEXt?5aMAlKrMcsl8`GhSn=~|*if+x55ZENlm2va-E{qpr#eu=V zoZPub%kSm&abAXzHfdW9;Vn!shu{wZwIUl-VQ;7df5DglK0e9~OXr`|0}Z}${@L<| z6q9b8CeC1%or2y_0#X$ZYKn`?DE-FNK_$}LLOxS(WoIAo>ZhLEvF1BUMzgEevfkhQrS(E*N#55ZD#Y*oFG;HL)YbK{e)u?-zvm5lCZ|pm8N>dj0 zUXz5h23HDU*+C|o*MYP;QZE`ur$ko^Yv^E6GD2rH2}#ofx$^Hw=9A0Ik2S$&n3Fi} zx!tA1bv!Fh{Q?BEntA{cqsr*irqS+>>Ax zE#ppmbalkQ>K~YlowCafsgUTl#ZEsP#Iae~s3Rkf)^jD)p#c{IOe(BmpX!9@Hp_JS z3ygXy{=I?XgB(@ZO3S1Rg!WT7ptS=Ze=IiebVX2KE+ooX5#j$Ra-Coc2lR8{>< zdcDr3Wa%!Ql+?}RAg!*UBz=7^Tq@UkzHyYBu3vNf_=DSn^g!BEGw6U$6tANE+g*O0 zns)P0T-fzv)Ad}*qU{zI6N$1Jv z3=c7Y`pm9V-g!VZ<8R*M5e&^R*uFioWwh;HV2I_d-AFHShOFV{jz$DJ;qmejZ(-Iw zt?YA^>uy)y&Mggx96J}z(L&`Wo7Vmcfg$@;qB9Q(6YdgaZZqY;01Vq7f?h<;#~vTRXdS{C*E! zIfFC_-_q8nJ6H(%gjyb7$a_+spHGK`WA@|wCr=D+EO{-q*Q+Pm@Nm7*gPJke%j?z+ zz!RnwLs!&Y_zulm&%Ar~=kL1rMaHVX7vK7IzJkTn>z|mqfLu5y-keVItnjCl_^VWW ztRG&0Z}?#KI;}8_b&pn*k?6ZM1#=cwKr_5Bh{|OAP*2(TtZundaJNlC#QDfH7I^eT zO$b*!9>;O}1BY}f3cf$8%F0&>8xdI;D_LBdVO2qiK;KKM+A#3)??J4JdSEOv=#|?U zdHhU*i<>gELqjm1UcAWClzhVt4`vmG6t;xDf(d{34|{t%7TmW=hD#YM>oSKy=~Zg} z7|I`-GU|f{CT$)~P1#>9{VjwnQe_hPPkT^edNeH=xhdwd0@N^YB<%`zgH*pD6A{vr z!7sgg-Rl|2zmQ*Mq`~*^7kV%M`!jg;{O`|5H|4+A{p*cKFFU~o?5AUbtw^qyBlHGM)R!*dvvg)f z;yrm16hm(yMD~benCK^0_LGoTk&iy_Py4;PHCVSZ*L_qmq%>c+kzU?Q)JRPBTtm`8 zI*9CzDm(R5NJne?^iL6$V3ISg2o-U;1DD4k4-T>$prCM$yOHQ4g5y0vN{lq>rZtMO zMjTWAR!yHA^BN&p_=wFHd%IagUl_6B3!EE3 zg3@7fNdqaZLK=Ih zNZ%pEWQdqSWS9Vg?}ylQ^6+>gp(zO6<-aBh>*tE8<^^G;Qr;=loRPg0Y(XsSMr2|l z!p`5L$#{$9+YVO+9$z|<* zdMeazd@fe~po;YPfNg-gRnxAAl989R&GX>(ca%pBOLbM#yxp*YT?Ip?cO?B*;ngsT+b= zVa|vce=b+8G`JC-;7TFVe6^ZF(Uo$~mN=t{zb^O`{>4sWT3fEsNF2LIraVuG z8B@Y`xtcWaX`;_?eu|_s;MR_p)(|&*WXyS0gYB=PJFaYm<>e3Y@|}pp!-McTb@6Ax z?+g^bkyWVGwlP;0EPrtdd%XSmOc{k!0&_ zF_HEb)iN2OQwjE8%D4;33ffPXUMs|_joPyBE%U;s3BJ^L>O8)eDyJ>`MGaFO(<{Ro z`JAk0q^L(vT{wjoYS7Hbosh5np7#Uwzg8wY3GoP14I!REnpj#2@k^IlD$P{Dz;MwU~cV{gV1c=$I^V@#?Eo!-@Qt#?;cW=Bh<6D-BBwBJyda?x)Ij zhcIYr=N4rr4xGNhcw^^g$-=!4)Dwy9SCYP(^nY1YUr1WO+w>wM52p|5h{W^!tCB{uZ%%!CUU2noy8e<8ixKvXe161RY`%-Ja!!ERVHBZRi%yH4 zT8=?hne}X4J140Mwd|e0XQ?^_DuBDPcL;8NxBL9wvo0X*U-IHqnl;bpy5QEh=ic zwu!b$)1mP-eG0F>Uw(X&&ibEP_qN=wPy6<7rKgprH81Cnm8_g!xw#TC zp4lapDUe|%>G8FtQ*FxNSD!LzW}?`w$-)3VHN9YL#XOIwy=c5i(NJksuGiZg+f_!T zZ6){2S*2Np3qK@&m|1sSXsqh2`l8q3l3^V-sTRm{>~QRR+=sjlI*WtFiweFNY=OJK z>-&JK;fb=`nB(@lk>!gqSv42wFUCGBdiblUi0ek@pSU8fo%!UR_@0{-Vfq0JgI|ek z=WL@}0?7EnBEn3$_>Xspf5MR#_3j;`4WJf1Es6-iVk@~w1 z5gH-6=PEjpS7e^8U8EAsXNWobm)7g-6B3_hwq`81Xy5t1$YC{0LG~+uH3W5ipZaW-HomiWltpfbgMkBkiblsq*66+!PuOCNRa3q*3rbNp*!lc?DK+*bKT2b<)6pm zF1%RHJYJ@0rkQ?PnH#QcBFttwS6}EX`%b~lXN+W)p5R>gHNrxL>PMA=cfRWtESn@1 z2h{WW>fQ>rYWn=JsRHNL66sZvUt#`B<3r2W^-M+0CM#_B!qZul=%XGL(~U7E(FYFm zk9+@OiE#PckTU-4{hNZ;8i!t|ey7E`AHo?H6wlu#%_ULXB_A1mPd<6={qolKr7_W@ zZvL^_kV>_hR}Bi)b&jt;^>oWC8|N5r8t)mOU+JHyupQdH+qc5vSGO(o=lhsN-Oi!X zr;lS-ajxoqclz$Queo2oqg)*Ic5(Ol{+;VZSMInici{Eo9TM?SR1VdA`_9&{Xlxie zcy39ZqJ!h(QTnQvF!_1%tB+r_&+xcne10zOoSNKWCzl)bjPWa+CK}-IWW%zFI;*Ds zO}(6El)7(WXYoO=OQ~=0_YdC#^)U|07g7plK3W^bpKgvWJ{(BE{_TxPPfYg@5cxN5 zGnQTHH*eoF=}hUe`hn`+bpNH}=y~}%`5w!^)%X6s8~a6TQf-o2B4>T)_r2c=M+pOZ z9knlNQ;TaXZ`G{$)>rsB?JTlnT6YCug-c?W%=TW+C;>6Ws9Tn;nnd#=SHurjlOa99m24X-lO=fSV}d5*p%2W z;pE{<88S+;vNOu=hl>ZV)p{Lzc{3P~vX8zLGct$gh}Fdzth%DWT!cgWSX4 zjZ4fWW9`lD>>odQONv`Er7E+fbnO-;;2 zWAD*F6F#QjzRwV@c@2lN-ESmkm87FL>Z6M@^jPw3>w0+ctgxFv^k+=Hd0q_zp;jKR z72`OK<4uD_=P0K;yRffBe<~fA__s?d35(MmoC4 z-+%vUYHI$FNa<3~Jnj(@5#b{uV~Vp(>ZOmu5V+K>x1WDb{GZ#aXItY?@OJ%~c+TX* zH4~GxoAz-oj*bMkZrw69Gs8g&i;8X?O^lA7Mr`cujW)2UB{)HFPtOt)50^7>^8ao0 z_xGm;4n4R&`O%+acW>`O(Oj?8aq+)YK4I#-yu7Q|Yd!0mM~F^7E8B^lnOXMtaG8Aw z2m9`jV+P5xDPC#Gg*{x){pxqaK_&K+_hBBJ`9g8Y19M@L6~VPXF)sZIw8 z2M33PTpc<(I)zG%!dsc-)YNO_>8@yuLzTDAKxt#0%1uC6Y)f-!e|>-QrBOMCL+8hQjoM7<4uJH-ie zlN{2dVoqI5n=2D(RfkM9qg7n#%7N_r+sl4ay&M_3uI}z1Y6A}U^m)}WLIfC$+!+2VXim&(8cwh0lay>Z&nG7|NW@@YwH~x4Jj!pBU{tl`hHkWN=nMj<*}q%-1sYof{8MI z+d>fRNAFq;mLE>d&3QWh`}^zI(GJsUsDjzlr_QfeF)f#=*2`3461T(0;%SJ|)6-AC z&J1X-@t9|~dT&{7lR|KESDpOdKT79&Qh$9x$;l8cetTGKg$6@-7M2qBKTa!Gdj70} zC6#wHH5vr*7YqD7RhBJ6EG;e94mNu2;UU)k{!P>}ZWnB)Vbwnu^XvL35nq=-U4oN#LTEej3dv$}KEhG}kEQx{QM^ ziGfk7jN1$rsv$!KmH;9&JS&UyE{E1PvUl&^QPI&h#`lXS+kRa!7D- zqeZyL`&(g9bTp||z&}SaCb1yLAPx1;7Yz+*J{}+KHxw#P{lS3+G~&!ABqbp+H#9eQ z-d`&WijAeTda!;eIW0}{3^&^=W5M;=Sml|YpArJ$LCeYnq8>fk3l%uYSjE9-0!GiD zJ$p7hm_d)mfgTfYzho-yv%tN@*F;W~x9{B1uDm<(*)ZDFbBO-kF{IPJyg>rbMi6K;G)Q)sR$rG;C?!GVBFL4ozx zuV2!?8Yd@}&vSBerYxSk9*2~ioLs?g`%ifU1)FbKb@eWDw}`5JdrWXh2p%4{gv6yw zmxilcthU)zB{$}KC%Z$@E$j@!|8LD1sG1CruYCLV_f3SDgrwQR zrM}HIk{Bt4zJ|GU2?v3SRb2n~^SMb`S=ou}4FUfA16=4XDQ>S_QRR*&p6!U8??R{* zpE}LiKvm@D=SQmTsnG~uEYVEhEz;+wd!8x*sTeM9-wqFwIZb7d-rClNhHqzASD^rr z>dR8Q`JQyrWQvn_l}0d9`FVR!)y!wK?5@xH3^P%GPcwYe3bn{}rZtMFQu5bB2efJT z8{pvJ6iAUqUgD-^5H`mb6%{2^!-)W4ba*$(@I-E~Vz6^yxwv2m!P-}cD^h>9lp`uOBNYO@o-Qk;Z)%C`(=BnV#5 z!_Ob-7!;YB!wO4d3r~wQrr@Es;_N)z{+QIL;5%>XeLUzc$r6qvLu6D22}Xj6!yoP! zIGrJ2@Nhy{>}@k$W~TGEnT|Fxk6kAa>cyh$Z0-j~|K>GxbYR!u?i3{hj}9huBG71O zr(t5MmNAXb4xJ6dYd)`C`w35+B!LT>O$*V{!#D8o^TRH2V2)( zPEFb9q)J6KK@}aX@!*3;r(An}f<^X>R8$%Tp_X}gw2aqy^p#`vV225pXWF8PNlDe5 zoSbxJr&LdZ;T0`>{=2ttKMTPRv)G>tfq7CYB_+!Re;A_u zz+p}4Xm|F4q@-l6JC`_hUnYA6)kt^u^5?(5z8rtKhi*OD6&stW&kG9+>qsla`pcIu z>#jydE_{S)XLoe)4-Ix?G4vGew(DRJtU_njjX{d)>gveMz#|{15){j$74~8nOvTT+ zIkZshZVg#;C48#>&x^&r{V?n7?$+%JYsFwS{Xc=4kJu-~P^i(zIIhdX=E=bJfR z&uKL27av1BpUut9txO#Yo9jvck{%Wrd7A(1Spr*ITj?s=k>TNI5S0GS4(NT~yS6+M z4Ee?>Ba>W?hr#IO14>&OD(Qe~kNo$7Z%51VxZ^bxusy73gJ`0nzS##If|{M(%A*(u zOMY^l_8iTGYkx*7ZgH=auzBxHc-53sQ5*1l>h5j>eB?ZMTIYp~8zUnZE!?7XT5>Ye zYIU_t)STzbUZwpQ^pkaeF7&w3pk{OaFn^*GA==)1$Wo1ui zW@fNZCaLJ?TH9bPbcNut3=9mQu7=dtAD)fE(A1vP)o@tj{5}X*5g}e)UL;6{pP$&J zev3hki`)4!%Z0lbV`Dl*0}mJ1ZhK`yKIsj*aMGu{SD;R59Q4SqHbM=4NkzRF1V#A4 z)^`)>=(rkrv{uCr>ZYB)W8mgirgr;wVM9bjgm=#i*@f{MNl2l7uKy&Ny198OirhxV z#u(QRaROZ7BRF#nqe@FlQzglHb&^Z5J1Y|#$z*Ul z1|c`^GC~KW=}nQ0s3N7NUW|g935|$&Raf_-iZcwV!wO7jl?Z zIMZi9NI~%D@Qsg;ul*_SMOz!iYrRa*D|&jXS=S1p{Wcf4?ByJEWQ<4utDxJ zh+1`xL7;3tg8~X!6Q7*ic}F`6^7VJ#_G0V!-Di=VuHSxCK6}kc3l;lNxci}9F$X8N zgigf}vn!4S+;AVBs+t-e)Vlpbr;BbkZt&~z@p4P(Lslm%2L^QG{)CXqNKpR+eE^_Kvb>jw@AjWhHj$BJ!)?)w)C>#&gLRTqQm*^WKE6=t zk&eSGxT1vFWCCDvG*wXY(N{g!P?4UMH3nY&3O0r;d@~i8fSDPy;JI_Qc6PW%Mgww( zBMA?zx%~(7bXn@4JyDT_X=rNR^jR5~HL0<;s9B*^dYxN!caN~Ev$cZ za;UFQ-1?>dW>H~bmmxnLL&dQUKiwl?At6pW66s?KZEfx0u`xA%x*4cc)yMPLjg1Yq zrHzWp%1XF5S69~{eg4q;dPN~2A?Ks({B+rVJ1eE-<+CAr-GW?PIA5L%<5eRmkh?L_ zb1D+ZjI75T+AUw|Cv<-Lbn(g+ig)GZP&?u>Gh=JLR%f98clP!s4eqOoQe`=QdA<{> zY71Y4SAG2aUiV}u%6Cmn)MGYe)8+2V&8@6t9JRB!+@>TZ&i7%IQdFGq{QmZ;M?yk^ z;_>cm>@||G=9yUKqZ?QEE9{|6&7N$ZukVNbt)(HNq4`@&pZod@#y{M@B_k`_)YnH1 zRMqwV{jlU6{U4q^ zqbsy-U zP`z^H$+M87ymRC|Jv}oNY;sw!;A`94m*D}KPbTJTm@Ru+EL%d@hvF*AIRhEO&&Wdad^$j@(I znM+XTntj&++i!0sc}mH$N?6{qdmjbP)7s>tO&Yp`)bUnmf6# zUyl(I5?;y-F={ zTxv`tIJ?K!gJEalUc9h0!o_g+)h~}#HB2up`2`Cw$A*T6TA`}SHo&fHsj-kK(TWNR z)nBL;r~ccc%*fFFpCk#^I71T=9-fWu%^+)w6iC!!mG>lrNa^hCRDqk)rS^ux<#`(x zlFNwuF)(9MltwM3272AUPw%xfNDh(NIMozVb1^u+)_a|Mt~WFG;X_;oJhZzzqEIn9 zD(V!%`Q!afTGC+ZFL2r0w}mJ;lnxqkrzw?oZFDi1R%c#JtmLi1!Z^{7yLNeVlVPea zy?589<224#M%espv)S8RL^#Qbi6cj#9$4n7!fMOqYzwpr;kPfYO}Fg+o6mIH-@cRc z`n5Jrwk5xd^nVFp}3p_qby`xs66qKsH ze4iMNt0XJmQvm@1<87V9{E1poP4-Rp=px}nX#wmc^VoTDh>|o za+~DnFilFrDCy{Du(-p$y_6q6ejK0q79?z18YGFx@N?n545_uxskOtwsv&zaB;nK+R1X8ZEu6bkFYq3|G}ah@L(9y4=+gBOZ4y7 zR#;{xM~JxC(b19R10D1dSBU{AwZ_NCleMb+oS)|r6eL++Uze6AcH7~UFs59!f*=|$Ym9IY+wy6FRhtt{9(|%#{K?oiZh005Pjl+Y3k5SRl zX>AUjrn~$5>Tq)nf>2d};I>0U{(PT^Q00xW-S2O2fU-G=b3!d)z}=GvSuFC=qvkUE z4(~=L=K2WA%Pb1srGUyJmnclhPyoq(mAHC2|43U`w{2yj_7(%BM?KOP;#?vRm6(Bu zkg$1RAfahN{H}=>Je&~zy~*j8h%(z2LMVG0I5t4{YCWOK27RJZ9Q{8eipa>zM?;mr zlkvRE4rMg$*NLn1O#@M(3>HSlYvlzU0y$oJXX3*aEw7sK1*1G2Ga7n&X=VhHh9?8| z#&>6>+h&uBkpqQ2q``ffy1JJ@x$LgY4h{~k1r=tAGKI{brEYulRwmFDd8%Cx`2l80 zr?pCM29)th-2Pt{+i-nV9vK;da(Myb$~%q9fG6MIBEF=gL`F<3u9klBra19`l2mla z7!s>bxqb@pWK_fcioA@Bj2N9FXVRB4PzPC+9+;9K4;W+e-EKM=;n+Z~&j9Le>gjpX zv=9vKDuzv-0ptL~k8WtL$D(O?$)fUh3W-YHts#+nE8{6I7lLB>%#*| zFUfO(roIu&DRlVsx(f@G3KN@&W`lU>q#JZ{wFQqyXUX5j1@B0Nw24q4mz;Jqk@t zK)2L3B7WfS&-^GOGB);jL)G3c+wL?*ZY?|T*vnv}R`Wye&`?z#Q=wJ8@3k8@Zg8T_ zP(ll?Xi2Do@*f6V#iwy`LC{oMe*8Fk+RzXvM9bwI09jXt3Meb9NW02rK2ukUB+>3h zcbTJ3s3}S30zN)|;oG-wB}geK!W$Zt@%m`thD|GPGt-fTNr75(qS(BlT5Oe;mW97w zgFH(TwrFR@I~2S}OhEzoIMEE{x4f#O?P2XX<+ylwCXgf-PAZPqq<>QILKJYF|F%`> zTi06mww6pJ=(#7vow}0k+uGVRu3Yg{_W7HmcK^OK{I0`hQw7kC+k6jO1gN`%vcPrI zVIghvR7abIH8ruM7tv0Pmi2r0q`JDhVZrDgLazqkY7MY`;9d_E)nYM9JQfFDyMW{s z!23Uj)rY5*OGC@66QskNa3xvHwioVnUYx$4u!k+U)_p0%oRKMRDh z12*}=0|j97y{alXe7?X=Jnr9D+yPb2_jOLr`@19W&W=w^Fvk>Aj_c_5)%$rt0t}5Ek%+*f2{`eBVxmi*~6kb))^Yj7w~ft^Y@X#3#Y8DtwF?8VTE`0qFum&3f<>+ zm>1O6)I{-cb2CL|>;ednPfr(4)HvfNE-qf4LL7etnqZ1>yv7Wm=Uk4!191p1YHDgw zLTEeSy-+&ct1bYXIF!EGpF@ul&`aF$-k>*-Ve~NS>a&0VY`8cj>g-&1*KFYjl2nRi zp3CNu7;f16&0SQPv*Cu?>B}c2CFKh0D=qMvRuDLVso3!QPJ!|U!qquMqxRmSdtww7 z^OOCCf&L3^hlQs6iEj`W9xDM$kfD|8Xr%T`~i3gPD*0L z>%*Jx&FtvU)s9a{;PF4)8uGdg;vbh0mxK_C$KIilv-0PUkk$Je)M#Ob$kEawaq4+- z3Ih0+R&IY{l)k@fUQntoqzh!6U^z%ekhAancJ88$0uK+*tDK68ISa^qcshK} z1QcXgGb<~r|K=JqBRdrP#W3{Izy6Vu0`Nu`H6kUlZ(LX(L3XipyqDb0+}X| zAJ+Z}n|%9N=g_AmWu_&~?{+qBrKWaUfQy1puc;})<_s&X4&>%VJKt(5{b8X`I zdeV>M2|mR#?{&Ls*kG6hNsIIHwx>k_4qD7X^9q*}2+Lfvo#MMYW9J>PST*?U^-3=8 z*-}7aVLMdl|brA4CK%#DmtrDKBra_q(7Zp!TF56UVPJiP@;^K{jQ6aGieW0hFpGlA4F< zQN{uw^o#6Q>m5 zk+_no)(uNaSYz3vvf25F{v0Pwly?9Q`e>%ruG2tzFGVwmlPR_=6Guf3;U9V)SC~9F zu1E$@o=Viq^VJ$k(EiS9L~gF*9H8r1acb^aY45cu`{&P}r=Y(ueB;3B8#>PjOD$cC zs`if_Ki=X19R%L22CxgdNr$#*{XLMyyg6vOB&?d6n)a=H7jyAou_B|R?Kyp&`g1fv z^-Pul88HxQ;)_bY=eW-1XrC3NiE3T_xG&Q3RSo7+w1@^9E9;|VLh6YI28~r~7LDGz z{gqm(D9ntoMWv48X-FvLH*yFujn8Jc^2JF!i_#%7u0cF*S7Yo2WLd zt*56BRT-~O5|X$@ezk6+@snkMt)qzRGfnGwh`yDavA=^G!VV&oVrh=z#w=b<=Z#oI$(o99|~&71*jE;I5};u z-oAai6Kd-zJv}|bY%Iu9Vjgqo>{jEu^L=$*RRl|tPwnmJ*MS@O<9w@XRwkf!92EeD z=ispw{~T3Z^gTAe=$q#;-y>Z`ebcHu%9KQ-0IhF%IXT{tS{YU3i*H&YD2G_DRlodbrLq}UQX<+O%OQG2Q*#|s*YXnvcKbc3d+jg<_N@aY4k}G@@WLCA zR8zR9s7shH!|pM`jB^H+WzlTZ2?f%24kaR|g5bqWb8~Z0pd1|>I6+3_j^|%~eBsA% z4<>S96^aeZ*d=ZQb0{wo5)w?2>=G9)bR1q-Z{4wO0aa|_6WY_E?^RNgkf?&@#T=O- z=lO@}r@L%8Oj=rphIVjixw0E(fl=Bne0`}7zj(qn8$7@F7N{bOPgFrcRMeAq65#aZ zMsE#ODbQQ3`r#WLyijHx@Q(n=0X;%iCnS=6{-{FL_jvT8@9(0j9}m$dg_z5%qGlGo ztEr{e)zma?=jOI92}oC%QeeRT)TvV_xTxYfo9)|oTMrUa)ivbQVPaZZw274u0SZ)r zHp~2S2?YgE@4zmr=1ydoO2bWxbLEwlBKD^-#wI%@%nDsPy%o2Ho>ZY)zqAgZAsbZj zOiN>NVd?h(APblx9KsQ}?U*XBD&~I0ow3R=h*uF=S!Vb(878)VYtZ679uHT7j5$DsHk8Y%eHia3jhw@xjV?UyXh?3b)VPHgt(>84fYqe?w&EW}66 zix;eC&YV$!D(EJRLoJO#M>ZhIBmp9O!Mb9U)eC4M0xG`o)vGC}OiN8`E5Ny@j;7@A znHla3shcz{Up>H2U_*>)y+!gp8GeBBEc6ARrGSJSE!vggqKtqk9A~&8P+3S=Sdxg6 zz3Xv%RDKg7CHsS}{(cFMy(|Ai6mLKE_ zVU3O$W}a;r6>}1z;fer^?u~}%$apqqo?TNJ7qId7Z!@sT>Pv6pr=r@8u@K!OSFc{x zVAOVCI6f89j51U5S*7}_?;>zZW&&arY4mfFfdAU01HxIGH3k^Uc z4wkHsAHw`xI@fUVGlq;2a{LM|F7CCPm7I=TVq_rfg5J3>@LKOQDGv3`5V)FVrjma@ z$fa0Pve1wawQh#oI#ehl{;vi%#gIeDEhl#xl%R(iXSCA9Y*nRbxw!A7z&G#V2etQ? z7_wja_=w{t@ai@P;o!BRZ`29N4T?#gOAm(bB1wj~KA@+3Q6x#*+UHlIi@(3Ws2DYO zKZCXPBxwoAyH)NrmV59DaVor*%y_PqLGWHOkZYpMHgE4NFYB2x$50*V@im)A}Ru%@HXC@KScvpVl}J`nhb zVgX5j!-UX|Q_3FquEA$RmCFv(&0$}V$Otm#W-+o>kTpS6{tBG>-iizc^Y{}ev^k(z zEP~36MlUDf%u%w+gTNRRB`)ylroLA#ClFwUcKPIRe@BgzN@HSTLR6BP+xelLs~P-t zXLXVi?WJU)PSO1*i0*WFNJLWKt4t=q%_!T4Zp#!N45UaM6vs0(pMFE17=a?3s){11 z;o;#pbY#)n8yn0N37CfsbXLO(^NIMIOn~dZTH_NE7JsXwRSBO>bNPsaEW!??cS}X) zzZq|sgO(T4sdA&8I8_`EIylawCr{Xw!6i`!h_6MA3aS-o3}_{RVfEo7SSh1Z@QoH^ zxg!<}%13Z86ETQ;WPm_`qbg>g3yssyswEMi5ZFQf2?Q1#1bjtB*6Cxa4akQ(wCohzu2VSs0V9}9H&a;pb!__2&k4YcD39qQwq6ZIE6%fCW*NjUIzkfT^F)+-0H?amiClGP3zJ!ZRK%pE9mRZ$VX_9A0spykUxdz`Tl$RIL^JBL zk5A1nfJ&ysWEd+ec5oArgsI}l%hzrK&2a00dDZy>lakasSZq?UK7Z-`rE5Qb{Sv3c zo0^&m0uxMDUETQ8lNAA-`XnwlDzlf*;dTgGfnoj~Z3LGHfuXe+l>B}*Xa@|? zC(_7N4w{HK#HjG}t*oAaemey;AAn+c?1J4DAxLKj5|MnZidlBB39c!`^-;MJeQLCVpS}k(VlPc%4!MTK159w-vifK9Wi)yx!w~rQ}h)uv9sqVwCRlF0!RnFO|=9N z9Jw905J3)KeeSSNZq=7XiTy3SgQ4zNfK?$t*e-sP(*G+l-0SFuA|{ z+w&LtlY)W*^aUryg`}G)1aMYITStc+9!mh0&$P~} zi6ek_DFD9wNx=u2B-R}j0#YorV-&K&{3{+M*}%oS(8}%tVv?i@+y@+d(U=T8_9wJM zr0mHu+`9}_U;Q#4}1*-6$`XLR-ogSCoT|G%$^`GuTv$|BzgFGb8|COB+l0t z?yRRzpYD{p$1z05#O&d*@dp^@>x2ZKjKq%++$`KYJX}y%pdRgmTt7ew`b$4H?Sjkc ze=D`CMOXRh(y?91!7u}mq*I(@Aw`^$}YZM%l_%}XWyRh-@n^|dIwa2?2;Zivjje>I-azJ z1rd0rsN;iqe87WAPiP56Lq@T0QF92p=7=0b1|y##%E9Eyg! zFU%Xg;gvug`(qTP?5X>8UkHdjCo<=(764kMP^fbZn)5BsnK+n5eg?N-$=>FG9uH1X zLj%U4vBUxif?9H;{q0q-($JBRA~XN(TR4l%5PrZ|fDA>rEkVi;J zheQQna{}zL!TMQ>4*uVK|NTTeNo1n%T7kiSAO;g|MJ9Dz8%X{K)e3;l2-OEZcF^!u zl_czo(bU*k+vdV!WUJr**Vs7pkzLFtNQRsHVerC5X4rE)=vO(272zO6QWk+*$hj4>*vZiOJ9(XiBtUs2DK~x~( zloN!ZAy6_|Kv%g47&qSESsE#14yC`)C2JvmX01^ zQ^wPbj32MI=T|2i0`FbA6qd$yFd+0f9edkOts*|OTT zDGBhNzL8QRV4@zH1(EJItvi|>;Blm1YWX2Ftr3$mVHzqzv!`ol#^Skb>T&DvQDh!qj{%rt7 z%(b#EFcvhhw2T3{-PqU|1~iSxB|TJq~D8;~>y>VoO88ryFxdp=#LG!yqC5w(`K!S?< zl<-)ezfb`EmGsCrbmvP^JY1oC9#wVfq(8X=i#Oc|ScAo4y{v3^G0CCC1awFoL;xWUYu18EY%OgpZFeL;%?m zAOROF0Rnn?%fLYSwh9&eHB5)s&*uCYP<2ScW`b9Z_6N@p5k+L1l{*#9-F`T|*!QaA zc!d!!dY^-)_k}DiXy`UFa?N97EMSrPG#Fx|%}6$|{5sTD+UQAa;Ri zSUNj98(2e5YHZ+UpL%AIX&Lulm_rn0`frcO>u5C)9iO{aXqsg#DC`_~Fl~mYtE+?H z!>p%#Cr4Lc6oVp5v|;hw*O5h8VeABX9i z|53PBc>s<|w_G@CGJz}jRSRjrDBZHCqwYTz)~tY3N$fTeWDX@7uF@P}0iirNI1uw* zv+3cKiiZ*6)aS%=DZp&5qxyjKQ#eHO;dp=Y_=tm%>4FGwJrMg*I$tbng22$RPsv}Q zC-M1nGxRk|!|)3~9qk5~WQ{7z%K9|E(h91rm4|s=C}iHZ@#Eg6hUr9tZn-cph)=^v z2-Y*uK@&*Cl^L=uE2Z|Lqn(E1$M4I^L||f36zgPRG}Y@t35wnNOtfe-j2dmFX}TZI z9v`j-9<$53JII}EbCzscP!)yrlZAyF;N1OAhjZ>+!6P6ty{X_aq|{MACIH!N4+btB z#@DWS5(YJe5Ve3kq`0^kbOg%DOHzgq%&=T)FjPT}Gk=D~k)W`!OM_5z>O=vPoj2+a zgwgFQRaI4P)CeR^&;%Vg`WZ`9Rn>)FAsYxae|?|x!Gmnd0j#hF?a=;nu3WkD4@J-= zT&5^mm<((wx`UmDcrg zGa;xVO~8o`zN-XtddO1!Jj_RFFvM z+}P4&6F;1c85gm11$1p5s6y~C4G0h2{@&m1y70A)jRINO?=3C%ldvIHPJ-b#r<-FZ z$w)~v3-tNrYCn9i1F!P9#IpXegECwQuMUNKI%v>o7u#AKnBRZ@{=Il( zCg0=9%&U36S{fRSfOfn|<0-^W+>l~az_8DWUjMM!1*aue8l`^oiHcI5GbzS^CG#D0 z)7Ky&Hvs?n8f#v4*Qn_pSuY%AP#3oTXm=49`~?>mm#$1@j`26q8PCA91`Y~WV0q&( z;=XD=`tM4SdBum366=NnosKny;{y*Oa9?SG?pEkB`LXDO_j+<17lwfFt z(YS{Jc#q|kl^a@ga{jsIHO8^)h=>&@0bZPq7X6qnJN>P-H3z&69$xP5e~piNlEv#TTstE{sFUZG71k#VniEnk%Z?(Px`oE)0g1F^s{mzf^l8<}NQChk0 zaE}z44nioE8eyu(e1*-msqC(pEl@Rds*W_mVFspld+>eCqA%&-2o&lDXW;0_NG=h+ z#x?NQb@M{05Tuo`gp3%)rmCVMKS(?8sNx1?<>h(A#HgTO!f+kEJwE{HO7;@rwqJz^Nz!=W=pMgVR!>d=Xc9*Lr!@;gb3gZose}OxIJ%}7+ zRCJmulYzAg{$a-paL2Z6{BB8eUBViE$j zjZ*3sVeQ9%9-sO@Ci7C^%!Lb#Aa;^qNm8k?BxH>&E_G#Oh?j!54i^ajBdQFzvoO+u z>SnJ75V#GM47swLW`F;F1S|`z%_i_5Ea)pz9<4{h^tk@^$h$Bkr^3ZP_xLq)%Z+xa zZ<{hkHpG*MWtCtwi$@m%$IRJsIE{3V^(xR zB`Gd$unaz>3u!D-R|}f)AhlTtGaemijEA8!8+{VMGm`6vMmUQ~kZ>|^h;MXsZwRO| zg#&lHEzrzp=*kN8K`_H&gp(-qr`mFifc%4pBR(z;LHc3Nb!P->tRb&GD@k141 z`~yYx3?H9locR-W#YdFN2XVPsDL||g8Nu#PlP}BrV#T7|i5#e=jh)?72HJ8jR@(nO z%mCd(m|WO``Xhv&;{j%eGSG0qvPX`z!YyUU1sv>(!3WvdZ9ge5E!AKk34^?r{QLJW z(`WzGvKXh^;AS?aL_poB_aH;UIP426=-a17|#?Qw9dHlU5(@E%G7nU^5>A0G4=WP6(F8dmEr#eq%l0zkU%E{eb0!y;80sdDo2}?q% zz60m1jMj#mQt#teRM3`!gX4bq0ugir2ld_b+n8`l4)*)&GnZmPEAe*qZ^A42Woh!2 zOvWgy1D%*|!Ni+hXQnqj96AN)8bqLb&_IL=;)50rE5397_Auh*<<-N)6yMs~$_d9~ z!O0L(RaNCgdgm3^(edg!OiqfhG_cA95Svpo1mDE$0$ZaNdQIy+$sCR2RYD+ zo07sBOHc4)9u*v4edMDj!u$wU(78uq4Lr@J(ja79(rI&re1}QWE}W3X0+@$O@bK|< zEO;=B+l7unJHKpQ^VGH*drBKevAy`ZwH`j_5$KXJO3^T$BSmrmt`LDMYs_SCXEpP+ z?A;8qa;n=EDUOjLS~;$pW!pYMg?uZy)CSX_uz ze2=BO^CvjQKV3E7jPk%(`-BzesUkE z&s~643hl8MR@iE_w6xFzT!@OH;nQ9eBS~d4ho3uh=4S}%7*17fWDD>73bW%ux|1KE z9vU(FmaUCmW##l|&wk<|=wS*?%_xI;@qhzwD9=7?DNTkd_krj;d<#4V(TxqfoS`Wx z%*a$2qg8b~Hq15^H6#O{GDBM7!LVGMuLKF<8zRIF%&bBX2O{0K!1T|I@;10YI^>=} zR$%3gzU-R_$lf;JWWn+YmWd`f#|*e$LP?U$XqovTKm|Ln!jXl=e1`5P?zLhG)=a`) zsM}6(1Q~`L@UB-qs*>$}F+Q-??5I#e3h^CtbQ?}BB1nF5aXUQM@i|xlGh*jns@;G8 z#OWloz-CI>#Z=0~lm~fzH1zxTZz8MNn3U+~Xm7Z}xb8!A-yj}iltMtmLv0!_^-%|^ z=7VcY&k0Bwy1vrkf?Y~c#-56rTKrbiLjncDp+?ZSIW>W=gS`lbbgWCRg)N3Eu@_)2 z@~XG-^Jfw`Z4IN(r`tP~`4aMA-vgd%!F#ibV zkE)6BaqnEj=I-4mpgh6x?YGI{w!jOUV$je5L=Y$rfB|K({NO>fZN>*AF#}9P^zx-k zx^qRC@D@;`u3o=R4!kiO2R#RH0GfV%_Zd@?1?mjH71?mtOiK}UFztzoooeC7PqheV z5V4Fr={Hn|A@H?fjx_}M>!un5UdvY?8KmwtFD|-voh&4n@xu`tP=SyjIPym)L4LRm z2OET-*h6=9HxM^qBj-@6o4b~cUZ4d%4a;oQ=aM=k)qRV#LxFXplMH<1`XPZa)`Q1vgp~sUxJ@29Qu-~j%?CD4c5nOZ zKK32Q`j~pNWU3ThlO*u>DFwp?dLBU?gApS;_$TrRq!ciLy#>io2Xm|gJhBgk@Tg50 z)r(PbL6=PkT}TFWVbuGT4j#JaCp1@Z@1MB~7Rv`M2^`l59*)?5+|270AInGuc)p6( zRjnMe$+#lh+kq?XJ4+=E?&}p947l?0@^1S2RIuN)r)69@Vcu;6SAhby@eQhusGZ>6 zr44hCpn6ps%k&Z??eX`1{YtWSA`U#;)7^cCQsp!MW&sr_AkjqRr(31*254q7fB zsFjWRaQanudu?s1mMD-%ShyRnQHl4ZANUZJ=fGv%`}vo<1ycRs-#hu023pT!hilH< zpe68&imG3_L;&Z0PD||`%YyryEjE0esk*T?PbALY#G(50cP2u(7!|}Ul_()`j zQf3J#m>e=ZGLi^~(06peu2qusVc-06?9}u$*AqC!&}G(CFcKa#mp>nc&p{mv4|xkNf)# z;ixDsqNAgO{@V){78a%!78^Lu7@)y$80V6w=XNJ5%%#Qx;*j4!|NRE=I6R2?rBVf) zT{BdbD`YoKTptd3gxtW4`)gRU^9N7O!3?4m<|+gIE9eG^cuODihE9zP}r z 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 E58~svmjD0& literal 0 HcmV?d00001 diff --git a/pcsx2-qt/resources/resources.qrc b/pcsx2-qt/resources/resources.qrc index 3b2aeab90c..d68738265e 100644 --- a/pcsx2-qt/resources/resources.qrc +++ b/pcsx2-qt/resources/resources.qrc @@ -50,6 +50,7 @@ icons/black/svg/shut-down-line.svg icons/black/svg/trophy-line.svg icons/black/svg/tv-2-line.svg + icons/black/svg/usb-fill.svg icons/black/svg/volume-up-line.svg icons/black/svg/window-2-line.svg icons/discord.png @@ -107,8 +108,11 @@ icons/white/svg/shut-down-line.svg icons/white/svg/trophy-line.svg icons/white/svg/tv-2-line.svg + icons/white/svg/usb-fill.svg icons/white/svg/volume-up-line.svg icons/white/svg/window-2-line.svg + images/DrivingForce.png + images/GTForce.png images/dualshock-2.png diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 052b9a71b7..737acad5d4 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -439,91 +439,77 @@ endif() # USBNull set(pcsx2USBNullSources USB/USBNull.cpp) -set(pcsx2USBNullHeaders USB/USB.h) +set(pcsx2USBNullHeaders USB/USBNull.h) # USB sources set(pcsx2USBSources USB/USB.cpp USB/deviceproxy.cpp - USB/configuration.cpp - USB/device_init.cpp - USB/qemu-usb/glib.cpp - USB/qemu-usb/vl.cpp - USB/qemu-usb/iov.cpp - USB/qemu-usb/desc.cpp - USB/qemu-usb/core.cpp USB/qemu-usb/bus.cpp - USB/qemu-usb/usb-ohci.cpp + USB/qemu-usb/core.cpp + USB/qemu-usb/desc.cpp USB/qemu-usb/hid.cpp USB/qemu-usb/input-keymap-qcode-to-qnum.cpp - USB/usb-msd/usb-msd.cpp - USB/usb-pad/usb-pad.cpp - USB/usb-pad/usb-pad-ff.cpp - USB/usb-pad/lg/lg_ff.cpp - USB/usb-pad/usb-seamic.cpp - USB/usb-mic/usb-mic-singstar.cpp - USB/usb-mic/usb-mic-logitech.cpp - USB/usb-mic/usb-headset.cpp + USB/qemu-usb/iov.cpp + USB/qemu-usb/usb-ohci.cpp + USB/shared/ringbuffer.cpp USB/usb-eyetoy/jo_mpeg.cpp USB/usb-eyetoy/usb-eyetoy-webcam.cpp USB/usb-hid/usb-hid.cpp - USB/shared/shared_usb.cpp - USB/shared/inifile_usb.cpp - USB/shared/ringbuffer.cpp + USB/usb-lightgun/guncon2.cpp + USB/usb-mic/usb-headset.cpp + USB/usb-mic/usb-mic-logitech.cpp + USB/usb-mic/usb-mic-singstar.cpp + USB/usb-msd/usb-msd.cpp + USB/usb-pad/lg/lg_ff.cpp + USB/usb-pad/usb-pad-ff.cpp + USB/usb-pad/usb-pad.cpp + USB/usb-pad/usb-seamic.cpp USB/usb-printer/usb-printer.cpp - ) +) # USB headers set(pcsx2USBHeaders USB/USB.h - USB/proxybase.h USB/deviceproxy.h - USB/configuration.h - USB/platcompat.h - USB/helpers.h - USB/readerwriterqueue/readerwriterqueue.h - USB/readerwriterqueue/atomicops.h - USB/qemu-usb/glib.h - USB/qemu-usb/vl.h - USB/qemu-usb/qusb.h USB/qemu-usb/USBinternal.h USB/qemu-usb/desc.h - USB/qemu-usb/iov.h - USB/qemu-usb/queue.h USB/qemu-usb/hid.h USB/qemu-usb/input-keymap.h - USB/usb-msd/usb-msd.h - USB/usb-pad/usb-pad.h - USB/usb-pad/padproxy.h - USB/usb-pad/lg/lg_ff.h - USB/usb-mic/audio.h - USB/usb-mic/audiodev.h - USB/usb-mic/audiodeviceproxy.h - USB/usb-mic/usb-mic-singstar.h - USB/usb-mic/usb-headset.h - USB/usb-mic/audiodev-noop.h - ../3rdparty/jpgd/jpgd.h - ../3rdparty/jpgd/jpge.h - USB/usb-eyetoy/jo_mpeg.h - USB/usb-eyetoy/videodeviceproxy.h - USB/usb-eyetoy/videodev.h - USB/usb-eyetoy/usb-eyetoy-webcam.h - USB/usb-eyetoy/ov519.h - USB/usb-hid/hidproxy.h - USB/usb-hid/usb-hid.h - USB/usb-hid/noop.h - USB/shared/shared_usb.h - USB/shared/inifile_usb.h + USB/qemu-usb/iov.h + USB/qemu-usb/queue.h + USB/qemu-usb/qusb.h + USB/readerwriterqueue/atomicops.h + USB/readerwriterqueue/readerwriterqueue.h USB/shared/ringbuffer.h + USB/usb-eyetoy/jo_mpeg.h + USB/usb-eyetoy/ov519.h + USB/usb-eyetoy/usb-eyetoy-webcam.h + USB/usb-eyetoy/videodev.h + USB/usb-hid/usb-hid.h + USB/usb-lightgun/guncon2.h + USB/usb-mic/audio.h + USB/usb-mic/audiodev-noop.h + USB/usb-mic/audiodev.h + USB/usb-mic/usb-headset.h + USB/usb-mic/usb-mic-singstar.h + USB/usb-msd/usb-msd.h + USB/usb-pad/lg/lg_ff.h + USB/usb-pad/usb-pad.h USB/usb-printer/usb-printer.h - ) +) -if(TARGET PulseAudio::PulseAudio) - list(APPEND pcsx2USBSources USB/usb-mic/audiodev-pulse.cpp) - list(APPEND pcsx2USBHeaders USB/usb-mic/audiodev-pulse.h) - target_link_libraries(PCSX2_FLAGS INTERFACE PulseAudio::PulseAudio) +if(CUBEB_API) + list(APPEND pcsx2USBSources USB/usb-mic/audiodev-cubeb.cpp) + list(APPEND pcsx2USBHeaders USB/usb-mic/audiodev-cubeb.h) endif() +if(TARGET SDL2::SDL2 OR TARGET SDL2::SDL2-static) + list(APPEND pcsx2USBSources USB/usb-pad/usb-pad-sdl-ff.cpp) + list(APPEND pcsx2USBHeaders USB/usb-pad/usb-pad-sdl-ff.h) +endif() + + if(PCSX2_CORE) # Host PAD set(pcsx2PADSources @@ -887,36 +873,10 @@ if(WIN32) ) list(APPEND pcsx2USBSources - USB/qemu-usb/input-keymap-win32-to-qcode.cpp - USB/shared/hidapi.cpp - USB/shared/rawinput_usb.cpp - USB/usb-eyetoy/api_init_win32_eyetoy.cpp USB/usb-eyetoy/cam-windows.cpp - USB/usb-hid/api_init_win32_hid.cpp - USB/usb-hid/raw/rawinput.cpp - USB/usb-mic/api_init_win32_mic.cpp - USB/usb-mic/audiodev-wasapi.cpp - USB/usb-msd/usb-msd-win32.cpp - USB/usb-pad/api_init_win32_pad.cpp - USB/usb-pad/dx/dinput-config.cpp - USB/usb-pad/dx/dinput.cpp - USB/usb-pad/dx/usb-pad-dx.cpp - USB/usb-pad/raw/raw-config.cpp - USB/usb-pad/raw/usb-pad-raw.cpp - USB/Win32/Config_usb.cpp ) list(APPEND pcsx2USBHeaders - USB/qemu-usb/input-keymap-win32-to-qcode.h - USB/shared/rawinput_usb.h USB/usb-eyetoy/cam-windows.h - USB/usb-mic/audiodev-wasapi.h - USB/usb-pad/dx/dx.h - USB/usb-pad/dx/usb-pad-dx.h - USB/usb-pad/dx/versionproxy.h - USB/usb-pad/raw/raw-config-res.h - USB/usb-pad/raw/usb-pad-raw.h - USB/Win32/Config_usb.h - USB/Win32/resource_usb.h ) list(APPEND pcsx2GSSources @@ -936,39 +896,16 @@ if(WIN32) GS/Renderers/DX12/GSDevice12.h GS/Renderers/DX12/GSTexture12.h ) -else() +elseif(Linux) list(APPEND pcsx2USBSources - USB/icon_buzz_24.cpp - USB/linux/config-gtk.cpp - USB/linux/config.cpp - USB/linux/util.cpp - USB/qemu-usb/input-keymap-linux-to-qcode.cpp - USB/usb-eyetoy/api_init_linux.cpp USB/usb-eyetoy/cam-linux.cpp - USB/usb-hid/api_init_linux.cpp - USB/usb-hid/evdev/evdev-gtk.cpp - USB/usb-mic/api_init_linux.cpp - USB/usb-msd/usb-msd-gtk.cpp - USB/usb-pad/api_init_linux.cpp - USB/usb-pad/evdev/evdev-ff.cpp - USB/usb-pad/evdev/evdev-gtk.cpp - USB/usb-pad/evdev/evdev.cpp - USB/usb-pad/evdev/shared-gtk.cpp ) list(APPEND pcsx2USBHeaders - USB/gtk.h - USB/icon_buzz_24.h - USB/linux/actualfile.h - USB/linux/config.h - USB/linux/ini.h - USB/linux/util.h - USB/qemu-usb/input-keymap-linux-to-qcode.h USB/usb-eyetoy/cam-linux.h - USB/usb-hid/evdev/evdev.cpp - USB/usb-hid/evdev/evdev.h - USB/usb-pad/evdev/evdev-ff.h - USB/usb-pad/evdev/evdev.h - USB/usb-pad/evdev/shared.h + ) +else() + list(APPEND pcsx2USBSources + USB/usb-eyetoy/cam-noop.cpp ) endif() @@ -1654,6 +1591,13 @@ if(Windows) ${pcsx2WindowsHeaders}) endif() +if(PCSX2_CORE) + target_sources(PCSX2 PRIVATE ${pcsx2USBSources} ${pcsx2USBHeaders}) + target_link_libraries(PCSX2_FLAGS INTERFACE jpgd) +else() + target_sources(PCSX2 PRIVATE ${pcsx2USBNullSources} ${pcsx2USBNullHeaders}) +endif() + # MacOSX/BSD if(UNIX AND NOT Linux) if(APPLE) @@ -1664,23 +1608,7 @@ if(UNIX AND NOT Linux) ${pcsx2FreeBSDSources}) endif() target_sources(PCSX2 PRIVATE - ${pcsx2LinuxHeaders} - ${pcsx2USBNullSources} - ${pcsx2USBNullHeaders}) -else() - if(NOT PCSX2_CORE) - target_sources(PCSX2 PRIVATE - ${pcsx2USBSources} - ${pcsx2USBHeaders}) - target_link_libraries(PCSX2_FLAGS INTERFACE - jpgd - PkgConfig::SAMPLERATE - ) - else() - target_sources(PCSX2 PRIVATE - ${pcsx2USBNullSources} - ${pcsx2USBNullHeaders}) - endif() + ${pcsx2LinuxHeaders}) endif() target_link_libraries(PCSX2_FLAGS INTERFACE @@ -1872,8 +1800,6 @@ function(setup_main_executable target) ${PCSX2_SOURCE_DIR}/GS/GS.rc ${PCSX2_SOURCE_DIR}/PAD/Windows/PAD.rc ${PCSX2_SOURCE_DIR}/SPU2/Windows/SPU2.rc - ${PCSX2_SOURCE_DIR}/USB/usb-pad/dx/versionproxy.rc - ${PCSX2_SOURCE_DIR}/USB/usb-pad/raw/raw-config.rc ) endif() target_sources(${target} PRIVATE diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 82c0ef1bea..1cf171145b 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -79,7 +79,10 @@ struct InputBindingInfo Axis, HalfAxis, Motor, - Macro + Pointer, // Receive relative mouse movement events, bind_index is offset by the axis. + Keyboard, // Receive host key events, bind_index is offset by the key code. + Device, // Used for special-purpose device selection, e.g. force feedback. + Macro, }; const char* name; @@ -1109,6 +1112,35 @@ struct Pcsx2Config } }; +#ifdef PCSX2_CORE + // ------------------------------------------------------------------------ + struct USBOptions + { + enum : u32 + { + NUM_PORTS = 2 + }; + + struct Port + { + s32 DeviceType; + u32 DeviceSubtype; + + bool operator==(const USBOptions::Port& right) const; + bool operator!=(const USBOptions::Port& right) const; + }; + + std::array Ports; + + USBOptions(); + void LoadSave(SettingsWrapper& wrap); + + bool operator==(const USBOptions& right) const; + bool operator!=(const USBOptions& right) const; + }; +#endif + + // ------------------------------------------------------------------------ // Options struct for each memory card. // @@ -1203,6 +1235,9 @@ struct Pcsx2Config FramerateOptions Framerate; SPU2Options SPU2; DEV9Options DEV9; +#ifdef PCSX2_CORE + USBOptions USB; +#endif TraceLogFilters Trace; diff --git a/pcsx2/Frontend/DInputSource.cpp b/pcsx2/Frontend/DInputSource.cpp index 01156fb32a..e71da9fe6d 100644 --- a/pcsx2/Frontend/DInputSource.cpp +++ b/pcsx2/Frontend/DInputSource.cpp @@ -155,7 +155,7 @@ bool DInputSource::ReloadDevices() { const u32 index = static_cast(m_controllers.size()); m_controllers.push_back(std::move(cd)); - Host::OnInputDeviceConnected(GetDeviceIdentifier(index), name); + InputManager::OnInputDeviceConnected(GetDeviceIdentifier(index), name); changed = true; } } @@ -167,7 +167,7 @@ void DInputSource::Shutdown() { while (!m_controllers.empty()) { - Host::OnInputDeviceDisconnected(GetDeviceIdentifier(static_cast(m_controllers.size() - 1))); + InputManager::OnInputDeviceDisconnected(GetDeviceIdentifier(static_cast(m_controllers.size() - 1))); m_controllers.pop_back(); } } @@ -268,7 +268,7 @@ void DInputSource::PollEvents() if (hr != DI_OK) { - Host::OnInputDeviceDisconnected(GetDeviceIdentifier(static_cast(i))); + InputManager::OnInputDeviceDisconnected(GetDeviceIdentifier(static_cast(i))); m_controllers.erase(m_controllers.begin() + i); continue; } diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 957f917760..f0e4d336e3 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -1301,7 +1301,7 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T // if this key is in our new binding list, it's a "release", and we're done SettingsInterface* bsi = GetEditingSettingsInterface(game_settings); const std::string new_binding(InputManager::ConvertInputBindingKeysToString( - s_input_binding_new_bindings.data(), s_input_binding_new_bindings.size())); + s_input_binding_type, s_input_binding_new_bindings.data(), s_input_binding_new_bindings.size())); bsi->SetStringValue(s_input_binding_section.c_str(), s_input_binding_key.c_str(), new_binding.c_str()); SetSettingsChanged(bsi); ClearInputBindingVariables(); diff --git a/pcsx2/Frontend/ImGuiOverlays.cpp b/pcsx2/Frontend/ImGuiOverlays.cpp index 3fa6359c48..c86ec633c5 100644 --- a/pcsx2/Frontend/ImGuiOverlays.cpp +++ b/pcsx2/Frontend/ImGuiOverlays.cpp @@ -46,6 +46,7 @@ #ifdef PCSX2_CORE #include "PAD/Host/PAD.h" #include "PAD/Host/KeyStatus.h" +#include "USB/USB.h" #include "Frontend/FullscreenUI.h" #include "Frontend/ImGuiManager.h" #include "Frontend/ImGuiFullscreen.h" @@ -482,6 +483,12 @@ void ImGuiManager::DrawInputsOverlay() num_ports++; } + for (u32 port = 0; port < USB::NUM_PORTS; port++) + { + if (EmuConfig.USB.Ports[port].DeviceType >= 0 && !USB::GetDeviceBindings(port).empty()) + num_ports++; + } + float current_x = margin; float current_y = display_size.y - margin - ((static_cast(num_ports) * (font->FontSize + spacing)) - spacing); @@ -544,6 +551,59 @@ void ImGuiManager::DrawInputsOverlay() current_y += font->FontSize + spacing; } + + for (u32 port = 0; port < USB::NUM_PORTS; port++) + { + if (EmuConfig.USB.Ports[port].DeviceType < 0) + continue; + + const gsl::span bindings(USB::GetDeviceBindings(port)); + if (bindings.empty()) + continue; + + text.clear(); + fmt::format_to(std::back_inserter(text), "USB{} |", port + 1u); + + for (const InputBindingInfo& bi : bindings) + { + switch (bi.bind_type) + { + case InputBindingInfo::Type::Axis: + case InputBindingInfo::Type::HalfAxis: + { + // axes are always shown + const float value = static_cast(USB::GetDeviceBindValue(port, bi.bind_index)); + if (value >= (254.0f / 255.0f)) + fmt::format_to(std::back_inserter(text), " {}", bi.name); + else if (value > (1.0f / 255.0f)) + fmt::format_to(std::back_inserter(text), " {}: {:.2f}", bi.name, value); + } + break; + + case InputBindingInfo::Type::Button: + { + // buttons only shown when active + const float value = static_cast(USB::GetDeviceBindValue(port, bi.bind_index)); + if (value >= 0.5f) + fmt::format_to(std::back_inserter(text), " {}", bi.name); + } + break; + + case InputBindingInfo::Type::Motor: + case InputBindingInfo::Type::Macro: + case InputBindingInfo::Type::Unknown: + default: + break; + } + } + + dl->AddText(font, font->FontSize, ImVec2(current_x + shadow_offset, current_y + shadow_offset), shadow_color, text.c_str(), + text.c_str() + text.length(), 0.0f, &clip_rect); + dl->AddText( + font, font->FontSize, ImVec2(current_x, current_y), text_color, text.c_str(), text.c_str() + text.length(), 0.0f, &clip_rect); + + current_y += font->FontSize + spacing; + } } #endif diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index 7769d3b40a..4745e2f57b 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -18,6 +18,7 @@ #include "Frontend/InputSource.h" #include "Frontend/ImGuiManager.h" #include "PAD/Host/PAD.h" +#include "USB/USB.h" #include "common/StringUtil.h" #include "common/Timer.h" #include "VMManager.h" @@ -107,11 +108,13 @@ namespace InputManager static void AddHotkeyBindings(SettingsInterface& si); static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type); + static void AddUSBBindings(SettingsInterface& si, u32 port); static void UpdateContinuedVibration(); static void GenerateRelativeMouseEvents(); static bool DoEventHook(InputBindingKey key, float value); static bool PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key); + static bool ProcessEvent(InputBindingKey key, float value, bool skip_button_handlers); template static void UpdateInputSourceState(SettingsInterface& si, std::unique_lock& settings_lock, InputSourceType type); @@ -158,6 +161,11 @@ static std::array(InputPointerAxis: s_pointer_state; static std::array(InputPointerAxis::Count)> s_pointer_axis_scale; +using PointerMoveCallback = std::function; +using KeyboardEventCallback = std::function; +static std::vector s_keyboard_event_callbacks; +static std::vector> s_pointer_move_callbacks; + // ------------------------------------------------------------------------ // Binding Parsing // ------------------------------------------------------------------------ @@ -260,43 +268,75 @@ bool InputManager::ParseBindingAndGetSource(const std::string_view& binding, Inp return false; } -std::string InputManager::ConvertInputBindingKeyToString(InputBindingKey key) +std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key) { - if (key.source_type == InputSourceType::Keyboard) + if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device) { - const std::optional str(ConvertHostKeyboardCodeToString(key.data)); - if (str.has_value() && !str->empty()) - return fmt::format("Keyboard/{}", str->c_str()); - } - else if (key.source_type == InputSourceType::Pointer) - { - if (key.source_subtype == InputSubclass::PointerButton) + // pointer and device bindings don't have a data part + if (key.source_type == InputSourceType::Keyboard) { - if (key.data < s_pointer_button_names.size()) - return fmt::format("Pointer-{}/{}", u32{key.source_index}, s_pointer_button_names[key.data]); - else - return fmt::format("Pointer-{}/Button{}", u32{key.source_index}, key.data); + return "Keyboard"; } - else if (key.source_subtype == InputSubclass::PointerAxis) + else if (key.source_type == InputSourceType::Pointer) { - return fmt::format("Pointer-{}/{}{:c}", u32{key.source_index}, s_pointer_axis_names[key.data], - key.modifier == InputModifier::Negate ? '-' : '+'); + return GetPointerDeviceName(key.data); + } + else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast(key.source_type)]) + { + // This assumes that it always follows the Type/Binding form. + std::string keystr(s_input_sources[static_cast(key.source_type)]->ConvertKeyToString(key)); + std::string::size_type pos = keystr.find('/'); + if (pos != std::string::npos) + keystr.erase(pos); + return keystr; } } - else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast(key.source_type)]) + else { - return s_input_sources[static_cast(key.source_type)]->ConvertKeyToString(key); + if (key.source_type == InputSourceType::Keyboard) + { + const std::optional str(ConvertHostKeyboardCodeToString(key.data)); + if (str.has_value() && !str->empty()) + return fmt::format("Keyboard/{}", str->c_str()); + } + else if (key.source_type == InputSourceType::Pointer) + { + if (key.source_subtype == InputSubclass::PointerButton) + { + if (key.data < s_pointer_button_names.size()) + return fmt::format("Pointer-{}/{}", u32{key.source_index}, s_pointer_button_names[key.data]); + else + return fmt::format("Pointer-{}/Button{}", u32{key.source_index}, key.data); + } + else if (key.source_subtype == InputSubclass::PointerAxis) + { + return fmt::format("Pointer-{}/{}{:c}", u32{key.source_index}, s_pointer_axis_names[key.data], + key.modifier == InputModifier::Negate ? '-' : '+'); + } + } + else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast(key.source_type)]) + { + return s_input_sources[static_cast(key.source_type)]->ConvertKeyToString(key); + } } return {}; } -std::string InputManager::ConvertInputBindingKeysToString(const InputBindingKey* keys, size_t num_keys) +std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys) { + // can't have a chord of devices/pointers + if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::Device) + { + // so only take the first + if (num_keys > 0) + return ConvertInputBindingKeyToString(binding_type, keys[0]); + } + std::stringstream ss; for (size_t i = 0; i < num_keys; i++) { - const std::string keystr(ConvertInputBindingKeyToString(keys[i])); + const std::string keystr(ConvertInputBindingKeyToString(binding_type, keys[i])); if (keystr.empty()) return std::string(); @@ -524,6 +564,23 @@ std::optional InputManager::ParsePointerKey(const std::string_v return std::nullopt; } +std::optional InputManager::GetIndexFromPointerBinding(const std::string_view& source) +{ + if (!StringUtil::StartsWith(source, "Pointer-")) + return std::nullopt; + + const std::optional pointer_index = StringUtil::FromChars(source.substr(8)); + if (!pointer_index.has_value() || pointer_index.value() < 0) + return std::nullopt; + + return static_cast(pointer_index.value()); +} + +std::string InputManager::GetPointerDeviceName(u32 pointer_index) +{ + return fmt::format("Pointer-{}", pointer_index); +} + // ------------------------------------------------------------------------ // Binding Enumeration // ------------------------------------------------------------------------ @@ -561,19 +618,35 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch if (type.empty() || type == "None") return; - const std::vector bind_names = PAD::GetControllerBinds(type); - if (!bind_names.empty()) + const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(type); + if (!cinfo) + return; + + for (u32 i = 0; i < cinfo->num_bindings; i++) { - for (u32 bind_index = 0; bind_index < static_cast(bind_names.size()); bind_index++) + const InputBindingInfo& bi = cinfo->bindings[i]; + + switch (bi.bind_type) { - const std::string& bind_name = bind_names[bind_index]; - const std::vector bindings(si.GetStringList(section.c_str(), bind_name.c_str())); - if (!bindings.empty()) + case InputBindingInfo::Type::Button: + case InputBindingInfo::Type::Axis: + case InputBindingInfo::Type::HalfAxis: { - // we use axes for all pad bindings to simplify things, and because they are pressure sensitive - AddBindings(bindings, - InputAxisEventHandler{[pad_index, bind_index](float value) { PAD::SetControllerState(pad_index, bind_index, value); }}); + const std::vector bindings(si.GetStringList(section.c_str(), bi.name)); + if (!bindings.empty()) + { + // we use axes for all pad bindings to simplify things, and because they are pressure sensitive + AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index](float value) { + PAD::SetControllerState(pad_index, bind_index, value); + }}); + } } + break; + + // TODO: Move vibration motors in here. + + default: + break; } } @@ -589,14 +662,13 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch } } - const PAD::VibrationCapabilities vibcaps = PAD::GetControllerVibrationCapabilities(type); - if (vibcaps != PAD::VibrationCapabilities::NoVibration) + if (cinfo->vibration_caps != PAD::VibrationCapabilities::NoVibration) { PadVibrationBinding vib; vib.pad_index = pad_index; bool has_any_bindings = false; - switch (vibcaps) + switch (cinfo->vibration_caps) { case PAD::VibrationCapabilities::LargeSmallMotors: { @@ -623,6 +695,65 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch } } +void InputManager::AddUSBBindings(SettingsInterface& si, u32 port) +{ + const std::string device(USB::GetConfigDevice(si, port)); + if (device.empty() || device == "None") + return; + + const std::string section(USBGetConfigSection(port)); + const u32 subtype = USB::GetConfigSubType(si, port, device); + for (const InputBindingInfo& bi : USB::GetDeviceBindings(device, subtype)) + { + const std::string bind_name(USB::GetConfigBindKey(device, bi.name)); + + switch (bi.bind_type) + { + case InputBindingInfo::Type::Button: + case InputBindingInfo::Type::Axis: + case InputBindingInfo::Type::HalfAxis: + { + // normal bindings + const std::vector bindings(si.GetStringList(section.c_str(), bind_name.c_str())); + if (!bindings.empty()) + { + AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index]( + float value) { USB::SetDeviceBindValue(port, bind_index, value); }}); + } + } + break; + + case InputBindingInfo::Type::Keyboard: + { + // set up to receive keyboard events + s_keyboard_event_callbacks.push_back([port, base = static_cast(bi.bind_index)](InputBindingKey key, float value) { + USB::SetDeviceBindValue(port, base + key.data, value); + }); + } + break; + + case InputBindingInfo::Type::Pointer: + { + const std::vector bindings(si.GetStringList(section.c_str(), bind_name.c_str())); + for (const std::string& binding : bindings) + { + const std::optional key(GetIndexFromPointerBinding(binding)); + if (!key.has_value()) + continue; + + s_pointer_move_callbacks.emplace_back(key.value(), [port, base = bi.bind_index](InputBindingKey key, float value) { + USB::SetDeviceBindValue(port, base + key.data, value); + }); + } + } + break; + + default: + break; + } + } +} + // ------------------------------------------------------------------------ // Event Handling // ------------------------------------------------------------------------ @@ -639,8 +770,7 @@ bool InputManager::HasAnyBindingsForSource(InputBindingKey key) for (const auto& it : s_binding_map) { const InputBindingKey& okey = it.first; - if (okey.source_type == key.source_type && okey.source_index == key.source_index && - okey.source_subtype == key.source_subtype) + if (okey.source_type == key.source_type && okey.source_index == key.source_index && okey.source_subtype == key.source_subtype) { return true; } @@ -661,7 +791,11 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBi // If imgui ate the event, don't fire our handlers. const bool skip_button_handlers = PreprocessEvent(key, value, generic_key); + return ProcessEvent(key, value, skip_button_handlers); +} +bool InputManager::ProcessEvent(InputBindingKey key, float value, bool skip_button_handlers) +{ // find all the bindings associated with this key const InputBindingKey masked_key = key.MaskDirection(); const auto range = s_binding_map.equal_range(masked_key); @@ -718,8 +852,7 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBi binding->current_mask = new_mask; // Workaround for multi-key bindings that share the same keys. - if (binding->num_keys > 1 && new_full_state && prev_full_state != new_full_state && - range.first != range.second) + if (binding->num_keys > 1 && new_full_state && prev_full_state != new_full_state && range.first != range.second) { // Because the binding map isn't ordered, we could iterate in the order of Shift+F1 and then // F1, which would mean that F1 wouldn't get cancelled and still activate. So, to handle this @@ -777,6 +910,9 @@ bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInpu { if (ImGuiManager::ProcessHostKeyEvent(key, value)) return true; + + for (const KeyboardEventCallback& kbc : s_keyboard_event_callbacks) + kbc(key, value); } else if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerButton) { @@ -810,44 +946,66 @@ void InputManager::GenerateRelativeMouseEvents() if (value != state.last_value) { state.last_value = value; - InvokeEvents(key, value, GenericInputBinding::Unknown); + ProcessEvent(key, value, false); + } + + if (delta != 0.0f) + { + for (const std::pair& pmc : s_pointer_move_callbacks) + { + if (pmc.first == device) + pmc.second(key, delta); + } } } } } +std::pair InputManager::GetPointerAbsolutePosition(u32 index) +{ + return std::make_pair(s_host_pointer_positions[index][static_cast(InputPointerAxis::X)], + s_host_pointer_positions[index][static_cast(InputPointerAxis::Y)]); +} + void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y) { const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast(InputPointerAxis::X)], x); const float dy = y - std::exchange(s_host_pointer_positions[index][static_cast(InputPointerAxis::Y)], y); if (dx != 0.0f) - UpdatePointerRelativeDelta(index, InputPointerAxis::X, dx); + s_pointer_state[index][static_cast(InputPointerAxis::X)].delta.fetch_add( + static_cast(dx * 65536.0f), std::memory_order_release); if (dy != 0.0f) - UpdatePointerRelativeDelta(index, InputPointerAxis::Y, dy); + s_pointer_state[index][static_cast(InputPointerAxis::Y)].delta.fetch_add( + static_cast(dy * 65536.0f), std::memory_order_release); - ImGuiManager::UpdateMousePosition(x, y); + if (index == 0) + ImGuiManager::UpdateMousePosition(x, y); } void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input) { + s_host_pointer_positions[index][static_cast(axis)] += d; s_pointer_state[index][static_cast(axis)].delta.fetch_add(static_cast(d * 65536.0f), std::memory_order_release); + + if (index == 0 && axis <= InputPointerAxis::Y) + ImGuiManager::UpdateMousePosition(s_host_pointer_positions[0][0], s_host_pointer_positions[0][1]); } -bool InputManager::HasPointerAxisBinds() +void InputManager::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name) { - std::unique_lock lock(s_binding_map_write_lock); - for (const auto& it : s_binding_map) - { - const InputBindingKey& key = it.first; - if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerAxis && - key.data >= static_cast(InputPointerAxis::X) && key.data <= static_cast(InputPointerAxis::Y)) - { - return true; - } - } + if (VMManager::HasValidVM()) + USB::InputDeviceConnected(identifier); - return false; + Host::OnInputDeviceConnected(identifier, device_name); +} + +void InputManager::OnInputDeviceDisconnected(const std::string_view& identifier) +{ + if (VMManager::HasValidVM()) + USB::InputDeviceDisconnected(identifier); + + Host::OnInputDeviceDisconnected(identifier); } // ------------------------------------------------------------------------ @@ -1017,6 +1175,8 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind s_binding_map.clear(); s_pad_vibration_array.clear(); + s_keyboard_event_callbacks.clear(); + s_pointer_move_callbacks.clear(); // Hotkeys use the base configuration, except if the custom hotkeys option is enabled. const bool use_profile_hotkeys = si.GetBoolValue("Pad", "UseProfileHotkeyBindings", false); @@ -1035,6 +1195,26 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind 1.0f / std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), default_scale), 1.0f); } + + for (u32 port = 0; port < USB::NUM_PORTS; port++) + AddUSBBindings(binding_si, port); + + // Check for relative mode bindings, and enable if there's anything using it. + bool has_relative_mode_bindings = !s_pointer_move_callbacks.empty(); + if (!has_relative_mode_bindings) + { + for (const auto& it : s_binding_map) + { + const InputBindingKey& key = it.first; + if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerAxis && + key.data >= static_cast(InputPointerAxis::X) && key.data <= static_cast(InputPointerAxis::Y)) + { + has_relative_mode_bindings = true; + break; + } + } + } + Host::SetRelativeMouseMode(has_relative_mode_bindings); } // ------------------------------------------------------------------------ diff --git a/pcsx2/Frontend/InputManager.h b/pcsx2/Frontend/InputManager.h index 83862a8726..5cc9dbf116 100644 --- a/pcsx2/Frontend/InputManager.h +++ b/pcsx2/Frontend/InputManager.h @@ -166,6 +166,7 @@ namespace InputManager /// Maximum number of host mouse devices. static constexpr u32 MAX_POINTER_DEVICES = 1; + static constexpr u32 MAX_POINTER_BUTTONS = 3; /// Returns a pointer to the external input source class, if present. InputSource* GetInputSourceInterface(InputSourceType type); @@ -179,6 +180,12 @@ namespace InputManager /// Parses an input class string. std::optional ParseInputSourceString(const std::string_view& str); + /// Parses a pointer device string, i.e. tells you which pointer is specified. + std::optional GetIndexFromPointerBinding(const std::string_view& str); + + /// Returns the device name for a pointer index (e.g. Pointer-0). + std::string GetPointerDeviceName(u32 pointer_index); + /// Converts a key code from a human-readable string to an identifier. std::optional ConvertHostKeyboardStringToCode(const std::string_view& str); @@ -199,10 +206,10 @@ namespace InputManager std::optional ParseInputBindingKey(const std::string_view& binding); /// Converts a input key to a string. - std::string ConvertInputBindingKeyToString(InputBindingKey key); + std::string ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key); /// Converts a chord of binding keys to a string. - std::string ConvertInputBindingKeysToString(const InputBindingKey* keys, size_t num_keys); + std::string ConvertInputBindingKeysToString(InputBindingInfo::Type binding_type, const InputBindingKey* keys, size_t num_keys); /// Returns a list of all hotkeys. std::vector GetHotkeyList(); @@ -266,14 +273,20 @@ namespace InputManager /// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes. void PauseVibration(); + /// Reads absolute pointer position. + std::pair GetPointerAbsolutePosition(u32 index); + /// Updates absolute pointer position. Can call from UI thread, use when the host only reports absolute coordinates. void UpdatePointerAbsolutePosition(u32 index, float x, float y); /// Updates relative pointer position. Can call from the UI thread, use when host supports relative coordinate reporting. void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false); - /// Returns true if any bindings are present which require relative mouse movement. - bool HasPointerAxisBinds(); + /// Called when a new input device is connected. + void OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name); + + /// Called when an input device is disconnected. + void OnInputDeviceDisconnected(const std::string_view& identifier); } // namespace InputManager namespace Host @@ -286,4 +299,7 @@ namespace Host /// Called when an input device is disconnected. void OnInputDeviceDisconnected(const std::string_view& identifier); + + /// Enables relative mouse mode in the host. + void SetRelativeMouseMode(bool enabled); } // namespace Host diff --git a/pcsx2/Frontend/SDLInputSource.cpp b/pcsx2/Frontend/SDLInputSource.cpp index eb0a4462fe..9e3b4d4023 100644 --- a/pcsx2/Frontend/SDLInputSource.cpp +++ b/pcsx2/Frontend/SDLInputSource.cpp @@ -610,7 +610,7 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) m_controllers.push_back(std::move(cd)); - Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("SDL-%d", player_id), name); + InputManager::OnInputDeviceConnected(StringUtil::StdStringFromFormat("SDL-%d", player_id), name); return true; } @@ -620,7 +620,7 @@ bool SDLInputSource::CloseDevice(int joystick_index) if (it == m_controllers.end()) return false; - Host::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("SDL-%d", it->player_id)); + InputManager::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("SDL-%d", it->player_id)); if (it->haptic) SDL_HapticClose(it->haptic); diff --git a/pcsx2/Frontend/XInputSource.cpp b/pcsx2/Frontend/XInputSource.cpp index dabc1942b5..a5667f93cd 100644 --- a/pcsx2/Frontend/XInputSource.cpp +++ b/pcsx2/Frontend/XInputSource.cpp @@ -397,16 +397,15 @@ void XInputSource::HandleControllerConnection(u32 index) cd.last_state = {}; cd.last_state_scp = {}; - Host::OnInputDeviceConnected( + InputManager::OnInputDeviceConnected( StringUtil::StdStringFromFormat("XInput-%u", index), StringUtil::StdStringFromFormat("XInput Controller %u", index)); } void XInputSource::HandleControllerDisconnection(u32 index) { Console.WriteLn("XInput controller %u disconnected.", index); + InputManager::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("XInput-%u", index)); m_controllers[index] = {}; - - Host::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("XInput-%u", index)); } void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state) diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index 1363b759a6..f6ad418af7 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -20,6 +20,7 @@ #include "SaveState.h" #include "pcsx2/Config.h" #include "pcsx2/GS/config.h" +#include "gsl/span" #include @@ -87,6 +88,10 @@ void GSgetInternalResolution(int* width, int* height); void GSgetStats(std::string& info); void GSgetTitleStats(std::string& info); +/// Converts window position to normalized display coordinates (0..1). A value less than 0 or greater than 1 is +/// returned if the position lies outside the display area. +void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y); + void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config); void GSSwitchRenderer(GSRendererType new_renderer); void GSResetAPIState(); diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp index 96a83ca297..826c646b4b 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp +++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp @@ -71,9 +71,15 @@ static std::mutex s_screenshot_threads_mutex; std::unique_ptr g_gs_renderer; +// Since we read this on the EE thread, we can't put it in the renderer, because +// we might be switching while the other thread reads it. +static GSVector4 s_last_draw_rect; + + GSRenderer::GSRenderer() : m_shader_time_start(Common::Timer::GetCurrentValue()) { + s_last_draw_rect = GSVector4::zero(); } GSRenderer::~GSRenderer() = default; @@ -686,6 +692,7 @@ void GSRenderer::VSync(u32 field, bool registers_written) draw_rect = CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(), src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(), GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)); + s_last_draw_rect = draw_rect; if (GSConfig.CASMode != GSCASMode::Disabled) { @@ -924,6 +931,7 @@ void GSRenderer::PresentCurrentFrame() const GSVector4 draw_rect(CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(), src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(), GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets))); + s_last_draw_rect = draw_rect; const u64 current_time = Common::Timer::GetCurrentValue(); const float shader_time = static_cast(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start)); @@ -937,6 +945,23 @@ void GSRenderer::PresentCurrentFrame() g_gs_device->RestoreAPIState(); } +void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y) +{ + const float draw_width = s_last_draw_rect.z - s_last_draw_rect.x; + const float draw_height = s_last_draw_rect.w - s_last_draw_rect.y; + const float rel_x = window_x - s_last_draw_rect.x; + const float rel_y = window_y - s_last_draw_rect.y; + if (rel_x < 0 || rel_x > draw_width || rel_y < 0 || rel_y > draw_height) + { + *display_x = -1.0f; + *display_y = -1.0f; + return; + } + + *display_x = rel_x / draw_width; + *display_y = rel_y / draw_height; +} + #ifndef PCSX2_CORE bool GSRenderer::BeginCapture(std::string& filename) diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp index 783ed00220..d8a507b6f4 100644 --- a/pcsx2/Hw.cpp +++ b/pcsx2/Hw.cpp @@ -21,6 +21,12 @@ #include "Gif_Unit.h" #include "SPU2/spu2.h" +#ifdef PCSX2_CORE +#include "USB/USB.h" +#else +#include "USB/USBNull.h" +#endif + #include "fmt/core.h" using namespace R5900; @@ -78,7 +84,7 @@ void hwReset() vif1Reset(); gif_fifo.init(); rcntInit(); - + USBreset(); } __fi uint intcInterrupt() diff --git a/pcsx2/IopCounters.cpp b/pcsx2/IopCounters.cpp index 4aba2606ed..0795fae4f9 100644 --- a/pcsx2/IopCounters.cpp +++ b/pcsx2/IopCounters.cpp @@ -24,7 +24,11 @@ #include "Common.h" #include "SPU2/spu2.h" #include "DEV9/DEV9.h" +#ifdef PCSX2_CORE #include "USB/USB.h" +#else +#include "USB/USBNull.h" +#endif #include "IopHw.h" #include "IopDma.h" #include "CDVD/CDVD.h" diff --git a/pcsx2/IopIrq.cpp b/pcsx2/IopIrq.cpp index 0e64cb3145..b54c3f7e79 100644 --- a/pcsx2/IopIrq.cpp +++ b/pcsx2/IopIrq.cpp @@ -15,7 +15,11 @@ #include "PrecompiledHeader.h" #include "DEV9/DEV9.h" +#ifdef PCSX2_CORE #include "USB/USB.h" +#else +#include "USB/USBNull.h" +#endif #include "IopHw.h" #include "IopDma.h" #include "Common.h" diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp index ffe930fe27..78f7da56a1 100644 --- a/pcsx2/PAD/Host/PAD.cpp +++ b/pcsx2/PAD/Host/PAD.cpp @@ -600,12 +600,6 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& } } -PAD::VibrationCapabilities PAD::GetControllerVibrationCapabilities(const std::string_view& type) -{ - const ControllerInfo* info = GetControllerInfo(type); - return info ? info->vibration_caps : VibrationCapabilities::NoVibration; -} - static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section, const InputManager::GenericInputBindingMapping& mapping, GenericInputBinding generic_name, const char* bind_name) diff --git a/pcsx2/PAD/Host/PAD.h b/pcsx2/PAD/Host/PAD.h index 379873929f..f2a87250e5 100644 --- a/pcsx2/PAD/Host/PAD.h +++ b/pcsx2/PAD/Host/PAD.h @@ -106,9 +106,6 @@ namespace PAD /// Returns the list of binds for the specified controller type. std::vector GetControllerBinds(const std::string_view& type); - /// Returns the vibration configuration for the specified controller type. - VibrationCapabilities GetControllerVibrationCapabilities(const std::string_view& type); - /// Returns general information for the specified controller type. const ControllerInfo* GetControllerInfo(ControllerType type); const ControllerInfo* GetControllerInfo(const std::string_view& name); diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 037770425c..5e8e7a370b 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -26,7 +26,10 @@ #include "CDVD/CDVDcommon.h" #include "MemoryCardFile.h" -#ifndef PCSX2_CORE +#ifdef PCSX2_CORE +#include "USB/USB.h" +#else +#include "USB/USBNull.h" #include "gui/AppConfig.h" #include "GS/GS.h" #endif @@ -1077,6 +1080,62 @@ void Pcsx2Config::FramerateOptions::LoadSave(SettingsWrapper& wrap) SettingsWrapEntry(SlomoScalar); } +#ifdef PCSX2_CORE + +Pcsx2Config::USBOptions::USBOptions() +{ + for (u32 i = 0; i < static_cast(Ports.size()); i++) + { + Ports[i].DeviceType = -1; + Ports[i].DeviceSubtype = 0; + } +} + +void Pcsx2Config::USBOptions::LoadSave(SettingsWrapper& wrap) +{ + for (u32 i = 0; i < static_cast(Ports.size()); i++) + { + const std::string section(USBGetConfigSection(i)); + + std::string device = USB::DeviceTypeIndexToName(Ports[i].DeviceType); + wrap.Entry(section.c_str(), "Type", device, device); + + if (wrap.IsLoading()) + Ports[i].DeviceType = USB::DeviceTypeNameToIndex(device); + + const std::string subtype_key(fmt::format("{}_subtype", USB::DeviceTypeIndexToName(Ports[i].DeviceType))); + wrap.Entry(section.c_str(), subtype_key.c_str(), Ports[i].DeviceSubtype); + } +} + +bool Pcsx2Config::USBOptions::Port::operator==(const USBOptions::Port& right) const +{ + return OpEqu(DeviceType) && OpEqu(DeviceSubtype); +} + +bool Pcsx2Config::USBOptions::Port::operator!=(const USBOptions::Port& right) const +{ + return !this->operator==(right); +} + +bool Pcsx2Config::USBOptions::operator==(const USBOptions& right) const +{ + for (u32 i = 0; i < static_cast(Ports.size()); i++) + { + if (!OpEqu(Ports[i])) + return false; + } + + return true; +} + +bool Pcsx2Config::USBOptions::operator!=(const USBOptions& right) const +{ + return !this->operator==(right); +} + +#endif + #ifdef ENABLE_ACHIEVEMENTS Pcsx2Config::AchievementsOptions::AchievementsOptions() @@ -1197,6 +1256,9 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap) Debugger.LoadSave(wrap); Trace.LoadSave(wrap); +#ifdef PCSX2_CORE + USB.LoadSave(wrap); +#endif #ifdef ENABLE_ACHIEVEMENTS Achievements.LoadSave(wrap); diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index 1a6af9c3b4..7139948321 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -38,7 +38,6 @@ #include "Elfheader.h" #include "CDVD/CDVD.h" -#include "USB/USB.h" #include "Patch.h" #include "GameDatabase.h" @@ -120,9 +119,6 @@ void cpuReset() g_GameStarted = false; g_GameLoading = false; - // Probably not the right place, but it has to be done when the ram is actually initialized - USBsetRAM(iopMem->Main); - // FIXME: LastELF should be reset on media changes as well as on CPU resets, in // the very unlikely case that a user swaps to another media source that "looks" // the same (identical ELF names) but is actually different (devs actually could diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index fcc1b401c1..cd4f3bd77f 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -40,14 +40,15 @@ #include "GS.h" #include "GS/GS.h" #include "SPU2/spu2.h" -#include "USB/USB.h" #include "PAD/Gamepad.h" #ifndef PCSX2_CORE #include "gui/App.h" #include "gui/ConsoleLogger.h" #include "gui/SysThreads.h" +#include "USB/USBNull.h" #else +#include "USB/USB.h" #include "VMManager.h" #endif @@ -424,7 +425,7 @@ static int SysState_MTGSFreeze(FreezeAction mode, freezeData* fP) static constexpr SysState_Component SPU2{ "SPU2", SPU2freeze }; static constexpr SysState_Component PAD_{ "PAD", PADfreeze }; -static constexpr SysState_Component USB{ "USB", USBfreeze }; +static constexpr SysState_Component USB_{ "USB", USBfreeze }; static constexpr SysState_Component GS{ "GS", SysState_MTGSFreeze }; @@ -644,8 +645,8 @@ public: virtual ~SavestateEntry_USB() = default; const char* GetFilename() const { return "USB.bin"; } - void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, USB); } - void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, USB); } + void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, USB_); } + void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, USB_); } bool IsRequired() const { return false; } }; @@ -726,7 +727,7 @@ static const std::unique_ptr SavestateEntries[] = { std::unique_ptr(new SavestateEntry_VU0prog), std::unique_ptr(new SavestateEntry_VU1prog), std::unique_ptr(new SavestateEntry_SPU2), -#ifndef PCSX2_CORE +#ifdef PCSX2_CORE std::unique_ptr(new SavestateEntry_USB), #endif std::unique_ptr(new SavestateEntry_PAD), diff --git a/pcsx2/USB/USB.cpp b/pcsx2/USB/USB.cpp index d9e1bd787e..fc3a8326c0 100644 --- a/pcsx2/USB/USB.cpp +++ b/pcsx2/USB/USB.cpp @@ -20,286 +20,150 @@ #include #include "PrecompiledHeader.h" +#include "common/SettingsInterface.h" #include "common/WindowInfo.h" #include "USB.h" #include "qemu-usb/USBinternal.h" #include "qemu-usb/desc.h" -#include "shared/shared_usb.h" #include "deviceproxy.h" -#include "configuration.h" + +#include "HostSettings.h" +#include "StateWrapper.h" +#include "fmt/format.h" #define PSXCLK 36864000 /* 36.864 Mhz */ -OHCIState* qemu_ohci = NULL; -USBDevice* usb_device[2] = {NULL}; -static bool usb_opened = false; - -Config conf; -// we'll probably switch our save state system at some point to standardize in -// the core anyways -char USBfreezeID[] = "govqemUSB1"; -typedef struct +namespace USB { - char freezeID[11]; - s64 cycles; - s64 remaining; - OHCIState t; - struct + OHCIPort& GetOHCIPort(u32 port); + + static bool CreateDevice(u32 port); + static void DestroyDevice(u32 port); + static void UpdateDevice(u32 port); + + static void DoOHCIState(StateWrapper& sw); + static void DoEndpointState(USBEndpoint* ep, StateWrapper& sw); + static void DoDeviceState(USBDevice* dev, StateWrapper& sw); + static void DoPacketState(USBPacket* p, StateWrapper& sw, const std::array& valid_devices); +} // namespace USB + +static OHCIState* s_qemu_ohci = nullptr; +static USBDevice* s_usb_device[USB::NUM_PORTS] = {}; +static const DeviceProxy* s_usb_device_proxy[USB::NUM_PORTS] = {}; + +static s64 s_usb_clocks = 0; +static s64 s_usb_remaining = 0; + +int64_t g_usb_frame_time = 0; +int64_t g_usb_bit_time = 0; +int64_t g_usb_last_cycle = 0; + +std::string USBGetConfigSection(int port) +{ + return fmt::format("USB{}", port + 1); +} + +OHCIPort& USB::GetOHCIPort(u32 port) +{ + // Apparently the ports on the hub are swapped. + // Get this wrong and games like GT4 won't spin your wheelz. + const u32 rhport = (port == 0) ? 1 : 0; + return s_qemu_ohci->rhport[rhport]; +} + +bool USB::CreateDevice(u32 port) +{ + const Pcsx2Config::USBOptions::Port& portcfg = EmuConfig.USB.Ports[port]; + const DeviceProxy* proxy = RegisterDevice::instance().Device(portcfg.DeviceType); + if (!proxy) + return true; + + DevCon.WriteLn("(USB) Creating a %s in port %u", proxy->Name(), port + 1); + USBDevice* dev; { - DeviceType index; - u32 size; - USBDevice dev; - } device[2]; - - struct usb_packet - { - USBEndpoint ep; //usb packet endpoint - u32 dev_index; - u32 data_size; - } usb_packet; -} USBfreezeData; - -u8* ram = 0; -FILE* usbLog; -int64_t usb_frame_time; -int64_t usb_bit_time; - -s64 clocks = 0; -s64 remaining = 0; - -#if defined(_WIN32) -HWND gsWnd = nullptr; -#elif defined(__linux__) -#include "gtk.h" -#include -#include -Display* g_GSdsp = nullptr; -Window g_GSwin; -#endif - -Config::Config() - : Log(0) -{ -} - -//Simpler to reset and reattach after USBclose/USBopen -void Reset() -{ - if (qemu_ohci) - ohci_hard_reset(qemu_ohci); -} - -void OpenDevice(int port) -{ - //TODO Pass pDsp to open probably so dinput can bind to this HWND - if (usb_device[port] && usb_device[port]->klass.open) - usb_device[port]->klass.open(usb_device[port] /*, pDsp*/); -} - -static void CloseDevice(int port) -{ - if (usb_device[port] && usb_device[port]->klass.close) - usb_device[port]->klass.close(usb_device[port]); -} - -void DestroyDevice(int port) -{ - if (qemu_ohci && qemu_ohci->rhport[port].port.dev) - { - qemu_ohci->rhport[port].port.dev->klass.unrealize(qemu_ohci->rhport[port].port.dev); - qemu_ohci->rhport[port].port.dev = nullptr; + auto lock = Host::GetSettingsLock(); + dev = proxy->CreateDevice(*Host::GetSettingsInterface(), port, portcfg.DeviceSubtype); } - else if (usb_device[port]) - usb_device[port]->klass.unrealize(usb_device[port]); - - usb_device[port] = nullptr; -} - -void DestroyDevices() -{ - for (int i = 0; i < 2; i++) + if (!dev) { - CloseDevice(i); - DestroyDevice(i); + Console.Error("Failed to create USB device in port %u (%s)", port + 1, proxy->Name()); + return false; } + + pxAssertRel(s_qemu_ohci, "Has OHCI"); + pxAssertRel(!GetOHCIPort(port).port.dev, "No device in OHCI when creating"); + GetOHCIPort(port).port.dev = dev; + dev->attached = true; + usb_attach(&GetOHCIPort(port).port); + s_usb_device[port] = dev; + s_usb_device_proxy[port] = proxy; + return true; } -static USBDevice* CreateDevice(DeviceType index, int port) +void USB::DestroyDevice(u32 port) { - USBDevice* device = nullptr; - - if (index == DEVTYPE_NONE) - return nullptr; - - DeviceProxyBase* devProxy = RegisterDevice::instance().Device(index); - if (devProxy) - device = devProxy->CreateDevice(port); - else - Console.WriteLn(Color_Red, "Device %d: Unknown device type", 1 - port); - - if (!device) - { - } - return device; -} - -//TODO re-do sneaky attach -static void USBAttach(int port, USBDevice* dev, bool sneaky = false) -{ - if (!qemu_ohci) + USBDevice* dev = s_usb_device[port]; + if (!dev) return; - USBDevice* tmp = qemu_ohci->rhport[port].port.dev; - if (tmp) - { - if (!sneaky) - usb_detach(&qemu_ohci->rhport[port].port); - tmp->klass.unrealize(tmp); - } - - qemu_ohci->rhport[port].port.dev = dev; - if (dev) - { - dev->attached = true; - usb_attach(&qemu_ohci->rhport[port].port); //.ops->attach(&(qemu_ohci->rhport[port].port)); - } + if (dev->klass.unrealize) + dev->klass.unrealize(dev); + GetOHCIPort(port).port.dev = nullptr; + s_usb_device[port] = nullptr; + s_usb_device_proxy[port] = nullptr; } -static USBDevice* CreateDevice(const std::string& name, int port) +void USB::UpdateDevice(u32 port) { - USBDevice* device = nullptr; + if (!s_usb_device[port]) + return; - if (!name.empty()) - { - DeviceProxyBase* devProxy = RegisterDevice::instance().Device(name); - if (devProxy) - device = devProxy->CreateDevice(port); - else - Console.WriteLn(Color_Red, "Port %d: Unknown device type", port); - } - - return device; -} - -void CreateDevices() -{ - if (!qemu_ohci) - return; //No USBinit yet ie. called from config. dialog - DestroyDevices(); - - for (int i = 0; i < 2; i++) - { - usb_device[i] = CreateDevice(conf.Port[i], i); - USBAttach(i, usb_device[i]); - if (usb_opened) - OpenDevice(i); - } + auto lock = Host::GetSettingsLock(); + s_usb_device_proxy[port]->UpdateSettings(s_usb_device[port], *Host::GetSettingsInterface()); } s32 USBinit() { - USBsetSettingsDir(); - RegisterDevice::Register(); - LoadConfig(); - - if (conf.Log && !usbLog) - { -#ifdef _WIN32 - usbLog = wfopen(LogDir.c_str(), L"wb"); // L"wb,ccs=UNICODE"); -#else - usbLog = wfopen(LogDir.c_str(), "wb"); // L"wb,ccs=UNICODE"); -#endif - //if(usbLog) setvbuf(usbLog, NULL, _IONBF, 0); - } - - qemu_ohci = ohci_create(0x1f801600, 2); - if (!qemu_ohci) - return 1; - - clocks = 0; - remaining = 0; - return 0; } void USBshutdown() { - - DestroyDevices(); RegisterDevice::instance().Unregister(); - - free(qemu_ohci); - - ram = 0; - - //#ifdef _DEBUG - if (conf.Log && usbLog) - { - fclose(usbLog); - usbLog = nullptr; - } - //#endif - usb_opened = false; } -s32 USBopen(const WindowInfo& wi) +bool USBopen() { + s_qemu_ohci = ohci_create(0x1f801600, 2); + if (!s_qemu_ohci) + return false; - if (conf.Log && !usbLog) - { - usbLog = fopen("logs/usbLog.txt", "a"); - //if(usbLog) setvbuf(usbLog, NULL, _IONBF, 0); - } + s_usb_clocks = 0; + s_usb_remaining = 0; + g_usb_last_cycle = 0; - void* window_handle_for_init = nullptr; -#if defined(_WIN32) - if (wi.type == WindowInfo::Type::Win32) - { - gsWnd = static_cast(wi.window_handle); - window_handle_for_init = wi.window_handle; - } -#elif defined(__linux__) - if (wi.type == WindowInfo::Type::X11) - { - g_GSdsp = static_cast(wi.display_connection); - g_GSwin = reinterpret_cast(wi.window_handle); - window_handle_for_init = reinterpret_cast(g_GSwin); - } -#endif + for (u32 port = 0; port < USB::NUM_PORTS; port++) + USB::CreateDevice(port); - try - { - shared::Initialize(window_handle_for_init); - } - catch (std::runtime_error& e) - { - Console.WriteLn(Color_Red, "USB: %s", e.what()); - } - - if (!usb_device[0] && !usb_device[1]) - { - CreateDevices(); //TODO Pass pDsp to init? - } - - OpenDevice(0 /*, pDsp */); - OpenDevice(1 /*, pDsp */); - usb_opened = true; - return 0; + return true; } void USBclose() { - CloseDevice(0); - CloseDevice(1); - shared::Uninitialize(); - usb_opened = false; -#if defined(_WIN32) - gsWnd = {}; -#elif defined(__linux__) - g_GSdsp = nullptr; - g_GSwin = {}; -#endif + for (u32 port = 0; port < USB::NUM_PORTS; port++) + USB::DestroyDevice(port); + + free(s_qemu_ohci); + s_qemu_ohci = nullptr; +} + +void USBreset() +{ + s_usb_clocks = 0; + s_usb_remaining = 0; + g_usb_last_cycle = 0; + ohci_hard_reset(s_qemu_ohci); } u8 USBread8(u32 addr) @@ -316,7 +180,7 @@ u32 USBread32(u32 addr) { u32 hard; - hard = ohci_mem_read(qemu_ohci, addr); + hard = ohci_mem_read(s_qemu_ohci, addr); return hard; @@ -332,237 +196,311 @@ void USBwrite16(u32 addr, u16 value) void USBwrite32(u32 addr, u32 value) { - ohci_mem_write(qemu_ohci, addr, value); + ohci_mem_write(s_qemu_ohci, addr, value); } -extern u32 bits; - -void USBsetRAM(void* mem) +void USB::DoOHCIState(StateWrapper& sw) { - ram = (u8*)mem; - Reset(); + if (!sw.DoMarker("USBOHCI")) + return; + + sw.Do(&g_usb_last_cycle); + sw.Do(&s_usb_clocks); + sw.Do(&s_usb_remaining); + + sw.Do(&s_qemu_ohci->eof_timer); + sw.Do(&s_qemu_ohci->sof_time); + + sw.Do(&s_qemu_ohci->ctl); + sw.Do(&s_qemu_ohci->status); + sw.Do(&s_qemu_ohci->intr_status); + sw.Do(&s_qemu_ohci->intr); + + sw.Do(&s_qemu_ohci->hcca); + sw.Do(&s_qemu_ohci->ctrl_head); + sw.Do(&s_qemu_ohci->ctrl_cur); + sw.Do(&s_qemu_ohci->bulk_head); + sw.Do(&s_qemu_ohci->bulk_cur); + sw.Do(&s_qemu_ohci->per_cur); + sw.Do(&s_qemu_ohci->done); + sw.Do(&s_qemu_ohci->done_count); + + s_qemu_ohci->fsmps = sw.DoBitfield(s_qemu_ohci->fsmps); + s_qemu_ohci->fit = sw.DoBitfield(s_qemu_ohci->fit); + s_qemu_ohci->fi = sw.DoBitfield(s_qemu_ohci->fi); + s_qemu_ohci->frt = sw.DoBitfield(s_qemu_ohci->frt); + sw.Do(&s_qemu_ohci->frame_number); + sw.Do(&s_qemu_ohci->padding); + sw.Do(&s_qemu_ohci->pstart); + sw.Do(&s_qemu_ohci->lst); + + sw.Do(&s_qemu_ohci->rhdesc_a); + sw.Do(&s_qemu_ohci->rhdesc_b); + for (u32 i = 0; i < OHCI_MAX_PORTS; i++) + sw.Do(&s_qemu_ohci->rhport[i].ctrl); + + sw.Do(&s_qemu_ohci->old_ctl); + sw.DoArray(s_qemu_ohci->usb_buf, sizeof(s_qemu_ohci->usb_buf)); + sw.Do(&s_qemu_ohci->async_td); + sw.Do(&s_qemu_ohci->async_complete); +} + +void USB::DoDeviceState(USBDevice* dev, StateWrapper& sw) +{ + if (!sw.DoMarker("USBDevice")) + return; + + sw.Do(&dev->speed); + sw.Do(&dev->addr); + sw.Do(&dev->state); + sw.DoBytes(&dev->setup_buf, sizeof(dev->setup_buf)); + sw.DoBytes(&dev->data_buf, sizeof(dev->data_buf)); + sw.Do(&dev->remote_wakeup); + sw.Do(&dev->setup_state); + sw.Do(&dev->setup_len); + sw.Do(&dev->setup_index); + + sw.Do(&dev->configuration); + usb_desc_set_config(dev, dev->configuration); + + int altsetting[USB_MAX_INTERFACES]; + std::memcpy(altsetting, dev->altsetting, sizeof(altsetting)); + sw.DoPODArray(altsetting, std::size(altsetting)); + for (u32 i = 0; i < USB_MAX_INTERFACES; i++) + { + dev->altsetting[i] = altsetting[i]; + usb_desc_set_interface(dev, i, altsetting[i]); + } + + DoEndpointState(&dev->ep_ctl, sw); + for (u32 i = 0; i < USB_MAX_ENDPOINTS; i++) + DoEndpointState(&dev->ep_in[i], sw); + for (u32 i = 0; i < USB_MAX_ENDPOINTS; i++) + DoEndpointState(&dev->ep_out[i], sw); +} + +void USB::DoEndpointState(USBEndpoint* ep, StateWrapper& sw) +{ + // assumed the fields above are constant + sw.Do(&ep->pipeline); + sw.Do(&ep->halted); + + if (sw.IsReading()) + { + // clear out all packets, we'll fill it in later + while (!QTAILQ_EMPTY(&ep->queue)) + QTAILQ_REMOVE(&ep->queue, QTAILQ_FIRST(&ep->queue), queue); + } +} + +void USB::DoPacketState(USBPacket* p, StateWrapper& sw, const std::array& valid_devices) +{ + if (!sw.DoMarker("USBPacket")) + return; + + s32 dev_index = -1; + s32 ep_index = -1; + u32 p_iov_size = 0; + bool queued = false; + if (sw.IsWriting()) + { + USBEndpoint* ep = p->ep; + if (ep) + { + for (u32 i = 0; i < NUM_PORTS; i++) + { + USBDevice* dev = s_usb_device[i]; + if (valid_devices[i] && ep->dev == dev) + { + dev_index = static_cast(i); + if (ep == &dev->ep_ctl) + ep_index = 0; + else if (ep >= &dev->ep_in[0] && ep <= &dev->ep_in[USB_MAX_ENDPOINTS - 1]) + ep_index = static_cast(ep - &dev->ep_in[0]) + 1; + else if (ep >= &dev->ep_out[0] && ep <= &dev->ep_out[USB_MAX_ENDPOINTS - 1]) + ep_index = static_cast(ep - &dev->ep_out[0]) + 1 + USB_MAX_ENDPOINTS; + + USBPacket* pp; + QTAILQ_FOREACH(pp, &ep->queue, queue) + { + if (pp == p) + queued = true; + } + + break; + } + } + if (dev_index < 0 || ep_index < 0) + Console.Error("Failed to save USB packet from unknown endpoint"); + else + p_iov_size = p->iov.size; + } + } + + sw.Do(&dev_index); + sw.Do(&ep_index); + sw.Do(&p_iov_size); + sw.Do(&queued); + + sw.Do(&p->pid); + sw.Do(&p->id); + sw.Do(&p->stream); + sw.Do(&p->parameter); + sw.Do(&p->short_not_ok); + sw.Do(&p->int_req); + sw.Do(&p->status); + sw.Do(&p->actual_length); + sw.Do(&p->state); + + if (sw.IsReading()) + { + qemu_iovec_reset(&p->iov); + p->ep = nullptr; + + if (dev_index >= 0 && ep_index >= 0 && valid_devices[static_cast(dev_index)]) + { + USBDevice* dev = s_usb_device[static_cast(dev_index)]; + pxAssert(dev); + + if (p_iov_size > 0) + qemu_iovec_add(&p->iov, s_qemu_ohci->usb_buf, p_iov_size); + + if (ep_index == 0) + p->ep = &dev->ep_ctl; + else if (ep_index < (1 + USB_MAX_ENDPOINTS)) + p->ep = &dev->ep_in[ep_index - 1]; + else if (ep_index < (1 + USB_MAX_ENDPOINTS + USB_MAX_ENDPOINTS)) + p->ep = &dev->ep_out[ep_index - 1 - USB_MAX_ENDPOINTS]; + + if (p->ep && queued) + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); + } + } } s32 USBfreeze(FreezeAction mode, freezeData* data) { - USBfreezeData usbd = {0}; + std::array valid_devices = {}; - //TODO FREEZE_SIZE mismatch causes loading to fail in PCSX2 beforehand if (mode == FreezeAction::Load) { - if ((long unsigned int)data->size < sizeof(USBfreezeData)) + StateWrapper::ReadOnlyMemoryStream swstream(data->data, data->size); + StateWrapper sw(&swstream, StateWrapper::Mode::Read, g_SaveVersion); + + if (!sw.DoMarker("USB")) { - Console.WriteLn(Color_Red, "USB: Unable to load freeze data! Got %d bytes, expected >= %zu.", data->size, sizeof(USBfreezeData)); - return -1; + Console.Error("USB state is invalid, resetting."); + USBreset(); + return 0; } - usbd = *(USBfreezeData*)data->data; - usbd.freezeID[10] = 0; + USB::DoOHCIState(sw); - if (strcmp(usbd.freezeID, USBfreezeID) != 0) + for (u32 port = 0; port < USB::NUM_PORTS; port++) { - Console.WriteLn(Color_Red, "USB: Unable to load freeze data! Found ID %s, expected ID %s.", usbd.freezeID, USBfreezeID); - return -1; + s32 state_devtype; + u32 state_devsubtype; + u32 state_size; + sw.Do(&state_devtype); + sw.Do(&state_devsubtype); + sw.Do(&state_size); + + // this is *assuming* the config is correct... there's no reason it shouldn't be. + if (sw.HasError() || + EmuConfig.USB.Ports[port].DeviceType != state_devtype || + EmuConfig.USB.Ports[port].DeviceSubtype != state_devsubtype || + (state_devtype != DEVTYPE_NONE && !s_usb_device[port])) + { + Console.Error("Save state has device type %u, but config has %u. Reattaching device.", state_devtype, EmuConfig.USB.Ports[port].DeviceType); + if (s_usb_device[port]) + usb_reattach(&USB::GetOHCIPort(port).port); + + sw.SkipBytes(state_size); + continue; + } + + if (!s_usb_device[port]) + { + // nothing in this port + sw.SkipBytes(state_size); + continue; + } + + USB::DoDeviceState(s_usb_device[port], sw); + + if (!s_usb_device_proxy[port]->Freeze(s_usb_device[port], sw) || sw.HasError()) + { + Console.Error("Failed to deserialize USB port %u, removing device.", port); + USB::DestroyDevice(port); + continue; + } + + valid_devices[port] = true; } - if ((long unsigned int)data->size < sizeof(USBfreezeData) + usbd.device[0].size + usbd.device[1].size + 8192) - return -1; - - //TODO Subsequent save state loadings make USB "stall" for n seconds since previous load - //clocks = usbd.cycles; - //remaining = usbd.remaining; - - CloseDevice(0); - CloseDevice(1); - - for (uint32_t i = 0; i < qemu_ohci->num_ports; i++) + USB::DoPacketState(&s_qemu_ohci->usb_packet, sw, valid_devices); + if (sw.HasError()) { - usbd.t.rhport[i].port.opaque = qemu_ohci; - usbd.t.rhport[i].port.ops = qemu_ohci->rhport[i].port.ops; - usbd.t.rhport[i].port.dev = qemu_ohci->rhport[i].port.dev; - } - //if (qemu_ohci->usb_packet.iov.iov) - usb_packet_cleanup(&qemu_ohci->usb_packet); - *qemu_ohci = usbd.t; - // restore USBPacket for OHCIState - usb_packet_init(&qemu_ohci->usb_packet); - - u8* ptr = data->data + sizeof(USBfreezeData); - RegisterDevice& regInst = RegisterDevice::instance(); - for (int i = 0; i < 2; i++) - { - auto index = regInst.Index(conf.Port[i]); - auto proxy = regInst.Device(index); - - //TODO FREEZE_SIZE mismatch causes loading to fail in PCSX2 beforehand - // but just in case, recreate the same device type as was saved - if (usbd.device[i].index != index) - { - index = usbd.device[i].index; - DestroyDevice(i); - conf.Port[i].clear(); - - proxy = regInst.Device(index); - if (proxy) - { - // re-create with saved device type - conf.Port[i] = proxy->TypeName(); - usb_device[i] = CreateDevice(index, i); - USBAttach(i, usb_device[i], index != DEVTYPE_MSD); - } - } - - if (proxy && usb_device[i]) /* usb device creation may have failed for some reason */ - { - if (proxy->Freeze(FreezeAction::Size, usb_device[i], nullptr) != (s32)usbd.device[i].size) - { - Console.WriteLn(Color_Red, "USB: Port %d: device's freeze size doesn't match.", i); - return -1; - } - - const USBDevice& tmp = usbd.device[i].dev; - - usb_device[i]->addr = tmp.addr; - usb_device[i]->attached = tmp.attached; - usb_device[i]->auto_attach = tmp.auto_attach; - usb_device[i]->configuration = tmp.configuration; - usb_device[i]->ninterfaces = tmp.ninterfaces; - usb_device[i]->flags = tmp.flags; - usb_device[i]->state = tmp.state; - usb_device[i]->remote_wakeup = tmp.remote_wakeup; - usb_device[i]->setup_state = tmp.setup_state; - usb_device[i]->setup_len = tmp.setup_len; - usb_device[i]->setup_index = tmp.setup_index; - - memcpy(usb_device[i]->data_buf, tmp.data_buf, sizeof(tmp.data_buf)); - memcpy(usb_device[i]->setup_buf, tmp.setup_buf, sizeof(tmp.setup_buf)); - - usb_desc_set_config(usb_device[i], tmp.configuration); - for (int k = 0; k < 16; k++) - { - usb_device[i]->altsetting[k] = tmp.altsetting[k]; - usb_desc_set_interface(usb_device[i], k, tmp.altsetting[k]); - } - - proxy->Freeze(FreezeAction::Load, usb_device[i], ptr); - if (!usb_device[i]->attached) - { // FIXME FREEZE_SAVE fcked up - usb_device[i]->attached = true; - usb_device_reset(usb_device[i]); - //TODO reset port if save state's and configured wheel types are different - usb_detach(&qemu_ohci->rhport[i].port); - usb_attach(&qemu_ohci->rhport[i].port); - } - OpenDevice(i); - } - else if (!proxy && index != DEVTYPE_NONE) - { - Console.WriteLn(Color_Red, "USB: Port %d: unknown device.\nUSB is probably too old for this save.", i); - } - ptr += usbd.device[i].size; - } - - u32 dev_index = usbd.usb_packet.dev_index; - - if (dev_index < countof(usb_device) && usb_device[dev_index]) - { - USBPacket* p = &qemu_ohci->usb_packet; - p->actual_length = usbd.usb_packet.data_size; - - QEMUIOVector* iov = p->combined ? &p->combined->iov : &p->iov; - iov_from_buf(iov->iov, iov->niov, 0, ptr, p->actual_length); - - if (usbd.usb_packet.ep.pid == USB_TOKEN_SETUP) - { - if (usb_device[dev_index]->ep_ctl.ifnum == usbd.usb_packet.ep.ifnum) - qemu_ohci->usb_packet.ep = &usb_device[dev_index]->ep_ctl; - } - else - { - USBEndpoint* eps = nullptr; - if (usbd.usb_packet.ep.pid == USB_TOKEN_IN) - eps = usb_device[dev_index]->ep_in; - else //if (usbd.ep.pid == USB_TOKEN_OUT) - eps = usb_device[dev_index]->ep_out; - - for (int k = 0; k < USB_MAX_ENDPOINTS; k++) - { - - if (usbd.usb_packet.ep.type == eps[k].type && usbd.usb_packet.ep.nr == eps[k].nr && usbd.usb_packet.ep.ifnum == eps[k].ifnum && usbd.usb_packet.ep.pid == eps[k].pid) - { - qemu_ohci->usb_packet.ep = &eps[k]; - break; - } - } - } + Console.WriteLn("Failed to read USB packet, resetting all devices."); + USBreset(); + return 0; } } - //TODO straight copying of structs can break cross-platform/cross-compiler save states 'cause padding 'n' stuff else if (mode == FreezeAction::Save) { - memset(data->data, 0, data->size); //maybe it already is... - RegisterDevice& regInst = RegisterDevice::instance(); - usbd.usb_packet.dev_index = -1; + std::memset(data->data, 0, data->size); - for (int i = 0; i < 2; i++) + StateWrapper::MemoryStream swstream(data->data, data->size); + StateWrapper sw(&swstream, StateWrapper::Mode::Write, g_SaveVersion); + + if (!sw.DoMarker("USB")) + return -1; + + USB::DoOHCIState(sw); + + for (u32 port = 0; port < USB::NUM_PORTS; port++) { - //TODO check that current created usb device and conf.Port[n] are the same - auto index = regInst.Index(conf.Port[i]); - auto proxy = regInst.Device(index); - usbd.device[i].index = index; + s32 state_devtype = EmuConfig.USB.Ports[port].DeviceType; + u32 state_devsubtype = EmuConfig.USB.Ports[port].DeviceSubtype; + sw.Do(&state_devtype); + sw.Do(&state_devsubtype); - if (proxy && usb_device[i]) - usbd.device[i].size = proxy->Freeze(FreezeAction::Size, usb_device[i], nullptr); - else - usbd.device[i].size = 0; + const u32 size_pos = swstream.GetPosition(); + u32 state_size = 0; + sw.Do(&state_size); - if (qemu_ohci->usb_packet.ep && qemu_ohci->usb_packet.ep->dev == usb_device[i]) - usbd.usb_packet.dev_index = i; - } + if (sw.HasError()) + return -1; - strncpy(usbd.freezeID, USBfreezeID, strlen(USBfreezeID)); - usbd.t = *qemu_ohci; - usbd.t.usb_packet.iov = {}; - usbd.t.usb_packet.ep = nullptr; - if (qemu_ohci->usb_packet.ep) - usbd.usb_packet.ep = *qemu_ohci->usb_packet.ep; - - for (uint32_t i = 0; i < qemu_ohci->num_ports; i++) - { - usbd.t.rhport[i].port.opaque = nullptr; - usbd.t.rhport[i].port.ops = nullptr; - usbd.t.rhport[i].port.dev = nullptr; - } - - usbd.cycles = clocks; - usbd.remaining = remaining; - - u8* ptr = data->data + sizeof(USBfreezeData); - - // Save the state of the attached devices - for (int i = 0; i < 2; i++) - { - auto proxy = regInst.Device(conf.Port[i]); - if (usb_device[i]) + if (!s_usb_device[port]) { - usbd.device[i].dev = *usb_device[i]; - if (proxy && usbd.device[i].size) - proxy->Freeze(FreezeAction::Save, usb_device[i], ptr); + // nothing in this port + continue; } - memset(&usbd.device[i].dev.klass, 0, sizeof(USBDeviceClass)); - ptr += usbd.device[i].size; + const u32 start_pos = swstream.GetPosition(); + USB::DoDeviceState(s_usb_device[port], sw); + if (!s_usb_device_proxy[port]->Freeze(s_usb_device[port], sw) || sw.HasError()) + { + Console.Error("Failed to serialize USB port %u.", port); + return -1; + } + + const u32 end_pos = swstream.GetPosition(); + state_size = end_pos - start_pos; + if (!swstream.SeekAbsolute(size_pos) || (sw.Do(&state_size), sw.HasError()) || !swstream.SeekAbsolute(end_pos)) + return -1; + + valid_devices[port] = true; } - USBPacket* p = &qemu_ohci->usb_packet; - usbd.usb_packet.data_size = p->actual_length; - QEMUIOVector* iov = p->combined ? &p->combined->iov : &p->iov; - iov_to_buf(iov->iov, iov->niov, 0, ptr, p->actual_length); - - *(USBfreezeData*)data->data = usbd; + USB::DoPacketState(&s_qemu_ohci->usb_packet, sw, valid_devices); + if (sw.HasError()) + return -1; } else if (mode == FreezeAction::Size) { + // I don't like this, but until we move everything over to state wrapper, it'll have to do. data->size = 0x10000; } @@ -571,31 +509,31 @@ s32 USBfreeze(FreezeAction mode, freezeData* data) void USBasync(u32 cycles) { - remaining += cycles; - clocks += remaining; - if (qemu_ohci->eof_timer > 0) + s_usb_remaining += cycles; + s_usb_clocks += s_usb_remaining; + if (s_qemu_ohci->eof_timer > 0) { - while ((uint64_t)remaining >= qemu_ohci->eof_timer) + while ((uint64_t)s_usb_remaining >= s_qemu_ohci->eof_timer) { - remaining -= qemu_ohci->eof_timer; - qemu_ohci->eof_timer = 0; - ohci_frame_boundary(qemu_ohci); + s_usb_remaining -= s_qemu_ohci->eof_timer; + s_qemu_ohci->eof_timer = 0; + ohci_frame_boundary(s_qemu_ohci); /* * Break out of the loop if bus was stopped. * If ohci_frame_boundary hits an UE, but doesn't stop processing, * it seems to cause a hang inside the game instead. */ - if (!qemu_ohci->eof_timer) + if (!s_qemu_ohci->eof_timer) break; } - if ((remaining > 0) && (qemu_ohci->eof_timer > 0)) + if ((s_usb_remaining > 0) && (s_qemu_ohci->eof_timer > 0)) { - s64 m = qemu_ohci->eof_timer; - if (remaining < m) - m = remaining; - qemu_ohci->eof_timer -= m; - remaining -= m; + s64 m = s_qemu_ohci->eof_timer; + if (s_usb_remaining < m) + m = s_usb_remaining; + s_qemu_ohci->eof_timer -= m; + s_usb_remaining -= m; } } //if(qemu_ohci->eof_timer <= 0) @@ -604,29 +542,226 @@ void USBasync(u32 cycles) //} } -int cpu_physical_memory_rw(u32 addr, u8* buf, size_t len, int is_write) -{ - // invalid address, reset and try again - if ((u64)addr + len >= 0x200000) - { - if (qemu_ohci) - ohci_soft_reset(qemu_ohci); - return 1; - } - - if (is_write) - memcpy(&(ram[addr]), buf, len); - else - memcpy(buf, &(ram[addr]), len); - return 0; -} - -int get_ticks_per_second() +int usb_get_ticks_per_second() { return PSXCLK; } -s64 get_clock() +s64 usb_get_clock() { - return clocks; + return s_usb_clocks; +} + +s32 USB::DeviceTypeNameToIndex(const std::string_view& device) +{ + RegisterDevice& rd = RegisterDevice::instance(); + return rd.Index(device); +} + +const char* USB::DeviceTypeIndexToName(s32 device) +{ + RegisterDevice& rd = RegisterDevice::instance(); + const DeviceProxy* proxy = rd.Device(device); + return proxy ? proxy->TypeName() : "None"; +} + +std::vector> USB::GetDeviceTypes() +{ + RegisterDevice& rd = RegisterDevice::instance(); + std::vector> ret; + ret.reserve(rd.Map().size()); + for (const auto& it : rd.Map()) + ret.emplace_back(it.second->TypeName(), it.second->Name()); + return ret; +} + +const char* USB::GetDeviceName(const std::string_view& device) +{ + const DeviceProxy* dev = RegisterDevice::instance().Device(device); + return dev ? dev->Name() : "Not Connected"; +} + +std::vector USB::GetDeviceSubtypes(const std::string_view& device) +{ + const DeviceProxy* dev = RegisterDevice::instance().Device(device); + return dev ? dev->SubTypes() : std::vector(); +} + +gsl::span USB::GetDeviceBindings(const std::string_view& device, u32 subtype) +{ + const DeviceProxy* dev = RegisterDevice::instance().Device(device); + return dev ? dev->Bindings(subtype) : gsl::span(); +} + +gsl::span USB::GetDeviceSettings(const std::string_view& device, u32 subtype) +{ + const DeviceProxy* dev = RegisterDevice::instance().Device(device); + return dev ? dev->Settings(subtype) : gsl::span(); +} + +gsl::span USB::GetDeviceBindings(u32 port) +{ + pxAssert(port < NUM_PORTS); + if (s_usb_device_proxy[port]) + return s_usb_device_proxy[port]->Bindings(EmuConfig.USB.Ports[port].DeviceSubtype); + else + return {}; +} + +float USB::GetDeviceBindValue(u32 port, u32 bind_index) +{ + pxAssert(port < NUM_PORTS); + if (!s_usb_device[port]) + return 0.0f; + + return s_usb_device_proxy[port]->GetBindingValue(s_usb_device[port], bind_index); +} + +void USB::SetDeviceBindValue(u32 port, u32 bind_index, float value) +{ + pxAssert(port < NUM_PORTS); + if (!s_usb_device[port]) + return; + + s_usb_device_proxy[port]->SetBindingValue(s_usb_device[port], bind_index, value); +} + +void USB::InputDeviceConnected(const std::string_view& identifier) +{ + for (u32 i = 0; i < NUM_PORTS; i++) + { + if (s_usb_device[i]) + s_usb_device_proxy[i]->InputDeviceConnected(s_usb_device[i], identifier); + } +} + +void USB::InputDeviceDisconnected(const std::string_view& identifier) +{ + for (u32 i = 0; i < NUM_PORTS; i++) + { + if (s_usb_device[i]) + s_usb_device_proxy[i]->InputDeviceDisconnected(s_usb_device[i], identifier); + } +} + +std::string USB::GetConfigDevice(const SettingsInterface& si, u32 port) +{ + return si.GetStringValue(USBGetConfigSection(port).c_str(), "Type", "None"); +} + +u32 USB::GetConfigSubType(const SettingsInterface& si, u32 port, const std::string_view& devname) +{ + return si.GetUIntValue(USBGetConfigSection(port).c_str(), fmt::format("{}_subtype", devname).c_str(), 0u); +} + +std::string USB::GetConfigBindKey(const std::string_view& device, const std::string_view& bind_name) +{ + return fmt::format("{}_{}", device, bind_name); +} + +bool USB::GetConfigBool(SettingsInterface& si, u32 port, const char* devname, const char* key, bool default_value) +{ + const std::string real_key(fmt::format("{}_{}", devname, key)); + return si.GetBoolValue(USBGetConfigSection(port).c_str(), real_key.c_str(), default_value); +} + +s32 USB::GetConfigInt(SettingsInterface& si, u32 port, const char* devname, const char* key, s32 default_value) +{ + const std::string real_key(fmt::format("{}_{}", devname, key)); + return si.GetIntValue(USBGetConfigSection(port).c_str(), real_key.c_str(), default_value); +} + +float USB::GetConfigFloat(SettingsInterface& si, u32 port, const char* devname, const char* key, float default_value) +{ + const std::string real_key(fmt::format("{}_{}", devname, key)); + return si.GetFloatValue(USBGetConfigSection(port).c_str(), real_key.c_str(), default_value); +} + + +std::string USB::GetConfigString(SettingsInterface& si, u32 port, const char* devname, const char* key, const char* default_value /*= ""*/) +{ + const std::string real_key(fmt::format("{}_{}", devname, key)); + return si.GetStringValue(USBGetConfigSection(port).c_str(), real_key.c_str(), default_value); +} + + +static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section, const std::string& type, + const std::vector>& mapping, GenericInputBinding generic_name, + const char* bind_name) +{ + // find the mapping it corresponds to + const std::string* found_mapping = nullptr; + for (const std::pair& it : mapping) + { + if (it.first == generic_name) + { + found_mapping = &it.second; + break; + } + } + + const std::string key(USB::GetConfigBindKey(type, bind_name)); + if (found_mapping) + { + Console.WriteLn("(MapDevice) Map %s/%s to '%s'", section.c_str(), bind_name, found_mapping->c_str()); + si.SetStringValue(section.c_str(), key.c_str(), found_mapping->c_str()); + return 1; + } + else + { + si.DeleteValue(section.c_str(), key.c_str()); + return 0; + } +} + +bool USB::MapDevice(SettingsInterface& si, u32 port, const std::vector>& mapping) +{ + const std::string section(USBGetConfigSection(port)); + const std::string type(GetConfigDevice(si, port)); + const u32 subtype = GetConfigSubType(si, port, type); + const DeviceProxy* dev = RegisterDevice::instance().Device(type); + if (!dev) + return false; + + u32 num_mappings = 0; + for (const InputBindingInfo& bi : dev->Bindings(subtype)) + { + if (bi.generic_mapping == GenericInputBinding::Unknown) + continue; + + num_mappings += TryMapGenericMapping(si, section, type, mapping, bi.generic_mapping, bi.name); + } + + return (num_mappings > 0); +} + +void USB::ClearPortBindings(SettingsInterface& si, u32 port) +{ + const std::string section(USBGetConfigSection(port)); + const std::string type(GetConfigDevice(si, port)); + const u32 subtype = GetConfigSubType(si, port, type); + const DeviceProxy* dev = RegisterDevice::instance().Device(type); + if (!dev) + return; + + for (const InputBindingInfo& bi : dev->Bindings(subtype)) + si.DeleteValue(section.c_str(), GetConfigBindKey(type, bi.name).c_str()); +} + +void USB::CheckForConfigChanges(const Pcsx2Config& old_config) +{ + static_assert(Pcsx2Config::USBOptions::NUM_PORTS == NUM_PORTS); + + for (u32 port = 0; port < NUM_PORTS; port++) + { + if (EmuConfig.USB.Ports[port] == old_config.USB.Ports[port]) + { + UpdateDevice(port); + continue; + } + + if (s_usb_device[port]) + DestroyDevice(port); + CreateDevice(port); + } } diff --git a/pcsx2/USB/USB.h b/pcsx2/USB/USB.h index bc9e5c4b60..5be5db0f1b 100644 --- a/pcsx2/USB/USB.h +++ b/pcsx2/USB/USB.h @@ -15,32 +15,85 @@ #pragma once -#include -#include +#include #include -#include +#include +#include +#include +#include "gsl/span" + +#include "Config.h" #include "SaveState.h" +class SettingsInterface; + +namespace USB +{ + enum : u32 + { + NUM_PORTS = 2, + }; + + s32 DeviceTypeNameToIndex(const std::string_view& device); + const char* DeviceTypeIndexToName(s32 device); + + std::vector> GetDeviceTypes(); + const char* GetDeviceName(const std::string_view& device); + std::vector GetDeviceSubtypes(const std::string_view& device); + gsl::span GetDeviceBindings(const std::string_view& device, u32 subtype); + gsl::span GetDeviceSettings(const std::string_view& device, u32 subtype); + + gsl::span GetDeviceBindings(u32 port); + float GetDeviceBindValue(u32 port, u32 bind_index); + void SetDeviceBindValue(u32 port, u32 bind_index, float value); + + /// Called when a new input device is connected. + void InputDeviceConnected(const std::string_view& identifier); + + /// Called when an input device is disconnected. + void InputDeviceDisconnected(const std::string_view& identifier); + + std::string GetConfigDevice(const SettingsInterface& si, u32 port); + u32 GetConfigSubType(const SettingsInterface& si, u32 port, const std::string_view& devname); + + /// Returns the configuration key for the specified bind and device type. + std::string GetConfigBindKey(const std::string_view& device, const std::string_view& bind_name); + + /// Performs automatic controller mapping with the provided list of generic mappings. + bool MapDevice(SettingsInterface& si, u32 port, const std::vector>& mapping); + + /// Clears all bindings for a given port. + void ClearPortBindings(SettingsInterface& si, u32 port); + + /// Identifies any device/subtype changes and recreates devices. + void CheckForConfigChanges(const Pcsx2Config& old_config); + + /// Reads a device-specific configuration boolean. + bool GetConfigBool(SettingsInterface& si, u32 port, const char* devname, const char* key, bool default_value); + + /// Reads a device-specific configuration integer. + s32 GetConfigInt(SettingsInterface& si, u32 port, const char* devname, const char* key, s32 default_value); + + /// Reads a device-specific configuration floating-point value. + float GetConfigFloat(SettingsInterface& si, u32 port, const char* devname, const char* key, float default_value); + + /// Reads a device-specific configuration string. + std::string GetConfigString(SettingsInterface& si, u32 port, const char* devname, const char* key, const char* default_value = ""); +} // namespace USB + +std::string USBGetConfigSection(int port); + struct WindowInfo; // --------------------------------------------------------------------- -#define USBdefs - -extern u8* ram; - -// --------------------------------------------------------------------- - -void USBconfigure(); - -void DestroyDevices(); -void CreateDevices(); s32 USBinit(); void USBasync(u32 cycles); void USBshutdown(); void USBclose(); -s32 USBopen(const WindowInfo& wi); +bool USBopen(); +void USBreset(); s32 USBfreeze(FreezeAction mode, freezeData* data); u8 USBread8(u32 addr); @@ -51,12 +104,3 @@ void USBwrite16(u32 addr, u16 value); void USBwrite32(u32 addr, u32 value); void USBsetRAM(void* mem); - -extern FILE* usbLog; -s64 get_clock(); - -/* usb-pad-raw.cpp */ -#if _WIN32 -#include "common/RedtapeWindows.h" -extern HWND gsWnd; -#endif diff --git a/pcsx2/USB/USBNull.cpp b/pcsx2/USB/USBNull.cpp index bee5ec834b..9c7d7b5695 100644 --- a/pcsx2/USB/USBNull.cpp +++ b/pcsx2/USB/USBNull.cpp @@ -15,19 +15,15 @@ #include "PrecompiledHeader.h" -#include "USB.h" - -u8* ram = nullptr; +#include "USBNull.h" void USBconfigure() {} -void DestroyDevices() {} -void CreateDevices() {} - s32 USBinit() { return 0; } void USBasync(u32 cycles) {} void USBshutdown() {} void USBclose() {} +void USBreset() {} s32 USBopen(const WindowInfo& wi) { return 0; } s32 USBfreeze(FreezeAction mode, freezeData* data) { return 0; } @@ -37,8 +33,3 @@ u32 USBread32(u32 addr) { return 0; } void USBwrite8(u32 addr, u8 value) {} void USBwrite16(u32 addr, u16 value) {} void USBwrite32(u32 addr, u32 value) {} - -void USBsetRAM(void* mem) { ram = static_cast(mem); } - -FILE* usbLog = nullptr; -s64 get_clock() { return 0; }; diff --git a/pcsx2/USB/linux/util.h b/pcsx2/USB/USBNull.h similarity index 60% rename from pcsx2/USB/linux/util.h rename to pcsx2/USB/USBNull.h index 6fda7cda24..2148c002a7 100644 --- a/pcsx2/USB/linux/util.h +++ b/pcsx2/USB/USBNull.h @@ -14,7 +14,29 @@ */ #pragma once -#include -bool file_exists(std::string path); -bool dir_exists(std::string path); +#include +#include +#include +#include + +#include "SaveState.h" + +struct WindowInfo; + +void USBconfigure(); + +s32 USBinit(); +void USBasync(u32 cycles); +void USBshutdown(); +void USBclose(); +void USBreset(); +s32 USBopen(const WindowInfo& wi); +s32 USBfreeze(FreezeAction mode, freezeData* data); + +u8 USBread8(u32 addr); +u16 USBread16(u32 addr); +u32 USBread32(u32 addr); +void USBwrite8(u32 addr, u8 value); +void USBwrite16(u32 addr, u16 value); +void USBwrite32(u32 addr, u32 value); diff --git a/pcsx2/USB/Win32/Config_usb.cpp b/pcsx2/USB/Win32/Config_usb.cpp deleted file mode 100644 index 4c57fad3ac..0000000000 --- a/pcsx2/USB/Win32/Config_usb.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "gui/AppCoreThread.h" -#include "USB/USB.h" -#include "resource_usb.h" -#include "Config_usb.h" -#include "USB/deviceproxy.h" -#include "USB/usb-pad/padproxy.h" -#include "USB/usb-mic/audiodeviceproxy.h" -#include "USB/configuration.h" -#include "USB/shared/inifile_usb.h" - -HINSTANCE hInstUSB; - -void SysMessageA(const char* fmt, ...) -{ - va_list list; - char tmp[512]; - - va_start(list, fmt); - vsprintf_s(tmp, 512, fmt, list); - va_end(list); - MessageBoxA(0, tmp, "USB Msg", 0); -} - -void SysMessageW(const wchar_t* fmt, ...) -{ - va_list list; - wchar_t tmp[512]; - - va_start(list, fmt); - vswprintf_s(tmp, 512, fmt, list); - va_end(list); - MessageBoxW(0, tmp, L"USB Msg", 0); -} - -void SelChangedAPI(HWND hW, int port) -{ - int sel = SendDlgItemMessage(hW, port ? IDC_COMBO_API1_USB : IDC_COMBO_API2_USB, CB_GETCURSEL, 0, 0); - int devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1_USB : IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - if (devtype == 0) - return; - devtype--; - auto& rd = RegisterDevice::instance(); - auto devName = rd.Name(devtype); - auto apis = rd.Device(devtype)->ListAPIs(); - auto it = apis.begin(); - std::advance(it, sel); - changedAPIs[std::make_pair(port, devName)] = *it; -} - -void SelChangedSubtype(HWND hW, int port) -{ - int sel = SendDlgItemMessage(hW, port ? IDC_COMBO_WHEEL_TYPE1_USB : IDC_COMBO_WHEEL_TYPE2_USB, CB_GETCURSEL, 0, 0); - int devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1_USB : IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - if (devtype == 0) - return; - devtype--; - auto& rd = RegisterDevice::instance(); - auto devName = rd.Name(devtype); - changedSubtype[std::make_pair(port, devName)] = sel; -} - -void PopulateAPIs(HWND hW, int port) -{ - SendDlgItemMessage(hW, port ? IDC_COMBO_API1_USB : IDC_COMBO_API2_USB, CB_RESETCONTENT, 0, 0); - int devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1_USB : IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - if (devtype == 0) - return; - devtype--; - auto& rd = RegisterDevice::instance(); - auto dev = rd.Device(devtype); - auto devName = rd.Name(devtype); - auto apis = dev->ListAPIs(); - - std::string selApi = GetSelectedAPI(std::make_pair(port, devName)); - - std::string var; - std::wstring tmp; - if (!LoadSetting(nullptr, port, rd.Name(devtype), N_DEVICE_API, tmp)) - { - if (apis.begin() != apis.end()) - { - selApi = *apis.begin(); - changedAPIs[std::make_pair(port, devName)] = selApi; - } - } - - var = wstr_to_str(tmp); - int i = 0, sel = 0; - for (auto& api : apis) - { - auto name = dev->LongAPIName(api); - if (!name) - continue; - SendDlgItemMessageW(hW, port ? IDC_COMBO_API1_USB : IDC_COMBO_API2_USB, CB_ADDSTRING, 0, (LPARAM)name); - if (api == var) - sel = i; - i++; - } - SendDlgItemMessage(hW, port ? IDC_COMBO_API1_USB : IDC_COMBO_API2_USB, CB_SETCURSEL, sel, 0); -} - -void PopulateSubType(HWND hW, int port) -{ - SendDlgItemMessage(hW, port ? IDC_COMBO_WHEEL_TYPE1_USB : IDC_COMBO_WHEEL_TYPE2_USB, CB_RESETCONTENT, 0, 0); - int devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1_USB : IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - if (devtype == 0) - return; - devtype--; - auto& rd = RegisterDevice::instance(); - auto dev = rd.Device(devtype); - auto devName = rd.Name(devtype); - - int sel = 0; - if (!LoadSetting(nullptr, port, dev->TypeName(), N_DEV_SUBTYPE, sel)) - { - changedSubtype[std::make_pair(port, devName)] = sel; - } - - for (auto subtype : dev->SubTypes()) - { - SendDlgItemMessageA(hW, port ? IDC_COMBO_WHEEL_TYPE1_USB : IDC_COMBO_WHEEL_TYPE2_USB, CB_ADDSTRING, 0, (LPARAM)subtype.c_str()); - } - SendDlgItemMessage(hW, port ? IDC_COMBO_WHEEL_TYPE1_USB : IDC_COMBO_WHEEL_TYPE2_USB, CB_SETCURSEL, sel, 0); -} - -BOOL CALLBACK ConfigureDlgProcUSB(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - - int port; - switch (uMsg) - { - case WM_INITDIALOG: - LoadConfig(); - CheckDlgButton(hW, IDC_LOGGING_USB, conf.Log); - //Selected emulated devices. - SendDlgItemMessageA(hW, IDC_COMBO1_USB, CB_ADDSTRING, 0, (LPARAM) "None"); - SendDlgItemMessageA(hW, IDC_COMBO2_USB, CB_ADDSTRING, 0, (LPARAM) "None"); - - { - auto& rd = RegisterDevice::instance(); - int i = 0, p1 = 0, p2 = 0; - for (auto& name : rd.Names()) - { - i++; //jump over "None" - auto dev = rd.Device(name); - SendDlgItemMessageW(hW, IDC_COMBO1_USB, CB_ADDSTRING, 0, (LPARAM)dev->Name()); - SendDlgItemMessageW(hW, IDC_COMBO2_USB, CB_ADDSTRING, 0, (LPARAM)dev->Name()); - - //Port 1 aka device/player 1 - if (conf.Port[1] == name) - p1 = i; - //Port 0 aka device/player 2 - if (conf.Port[0] == name) - p2 = i; - } - SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_SETCURSEL, p1, 0); - SendDlgItemMessage(hW, IDC_COMBO2_USB, CB_SETCURSEL, p2, 0); - PopulateAPIs(hW, 0); - PopulateAPIs(hW, 1); - PopulateSubType(hW, 0); - PopulateSubType(hW, 1); - } - - return TRUE; - break; - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case CBN_SELCHANGE: - switch (LOWORD(wParam)) - { - case IDC_COMBO_API1_USB: - case IDC_COMBO_API2_USB: - port = (LOWORD(wParam) == IDC_COMBO_API1_USB) ? 1 : 0; - SelChangedAPI(hW, port); - break; - case IDC_COMBO1_USB: - case IDC_COMBO2_USB: - port = (LOWORD(wParam) == IDC_COMBO1_USB) ? 1 : 0; - PopulateAPIs(hW, port); - PopulateSubType(hW, port); - break; - case IDC_COMBO_WHEEL_TYPE1_USB: - case IDC_COMBO_WHEEL_TYPE2_USB: - port = (LOWORD(wParam) == IDC_COMBO_WHEEL_TYPE1_USB) ? 1 : 0; - SelChangedSubtype(hW, port); - break; - } - break; - case BN_CLICKED: - switch (LOWORD(wParam)) - { - case IDC_CONFIGURE1_USB: - case IDC_CONFIGURE2_USB: - { - LRESULT devtype, apitype; - port = (LOWORD(wParam) == IDC_CONFIGURE1_USB) ? 1 : 0; - devtype = SendDlgItemMessage(hW, port ? IDC_COMBO1_USB : IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - apitype = SendDlgItemMessage(hW, port ? IDC_COMBO_API1_USB : IDC_COMBO_API2_USB, CB_GETCURSEL, 0, 0); - - if (devtype > 0) - { - devtype--; - auto device = RegisterDevice::instance().Device(devtype); - if (device) - { - auto list = device->ListAPIs(); - auto it = list.begin(); - std::advance(it, apitype); - if (it == list.end()) - break; - std::string api = *it; - Win32Handles handles(hInstUSB, hW); - if (device->Configure(port, api, &handles) == RESULT_FAILED) - SysMessage(TEXT("Some settings may not have been saved!\n")); - } - } - } - break; - case IDCANCEL: - EndDialog(hW, TRUE); - return TRUE; - case IDOK: - conf.Log = IsDlgButtonChecked(hW, IDC_LOGGING_USB); - { - auto& regInst = RegisterDevice::instance(); - int i; - //device type - i = SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_GETCURSEL, 0, 0); - conf.Port[1] = regInst.Name(i - 1); - i = SendDlgItemMessage(hW, IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - conf.Port[0] = regInst.Name(i - 1); - } - - SaveConfig(); - CreateDevices(); - EndDialog(hW, RESULT_OK); - return TRUE; - } - } - } - - return FALSE; -} - -void USBconfigure() -{ - ScopedCoreThreadPause paused_core; - USBsetSettingsDir(); - RegisterDevice::Register(); - DialogBox(hInstUSB, - MAKEINTRESOURCE(IDD_CONFIG_USB), - GetActiveWindow(), - (DLGPROC)ConfigureDlgProcUSB); - paused_core.AllowResume(); -} diff --git a/pcsx2/USB/Win32/Config_usb.h b/pcsx2/USB/Win32/Config_usb.h deleted file mode 100644 index 9259d8bc0e..0000000000 --- a/pcsx2/USB/Win32/Config_usb.h +++ /dev/null @@ -1,45 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef WIN32_H -#define WIN32_H -#include - -typedef struct Win32Handles -{ - HINSTANCE hInst; - HWND hWnd; - Win32Handles(HINSTANCE i, HWND w) - : hInst(i) - , hWnd(w) - { - } -} Win32Handles; - -#define CHECKED_SET_MAX_INT(var, hDlg, nIDDlgItem, bSigned, min, max) \ - do \ - { \ - /*CheckControlTextIsNumber(GetDlgItem(hDlg, nIDDlgItem), bSigned, 0);*/ \ - var = GetDlgItemInt(hDlg, nIDDlgItem, NULL, bSigned); \ - if (var < min) \ - var = min; \ - else if (var > max) \ - { \ - var = max; \ - SetDlgItemInt(hDlg, nIDDlgItem, var, bSigned); \ - SendMessage(GetDlgItem(hDlg, nIDDlgItem), EM_SETSEL, -2, -2); \ - } \ - } while (0) -#endif diff --git a/pcsx2/USB/Win32/USBDialog.rc b/pcsx2/USB/Win32/USBDialog.rc deleted file mode 100644 index 46ae18a270..0000000000 --- a/pcsx2/USB/Win32/USBDialog.rc +++ /dev/null @@ -1,206 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource_usb.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "WinResrc.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Anglais (États-Unis) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_DLGMSD_USB DIALOGEX 0, 0, 316, 74 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "USB Mass Storage Device" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,204,54,50,14 - PUSHBUTTON "Cancel",IDCANCEL,258,54,50,14 - GROUPBOX "Image file path",IDC_STATIC_USB,6,6,300,36 - EDITTEXT IDC_EDIT1_USB,12,18,234,14,ES_AUTOHSCROLL,WS_EX_ACCEPTFILES - PUSHBUTTON "Browse",IDC_BUTTON1_USB,252,18,50,14 -END - -IDD_CONFIG_USB DIALOGEX 0, 0, 257, 205 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "USB Settings" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,194,184,50,14 - DEFPUSHBUTTON "OK",IDOK,138,184,50,14 - GROUPBOX "Device type",IDC_STATIC_USB,6,6,246,48 - LTEXT "Port 1:",IDC_STATIC_USB,19,19,23,8 - COMBOBOX IDC_COMBO1_USB,48,17,198,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Port 2:",IDC_STATIC_USB,19,37,23,8 - COMBOBOX IDC_COMBO2_USB,48,35,198,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - GROUPBOX "Device API",IDC_STATIC_USB,6,54,246,48 - LTEXT "Port 1:",IDC_STATIC_USB,19,67,23,8 - COMBOBOX IDC_COMBO_API1_USB,48,65,144,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - DEFPUSHBUTTON "Configure",IDC_CONFIGURE1_USB,198,65,49,14 - LTEXT "Port 2:",IDC_STATIC_USB,19,85,23,8 - COMBOBOX IDC_COMBO_API2_USB,48,83,144,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - DEFPUSHBUTTON "Configure",IDC_CONFIGURE2_USB,198,83,49,14 - CONTROL "Enable Logging (for developer use only)",IDC_LOGGING_USB, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,105,144,12 - GROUPBOX "Emulated device",IDC_STATIC_USB,6,131,246,48 - LTEXT "Port 1:",IDC_STATIC_USB,19,144,23,8 - COMBOBOX IDC_COMBO_WHEEL_TYPE1_USB,48,142,198,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Port 2:",IDC_STATIC_USB,19,162,23,8 - COMBOBOX IDC_COMBO_WHEEL_TYPE2_USB,48,160,198,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP -END - -IDD_DLGWASAPI_USB DIALOGEX 0, 0, 287, 230 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "WASAPI Settings" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,174,210,50,14 - PUSHBUTTON "Cancel",IDCANCEL,228,210,50,14 - GROUPBOX "Audio Input",IDC_STATIC_USB,6,6,270,54 - LTEXT "Player 1:",IDC_STATIC_USB,12,18,30,8 - LTEXT "Player 2:",IDC_STATIC_USB,12,36,30,8 - COMBOBOX IDC_COMBO1_USB,48,18,216,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_COMBO2_USB,48,36,216,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "1 ms",IDC_STATIC_USB,18,138,16,8 - LTEXT "1000 ms",IDC_STATIC_USB,240,138,28,8 - GROUPBOX "Input Buffering",IDC_STATIC_USB,6,108,270,46 - CONTROL "",IDC_SLIDER1_USB,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,18,121,252,15 - EDITTEXT IDC_BUFFER1_USB,122,138,40,12,ES_AUTOHSCROLL | ES_NUMBER - GROUPBOX "Audio Output",IDC_STATIC_USB,6,66,270,36 - COMBOBOX IDC_COMBO3_USB,48,78,216,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "1 ms",IDC_STATIC_USB,18,186,16,8 - LTEXT "1000 ms",IDC_STATIC_USB,240,186,28,8 - GROUPBOX "Output Buffering",IDC_STATIC_USB,6,156,270,46 - CONTROL "",IDC_SLIDER2_USB,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,18,169,252,15 - EDITTEXT IDC_BUFFER2_USB,122,186,40,12,ES_AUTOHSCROLL | ES_NUMBER -END - -IDD_DLG_EYETOY_USB DIALOGEX 0, 0, 309, 49 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "EyeToy Settings" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - LTEXT "Device:",IDC_STATIC_USB,7,8,27,8 - COMBOBOX IDC_COMBO1_USB,40,7,262,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - DEFPUSHBUTTON "OK",IDOK,198,28,50,14 - PUSHBUTTON "Cancel",IDCANCEL,252,28,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_DLGMIC_USB AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_CONFIG_USB AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DLGWASAPI_USB AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DLG_EYETOY_USB AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_DLGMSD_USB, DIALOG - BEGIN - END - - IDD_CONFIG_USB, DIALOG - BEGIN - END - - IDD_DLGWASAPI_USB, DIALOG - BEGIN - END - - IDD_DLG_EYETOY_USB, DIALOG - BEGIN - END -END -#endif // APSTUDIO_INVOKED - -#endif // Anglais (États-Unis) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// Espagnol (Argentine) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESS) -LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource_usb.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""WinResrc.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // Espagnol (Argentine) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/pcsx2/USB/Win32/guid.cpp b/pcsx2/USB/Win32/guid.cpp deleted file mode 100644 index 927722327e..0000000000 --- a/pcsx2/USB/Win32/guid.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include -#include -#include -#include diff --git a/pcsx2/USB/Win32/resource_usb.h b/pcsx2/USB/Win32/resource_usb.h deleted file mode 100644 index 5368e9d6ae..0000000000 --- a/pcsx2/USB/Win32/resource_usb.h +++ /dev/null @@ -1,41 +0,0 @@ -//{{NO_DEPENDENCIES}} -// fichier Include Microsoft Visual C++. -// Utilisé par USBDialog.rc -// -#define IDC_CONFIGURE1_USB 3 -#define IDC_CONFIGURE2_USB 4 -#define IDD_CONFDLG_USB 101 -#define IDD_CONFIG_USB 101 -#define IDD_DLGMSD_USB 106 -#define IDD_DLGWASAPI_USB 107 -#define IDD_DLG_EYETOY_USB 108 -#define IDC_LOGGING_USB 1007 -#define IDC_COMBO1_USB 1008 -#define IDC_COMBO2_USB 1009 -#define IDC_LIST1_USB 1010 -#define IDC_COMBO_API1_USB 1010 -#define IDC_COMBO3_USB 1010 -#define IDC_COMBO_FFB_USB 1011 -#define IDC_COMBO_API2_USB 1011 -#define IDC_BUTTON1_USB 1012 -#define IDC_DFP_PASS_USB 1013 -#define IDC_EDIT1_USB 1015 -#define IDC_COMBO_WHEEL_TYPE1_USB 1037 -#define IDC_COMBO_WHEEL_TYPE2_USB 1038 -#define IDC_SLIDER1_USB 1039 -#define IDC_BUFFER1_USB 1040 -#define IDC_COMBOMICAPI_USB 1041 -#define IDC_SLIDER2_USB 1041 -#define IDC_BUFFER2_USB 1042 -#define IDC_STATIC_USB -1 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1042 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/pcsx2/USB/configuration.cpp b/pcsx2/USB/configuration.cpp deleted file mode 100644 index c40efe6230..0000000000 --- a/pcsx2/USB/configuration.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "deviceproxy.h" -#include "configuration.h" -#include "shared/inifile_usb.h" -#include "platcompat.h" -#include "Config.h" -#include "common/Path.h" -#include "common/StringUtil.h" -#include "gui/StringHelpers.h" -#include -#include - -std::map, std::string> changedAPIs; -std::map, int> changedSubtype; -wxString iniFileUSB(L"USB.ini"); -static TSTDSTRING usb_path; -TSTDSTRING IniPath; // default path, just in case -TSTDSTRING LogDir; -CIniFile ciniFile; -bool USBpathSet = false; - -void USBsetSettingsDir() -{ - if(!USBpathSet) - { - IniPath = StringUtil::UTF8StringToWxString(Path::Combine(EmuFolders::Settings, "USB.ini")); // default path, just in case - USBpathSet = true; - } -} - -void USBsetLogDir(const char* dir) -{ -#ifdef _WIN32 - LogDir = str_to_wstr(dir); -#else - LogDir = dir; -#endif -} - -std::string GetSelectedAPI(const std::pair& pair) -{ - USBsetSettingsDir(); - auto it = changedAPIs.find(pair); - if (it != changedAPIs.end()) - return it->second; - return std::string(); -} - -int GetSelectedSubtype(const std::pair& pair) -{ - auto it = changedSubtype.find(pair); - if (it != changedSubtype.end()) - return it->second; - return 0; -} - -bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, TSTDSTRING& value) -{ - USBsetSettingsDir(); - CIniKey* key; -#ifdef _WIN32 - auto sect = ciniFile.GetSection(section); - if (sect && (key = sect->GetKey(param))) - { - value = key->GetValue(); - return true; - } -#else - auto sect = ciniFile.GetSection(str_to_wstr(section)); - if (sect && (key = sect->GetKey(str_to_wstr(param)))) - { - value = wstr_to_str(key->GetValue()); - return true; - } -#endif - return false; -} - -bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t& value) -{ - USBsetSettingsDir(); - CIniKey* key; -#ifdef _WIN32 - auto sect = ciniFile.GetSection(section); - if (sect && (key = sect->GetKey(param))) - { - try - { - value = std::stoi(key->GetValue()); - return true; - } -#else - auto sect = ciniFile.GetSection(str_to_wstr(section)); - if (sect && (key = sect->GetKey(str_to_wstr(param)))) - { - try - { - value = std::stoi(key->GetValue()); - return true; - } -#endif - catch (std::exception& err) - { - DevCon.WriteLn("%s\n", err.what()); - } - } - return false; -} - -bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const TSTDSTRING& value) -{ - USBsetSettingsDir(); -#ifdef _WIN32 - ciniFile.SetKeyValue(section, param, value); -#else - ciniFile.SetKeyValue(str_to_wstr(section), str_to_wstr(param), str_to_wstr(value)); -#endif - return true; -} - -bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t value) -{ - USBsetSettingsDir(); -#ifdef _WIN32 - ciniFile.SetKeyValue(section, param, TSTDTOSTRING(value)); -#else - ciniFile.SetKeyValue(str_to_wstr(section), str_to_wstr(param), str_to_wstr(TSTDTOSTRING(value))); -#endif - return true; -} - -void SaveConfig() -{ - USBsetSettingsDir(); -#ifdef _WIN32 - SaveSetting(L"MAIN", L"log", conf.Log); -#else - SaveSetting("MAIN", "log", conf.Log); -#endif - -#ifdef _WIN32 - SaveSetting(nullptr, 0, N_DEVICE_PORT, N_DEVICE, str_to_wstr(conf.Port[0])); - SaveSetting(nullptr, 1, N_DEVICE_PORT, N_DEVICE, str_to_wstr(conf.Port[1])); -#else - SaveSetting(nullptr, 0, N_DEVICE_PORT, N_DEVICE, conf.Port[0]); - SaveSetting(nullptr, 1, N_DEVICE_PORT, N_DEVICE, conf.Port[1]); -#endif - - for (auto& k : changedAPIs) - { -#ifdef _WIN32 - SaveSetting(nullptr, k.first.first, k.first.second, N_DEVICE_API, str_to_wstr(k.second)); -#else - SaveSetting(nullptr, k.first.first, k.first.second, N_DEVICE_API, k.second); -#endif - } - - for (auto& k : changedSubtype) - { - SaveSetting(nullptr, k.first.first, k.first.second, N_DEV_SUBTYPE, k.second); - } - -#ifdef _WIN32 - bool ret = ciniFile.Save(IniPath); -#else - [[maybe_unused]]bool ret = ciniFile.Save(str_to_wstr(IniPath)); -#endif -} - -void LoadConfig() -{ - USBsetSettingsDir(); - - static bool loaded = false; - if (loaded) - return; - loaded = true; - -#ifdef _WIN32 - ciniFile.Load(IniPath); - LoadSetting(L"MAIN", L"log", conf.Log); -#else - ciniFile.Load(str_to_wstr(IniPath)); - LoadSetting("MAIN", "log", conf.Log); -#endif - -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, 0, N_DEVICE_PORT, N_DEVICE, tmp); - conf.Port[0] = wstr_to_str(tmp); - LoadSetting(nullptr, 1, N_DEVICE_PORT, N_DEVICE, tmp); - conf.Port[1] = wstr_to_str(tmp); -#else - LoadSetting(nullptr, 0, N_DEVICE_PORT, N_DEVICE, conf.Port[0]); - LoadSetting(nullptr, 1, N_DEVICE_PORT, N_DEVICE, conf.Port[1]); -#endif - - auto& instance = RegisterDevice::instance(); - - for (int i = 0; i < 2; i++) - { - std::string api; -#ifdef _WIN32 - LoadSetting(nullptr, i, conf.Port[i], N_DEVICE_API, tmp); - api = wstr_to_str(tmp); -#else - LoadSetting(nullptr, i, conf.Port[i], N_DEVICE_API, api); -#endif - auto dev = instance.Device(conf.Port[i]); - - if (dev) - { - if (!dev->IsValidAPI(api)) - { - api = ""; - const auto& apis = dev->ListAPIs(); - if (!apis.empty()) - api = *apis.begin(); - - } - } - - if (api.size()) - changedAPIs[std::make_pair(i, conf.Port[i])] = api; - - int subtype = 0; - LoadSetting(nullptr, i, conf.Port[i], N_DEV_SUBTYPE, subtype); - changedSubtype[std::make_pair(i, conf.Port[i])] = subtype; - } -} - -void ClearSection(const TCHAR* section) -{ - USBsetSettingsDir(); -#ifdef _WIN32 - auto s = ciniFile.GetSection(section); -#else - auto s = ciniFile.GetSection(str_to_wstr(section)); -#endif - if (s) - { - s->RemoveAllKeys(); - } -} - -void RemoveSection(const char* dev_type, int port, const std::string& key) -{ - USBsetSettingsDir(); - TSTDSTRING tkey; - tkey.assign(key.begin(), key.end()); - - TSTDSTRINGSTREAM section; - if (dev_type) - section << dev_type << TEXT(" "); - section << tkey << TEXT(" ") << port; - TSTDSTRING str = section.str(); - -#ifdef _WIN32 - ciniFile.RemoveSection(section.str()); -#else - ciniFile.RemoveSection(str_to_wstr(section.str())); -#endif -} diff --git a/pcsx2/USB/configuration.h b/pcsx2/USB/configuration.h deleted file mode 100644 index 4d51038832..0000000000 --- a/pcsx2/USB/configuration.h +++ /dev/null @@ -1,162 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once - -#include "platcompat.h" -#include -#include -#include -#include -#include - -#define RESULT_CANCELED 0 -#define RESULT_OK 1 -#define RESULT_FAILED 2 - -// freeze modes: -#define FREEZE_LOAD 0 -#define FREEZE_SAVE 1 -#define FREEZE_SIZE 2 - -// Device-level config related defines -#define S_DEVICE_API TEXT("Device API") -#define S_WHEEL_TYPE TEXT("Wheel type") -#define S_CONFIG_PATH TEXT("Path") - -#define N_DEVICE_API TEXT("device_api") -#define N_DEVICES TEXT("devices") -#define N_DEVICE TEXT("device") -#define N_WHEEL_PT TEXT("wheel_pt") -#define N_DEVICE_PORT0 TEXT("port_0") -#define N_DEVICE_PORT1 TEXT("port_1") -#define N_DEVICE_PORT "port" -#define N_DEV_SUBTYPE TEXT("subtype") -#define N_CONFIG_PATH TEXT("path") - -#define PLAYER_TWO_PORT 0 -#define PLAYER_ONE_PORT 1 -#define USB_PORT PLAYER_ONE_PORT - -struct Config -{ - int Log; - std::string Port[2]; - - Config(); -}; - -extern Config conf; -void SaveConfig(); -void LoadConfig(); -void ClearSection(const TCHAR* section); -void RemoveSection(const char* dev_type, int port, const std::string& key); - -extern TSTDSTRING IniPath; -extern TSTDSTRING LogDir; -extern std::map, std::string> changedAPIs; -extern std::map, int> changedSubtype; -std::string GetSelectedAPI(const std::pair& pair); -int GetSelectedSubtype(const std::pair& pair); - -bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, TSTDSTRING& value); -bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t& value); - -bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const TSTDSTRING& value); -bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, int32_t value); - -#ifdef _UNICODE -bool LoadSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, std::string& value); -bool SaveSettingValue(const TSTDSTRING& ini, const TSTDSTRING& section, const TCHAR* param, const std::string& value); -#endif - -void USBsetSettingsDir(); -void USBsetLogDir(const char* dir); - -template -bool LoadSetting(const char* dev_type, int port, const std::string& key, const TCHAR* name, Type& var) -{ - bool ret = false; - if (key.empty()) - { - return false; - } - - TSTDSTRING tkey; - tkey.assign(key.begin(), key.end()); - - TSTDSTRINGSTREAM section; - if (dev_type) - section << dev_type << TEXT(" "); - section << tkey << TEXT(" ") << port; - TSTDSTRING str = section.str(); - - ret = LoadSettingValue(IniPath, str, name, var); - return ret; -} - -template -bool LoadSetting(const TCHAR* section, const TCHAR* key, Type& var) -{ - bool ret = false; - ret = LoadSettingValue(IniPath, section, key, var); - return ret; -} - -/** - * - * [devices] - * portX = pad - * - * [pad X] - * api = joydev - * - * [joydev X] - * button0 = 1 - * button1 = 2 - * ... - * - * */ -template -bool SaveSetting(const char* dev_type, int port, const std::string& key, const TCHAR* name, const Type var) -{ - bool ret = false; - if (key.empty()) - { - return false; - } - - TSTDSTRING tkey; - tkey.assign(key.begin(), key.end()); - - TSTDSTRINGSTREAM section; - if (dev_type) - section << dev_type << TEXT(" "); - section << tkey << TEXT(" ") << port; - TSTDSTRING str = section.str(); - - - ret = SaveSettingValue(IniPath, str, name, var); - return ret; -} - -template -bool SaveSetting(const TCHAR* section, const TCHAR* key, const Type var) -{ - bool ret = false; - - ret = SaveSettingValue(IniPath, section, key, var); - return ret; -} diff --git a/pcsx2/USB/device_init.cpp b/pcsx2/USB/device_init.cpp deleted file mode 100644 index 7cbf26637b..0000000000 --- a/pcsx2/USB/device_init.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "deviceproxy.h" -#include "usb-eyetoy/usb-eyetoy-webcam.h" -#include "usb-hid/usb-hid.h" -#include "usb-mic/usb-headset.h" -#include "usb-mic/usb-mic-singstar.h" -#include "usb-msd/usb-msd.h" -#include "usb-pad/usb-pad.h" -#include "usb-printer/usb-printer.h" - -void RegisterDevice::Register() -{ - auto& inst = RegisterDevice::instance(); - if (inst.Map().size()) // FIXME Don't clear proxies, singstar keeps a copy to uninit audio - return; - inst.Add(DEVTYPE_PAD, new DeviceProxy()); - inst.Add(DEVTYPE_MSD, new DeviceProxy()); - inst.Add(DEVTYPE_SINGSTAR, new DeviceProxy()); - inst.Add(DEVTYPE_LOGITECH_MIC, new DeviceProxy()); - inst.Add(DEVTYPE_LOGITECH_HEADSET, new DeviceProxy()); - inst.Add(DEVTYPE_HIDKBD, new DeviceProxy()); - inst.Add(DEVTYPE_HIDMOUSE, new DeviceProxy()); - inst.Add(DEVTYPE_RBKIT, new DeviceProxy()); - inst.Add(DEVTYPE_BUZZ, new DeviceProxy()); -#ifdef _WIN32 - inst.Add(DEVTYPE_GAMETRAK, new DeviceProxy()); - inst.Add(DEVTYPE_REALPLAY, new DeviceProxy()); -#endif - inst.Add(DEVTYPE_EYETOY, new DeviceProxy()); - inst.Add(DEVTYPE_BEATMANIA_DADADA, new DeviceProxy()); - inst.Add(DEVTYPE_SEGA_SEAMIC, new DeviceProxy()); - inst.Add(DEVTYPE_PRINTER, new DeviceProxy()); - inst.Add(DEVTYPE_KEYBOARDMANIA, new DeviceProxy()); - - RegisterAPIs(); -} - -void RegisterDevice::Unregister() -{ - /*for (auto& i: registerDeviceMap) - delete i.second;*/ - registerDeviceMap.clear(); - delete registerDevice; - registerDevice = nullptr; - - UnregisterAPIs(); -} diff --git a/pcsx2/USB/deviceproxy.cpp b/pcsx2/USB/deviceproxy.cpp index 42dd85f802..6c59733aec 100644 --- a/pcsx2/USB/deviceproxy.cpp +++ b/pcsx2/USB/deviceproxy.cpp @@ -15,25 +15,85 @@ #include "PrecompiledHeader.h" #include "deviceproxy.h" -#include "usb-pad/padproxy.h" -#include "usb-mic/audiodeviceproxy.h" -#include "usb-hid/hidproxy.h" -#include "usb-eyetoy/videodeviceproxy.h" +#include "usb-eyetoy/usb-eyetoy-webcam.h" +#include "usb-hid/usb-hid.h" +#include "usb-mic/usb-headset.h" +#include "usb-mic/usb-mic-singstar.h" +#include "usb-msd/usb-msd.h" +#include "usb-pad/usb-pad.h" +#include "usb-printer/usb-printer.h" +#include "usb-lightgun/guncon2.h" RegisterDevice* RegisterDevice::registerDevice = nullptr; -void RegisterAPIs() +DeviceProxy::~DeviceProxy() = default; + +std::vector DeviceProxy::SubTypes() const { - usb_pad::RegisterPad::Register(); - usb_mic::RegisterAudioDevice::Register(); - usb_hid::RegisterUsbHID::Register(); - usb_eyetoy::RegisterVideoDevice::Register(); + return {}; } -void UnregisterAPIs() +gsl::span DeviceProxy::Bindings(u32 subtype) const { - usb_pad::RegisterPad::instance().Clear(); - usb_mic::RegisterAudioDevice::instance().Clear(); - usb_hid::RegisterUsbHID::instance().Clear(); - usb_eyetoy::RegisterVideoDevice::instance().Clear(); + return {}; +} + +gsl::span DeviceProxy::Settings(u32 subtype) const +{ + return {}; +} + +float DeviceProxy::GetBindingValue(const USBDevice* dev, u32 bind) const +{ + return 0.0f; +} + +void DeviceProxy::SetBindingValue(USBDevice* dev, u32 bind, float value) const +{ +} + +bool DeviceProxy::Freeze(USBDevice* dev, StateWrapper& sw) const +{ + return false; +} + +void DeviceProxy::UpdateSettings(USBDevice* dev, SettingsInterface& si) const +{ +} + +void DeviceProxy::InputDeviceConnected(USBDevice* dev, const std::string_view& identifier) const +{ +} + +void DeviceProxy::InputDeviceDisconnected(USBDevice* dev, const std::string_view& identifier) const +{ +} + +void RegisterDevice::Register() +{ + auto& inst = RegisterDevice::instance(); + if (inst.Map().size()) // FIXME Don't clear proxies, singstar keeps a copy to uninit audio + return; + inst.Add(DEVTYPE_PAD, new usb_pad::PadDevice()); + inst.Add(DEVTYPE_MSD, new usb_msd::MsdDevice()); + inst.Add(DEVTYPE_SINGSTAR, new usb_mic::SingstarDevice()); + inst.Add(DEVTYPE_LOGITECH_MIC, new usb_mic::LogitechMicDevice()); + inst.Add(DEVTYPE_LOGITECH_HEADSET, new usb_mic::HeadsetDevice()); + inst.Add(DEVTYPE_HIDKBD, new usb_hid::HIDKbdDevice()); + inst.Add(DEVTYPE_HIDMOUSE, new usb_hid::HIDMouseDevice()); + inst.Add(DEVTYPE_RBKIT, new usb_pad::RBDrumKitDevice()); + inst.Add(DEVTYPE_BUZZ, new usb_pad::BuzzDevice()); + inst.Add(DEVTYPE_EYETOY, new usb_eyetoy::EyeToyWebCamDevice()); + inst.Add(DEVTYPE_BEATMANIA_DADADA, new usb_hid::BeatManiaDevice()); + inst.Add(DEVTYPE_SEGA_SEAMIC, new usb_pad::SeamicDevice()); + inst.Add(DEVTYPE_PRINTER, new usb_printer::PrinterDevice()); + inst.Add(DEVTYPE_KEYBOARDMANIA, new usb_pad::KeyboardmaniaDevice()); + inst.Add(DEVTYPE_GUNCON2, new usb_lightgun::GunCon2Device()); +} + +void RegisterDevice::Unregister() +{ + registerDeviceMap.clear(); + delete registerDevice; + registerDevice = nullptr; } diff --git a/pcsx2/USB/deviceproxy.h b/pcsx2/USB/deviceproxy.h index 35fd7ffd24..a3714cbb76 100644 --- a/pcsx2/USB/deviceproxy.h +++ b/pcsx2/USB/deviceproxy.h @@ -13,26 +13,27 @@ * If not, see . */ -#ifndef DEVICEPROXY_H -#define DEVICEPROXY_H -#include "configuration.h" +#pragma once + #include #include +#include #include #include #include #include -//#include -#include "helpers.h" -#include "proxybase.h" +#include +#include "gsl/span" + #include "qemu-usb/USBinternal.h" + +#include "Config.h" #include "SaveState.h" -void RegisterAPIs(); -void UnregisterAPIs(); +class StateWrapper; // also map key/array index -enum DeviceType +enum DeviceType : s32 { DEVTYPE_NONE = -1, DEVTYPE_PAD = 0, @@ -44,160 +45,35 @@ enum DeviceType DEVTYPE_HIDMOUSE, DEVTYPE_RBKIT, DEVTYPE_BUZZ, - DEVTYPE_GAMETRAK, - DEVTYPE_REALPLAY, DEVTYPE_EYETOY, DEVTYPE_BEATMANIA_DADADA, DEVTYPE_SEGA_SEAMIC, DEVTYPE_PRINTER, DEVTYPE_KEYBOARDMANIA, + DEVTYPE_GUNCON2 }; -struct SelectDeviceName -{ - template - std::string operator()(const std::pair& x) const - { - return x.second->TypeName(); - } -}; - -class DeviceError : public std::runtime_error +class DeviceProxy { public: - DeviceError(const char* msg) - : std::runtime_error(msg) - { - } - virtual ~DeviceError() {} -}; + virtual ~DeviceProxy(); -class DeviceProxyBase -{ -public: - DeviceProxyBase(){}; - virtual ~DeviceProxyBase() {} - virtual USBDevice* CreateDevice(int port) = 0; - virtual const TCHAR* Name() const = 0; + virtual const char* Name() const = 0; virtual const char* TypeName() const = 0; - virtual int Configure(int port, const std::string& api, void* data) = 0; - virtual std::list ListAPIs() = 0; - virtual const TCHAR* LongAPIName(const std::string& name) = 0; - virtual int Freeze(FreezeAction mode, USBDevice* dev, void* data) = 0; - virtual std::vector SubTypes() = 0; + virtual std::vector SubTypes() const; + virtual gsl::span Bindings(u32 subtype) const; + virtual gsl::span Settings(u32 subtype) const; - virtual bool IsValidAPI(const std::string& api) - { - const std::list& apis = ListAPIs(); - auto it = std::find(apis.begin(), apis.end(), api); - if (it != apis.end()) - return true; - return false; - } -}; + virtual USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const = 0; -template -class DeviceProxy : public DeviceProxyBase -{ -public: - DeviceProxy() {} - virtual ~DeviceProxy() - { - } - virtual USBDevice* CreateDevice(int port) - { - return T::CreateDevice(port); - } - virtual const TCHAR* Name() const - { - return T::Name(); - } - virtual const char* TypeName() const - { - return T::TypeName(); - } - virtual int Configure(int port, const std::string& api, void* data) - { - return T::Configure(port, api, data); - } - virtual std::list ListAPIs() - { - return T::ListAPIs(); - } - virtual const TCHAR* LongAPIName(const std::string& name) - { - return T::LongAPIName(name); - } - virtual int Freeze(FreezeAction mode, USBDevice* dev, void* data) - { - return T::Freeze(mode, dev, data); - } - virtual std::vector SubTypes() - { - return T::SubTypes(); - } -}; + virtual float GetBindingValue(const USBDevice* dev, u32 bind) const; + virtual void SetBindingValue(USBDevice* dev, u32 bind, float value) const; -template -class RegisterProxy -{ - RegisterProxy(const RegisterProxy&) = delete; - RegisterProxy() {} + virtual bool Freeze(USBDevice* dev, StateWrapper& sw) const; + virtual void UpdateSettings(USBDevice* dev, SettingsInterface& si) const; -public: - typedef std::map> RegisterProxyMap; - static RegisterProxy& instance() - { - static RegisterProxy registerProxy; - return registerProxy; - } - - virtual ~RegisterProxy() - { - Clear(); - } - - void Clear() - { - registerProxyMap.clear(); - } - - void Add(const std::string& name, T* creator) - { - registerProxyMap[name] = std::unique_ptr(creator); - } - - T* Proxy(const std::string& name) - { - return registerProxyMap[name].get(); - } - - std::list Names() const - { - std::list nameList; - std::transform( - registerProxyMap.begin(), registerProxyMap.end(), - std::back_inserter(nameList), - SelectKey()); - return nameList; - } - - std::string Name(int idx) const - { - auto it = registerProxyMap.begin(); - std::advance(it, idx); - if (it != registerProxyMap.end()) - return std::string(it->first); - return std::string(); - } - - const RegisterProxyMap& Map() const - { - return registerProxyMap; - } - -private: - RegisterProxyMap registerProxyMap; + virtual void InputDeviceConnected(USBDevice* dev, const std::string_view& identifier) const; + virtual void InputDeviceDisconnected(USBDevice* dev, const std::string_view& identifier) const; }; class RegisterDevice @@ -207,7 +83,7 @@ class RegisterDevice static RegisterDevice* registerDevice; public: - typedef std::map> RegisterDeviceMap; + typedef std::map> RegisterDeviceMap; static RegisterDevice& instance() { if (!registerDevice) @@ -220,18 +96,13 @@ public: static void Register(); void Unregister(); - void Add(DeviceType key, DeviceProxyBase* creator) + void Add(DeviceType key, DeviceProxy* creator) { - registerDeviceMap[key] = std::unique_ptr(creator); + registerDeviceMap[key] = std::unique_ptr(creator); } - DeviceProxyBase* Device(const std::string& name) + DeviceProxy* Device(const std::string_view& name) { - //return registerDeviceMap[name]; - /*for (auto& k : registerDeviceMap) - if(k.first.name == name) - return k.second; - return nullptr;*/ auto proxy = std::find_if(registerDeviceMap.begin(), registerDeviceMap.end(), [&name](const RegisterDeviceMap::value_type& val) -> bool { @@ -242,7 +113,7 @@ public: return nullptr; } - DeviceProxyBase* Device(int index) + DeviceProxy* Device(int index) { auto it = registerDeviceMap.begin(); std::advance(it, index); @@ -251,7 +122,7 @@ public: return nullptr; } - DeviceType Index(const std::string& name) + DeviceType Index(const std::string_view& name) { auto proxy = std::find_if(registerDeviceMap.begin(), registerDeviceMap.end(), @@ -263,25 +134,6 @@ public: return DEVTYPE_NONE; } - std::list Names() const - { - std::list nameList; - std::transform( - registerDeviceMap.begin(), registerDeviceMap.end(), - std::back_inserter(nameList), - SelectDeviceName()); - return nameList; - } - - std::string Name(int index) const - { - auto it = registerDeviceMap.begin(); - std::advance(it, index); - if (it != registerDeviceMap.end()) - return it->second->TypeName(); - return std::string(); - } - const RegisterDeviceMap& Map() const { return registerDeviceMap; @@ -290,5 +142,3 @@ public: private: RegisterDeviceMap registerDeviceMap; }; - -#endif diff --git a/pcsx2/USB/gtk.h b/pcsx2/USB/gtk.h deleted file mode 100644 index c8748b83e4..0000000000 --- a/pcsx2/USB/gtk.h +++ /dev/null @@ -1,19 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ -#pragma once - -#include - -GtkWidget* new_combobox(const char* label, GtkWidget* vbox, bool scrollable = false); // linux/config-gtk.cpp diff --git a/pcsx2/USB/helpers.h b/pcsx2/USB/helpers.h deleted file mode 100644 index a641f22aa8..0000000000 --- a/pcsx2/USB/helpers.h +++ /dev/null @@ -1,28 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef HELPERS_H -#define HELPERS_H - -struct SelectKey -{ - template - F operator()(const std::pair& x) const - { - return x.first; - } -}; - -#endif diff --git a/pcsx2/USB/icon_buzz_24.cpp b/pcsx2/USB/icon_buzz_24.cpp deleted file mode 100644 index d173263ae7..0000000000 --- a/pcsx2/USB/icon_buzz_24.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -// clang-format off -#include "icon_buzz_24.h" -const unsigned char icon_buzz_24[] -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xa6, 0xf5, - 0xfd, 0xff, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfd, - 0xfd, 0xff, 0xfd, 0xf6, 0xa6, 0x39, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x50, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xd6, 0x50, 0x00, 0x00, 0x00, 0x1e, 0xce, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xce, 0x1e, 0x00, 0x00, 0x8b, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0x00, - 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x5e, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x5e, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x5e, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, - 0x00, 0x8b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8b, 0x00, 0x00, 0x1e, 0xce, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xce, 0x1e, 0x00, 0x00, 0x00, 0x50, 0xd6, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x50, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x39, 0xa6, 0xf0, 0xfd, 0xff, 0xfe, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfd, - 0xf0, 0xa6, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -// clang-format on diff --git a/pcsx2/USB/icon_buzz_24.h b/pcsx2/USB/icon_buzz_24.h deleted file mode 100644 index 12ba435f67..0000000000 --- a/pcsx2/USB/icon_buzz_24.h +++ /dev/null @@ -1,17 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -extern const unsigned char icon_buzz_24[]; diff --git a/pcsx2/USB/linux/actualfile.c b/pcsx2/USB/linux/actualfile.c deleted file mode 100644 index 17b0fe7a78..0000000000 --- a/pcsx2/USB/linux/actualfile.c +++ /dev/null @@ -1,152 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include // errno -#include // open() -#include // rename() -#include // strerror() -#include // stat64(), open(), fstat() -#include // stat64(), open(), fstat(), lseek64() -#include // stat64(), fstat(), lseek64(), read(), close(), write() -// unlink() - -//#include "logfile.h" -#include "actualfile.h" -int IsActualFile(const char *filename) { - int retval; - struct stat64 filestat; - - errno = 0; - retval = stat64(filename, &filestat); - if((retval < 0) || (errno != 0)) { - return(-1); // Name doesn't exist. - } // ENDIF- Trouble getting stat on a file? - - if(S_ISREG(filestat.st_mode) == 0) return(-2); // Not a regular file. - return(0); // Yep, that's a file. -} // END IsActualFile() - - -void ActualFileDelete(const char *filename) { - - unlink(filename); -} // END ActualFileDelete() - - -void ActualFileRename(const char *origname, const char *newname) { - - rename(origname, newname); - return; -} // END ActualFileRename() - - -ACTUALHANDLE ActualFileOpenForRead(const char *filename) { - int newhandle; - - if(filename == NULL) return(-1); - - errno = 0; - newhandle = open(filename, O_RDONLY | O_LARGEFILE); - if((newhandle < 0) || (errno != 0)) { - return(-1); - } // ENDIF- Error? Abort - - return(newhandle); -} // END ActualFileOpenForRead() - - -off64_t ActualFileSize(ACTUALHANDLE handle) { - int retval; - struct stat64 filestat; - - errno = 0; - retval = fstat64(handle, &filestat); - if((retval < 0) || (errno != 0)) return(-1); // Name doesn't exist. - return(filestat.st_size); -} // END ActualFileSize() - - -int ActualFileSeek(ACTUALHANDLE handle, off64_t position) { - off64_t moved; - - if(handle < 0) return(-1); - if(position < 0) return(-1); // Maybe... position = 0? - - errno = 0; - moved = lseek64(handle, position, SEEK_SET); - if(errno != 0) { - return(-1); - } // ENDIF- Error? Abort - - return(0); -} // END ActualFileSeek() - - -int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer) { - int retval; - - if(handle == ACTUALHANDLENULL) return(-1); - if(bytes < 1) return(-1); - if(buffer == NULL) return(-1); - - errno = 0; - retval = read(handle, buffer, bytes); - if((retval < 0) || (errno != 0)) { - // return(-1); - } // ENDIF- Error? Abort - - return(retval); // Send back how many bytes read -} // END ActualFileRead() - - -void ActualFileClose(ACTUALHANDLE handle) { - if(handle < 0) return; - - errno = 0; - close(handle); - return; -} // END ActualFileClose() - - -ACTUALHANDLE ActualFileOpenForWrite(const char *filename) { - int newhandle; - - if(filename == NULL) return(-1); - - errno = 0; - newhandle = open(filename, O_WRONLY | O_CREAT | O_LARGEFILE, 0644); - if((newhandle < 0) || (errno != 0)) { - return(-1); - } // ENDIF- Error? Abort - - return(newhandle); -} // END ActualFileOpenForWrite() - - -int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer) { - int retval; - - if(handle < 0) return(-1); - if(bytes < 1) return(-1); - if(buffer == NULL) return(-1); - - errno = 0; - retval = write(handle, buffer, bytes); - if((retval < 0) || (errno != 0)) { - // return(-1); - } // ENDIF- Error? Abort - - return(retval); // Send back how many bytes written -} // END ActualFileWrite() diff --git a/pcsx2/USB/linux/actualfile.h b/pcsx2/USB/linux/actualfile.h deleted file mode 100644 index ae7b1ffc84..0000000000 --- a/pcsx2/USB/linux/actualfile.h +++ /dev/null @@ -1,42 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef ACTUALFILE_H -#define ACTUALFILE_H - -#include // off64_t - - -#define ACTUALHANDLE int -#define ACTUALHANDLENULL -1 - -// #define VERBOSE_FUNCTION_ACTUALFILE -// #define VERBOSE_WARNING_ACTUALFILE - -extern int IsActualFile(const char* filename); -extern void ActualFileDelete(const char* filename); -extern void ActualFileRename(const char* origname, const char* newname); - -extern ACTUALHANDLE ActualFileOpenForRead(const char* filename); -extern off64_t ActualFileSize(ACTUALHANDLE handle); -extern int ActualFileSeek(ACTUALHANDLE handle, off64_t position); -extern int ActualFileRead(ACTUALHANDLE handle, int bytes, char* buffer); -extern void ActualFileClose(ACTUALHANDLE handle); - -extern ACTUALHANDLE ActualFileOpenForWrite(const char* filename); -extern int ActualFileWrite(ACTUALHANDLE handle, int bytes, char* buffer); - - -#endif /* ACTUALFILE_H */ diff --git a/pcsx2/USB/linux/config-gtk.cpp b/pcsx2/USB/linux/config-gtk.cpp deleted file mode 100644 index 29c32e2d31..0000000000 --- a/pcsx2/USB/linux/config-gtk.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include "gui/AppCoreThread.h" -#include "USB/gtk.h" - -#include "USB/configuration.h" -#include "USB/deviceproxy.h" -#include "USB/usb-pad/padproxy.h" -#include "USB/usb-mic/audiodeviceproxy.h" - -#include "config.h" -#include "USB/USB.h" - -struct SettingsCB -{ - int player; - std::string device; - std::string api; - GtkComboBox* combo; - GtkComboBox* subtype; -}; - -gboolean run_msg_dialog(gpointer data) -{ - GtkWidget* dialog = (GtkWidget*)data; - gtk_widget_show_all(dialog); - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - return FALSE; -} - -static void populateApiWidget(SettingsCB* settingsCB, const std::string& device) -{ - gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(settingsCB->combo))); - - auto dev = RegisterDevice::instance().Device(device); - GtkComboBox* widget = settingsCB->combo; - if (dev) - { - int port = 1 - settingsCB->player; - - std::string api; - - auto it = changedAPIs.find(std::make_pair(port, device)); - if (it == changedAPIs.end()) - { - LoadSetting(nullptr, port, device, N_DEVICE_API, api); - if (!dev->IsValidAPI(api)) - api.clear(); - } - else - api = it->second; - - settingsCB->api = api; - int i = 0; - for (auto& it : dev->ListAPIs()) - { - auto name = dev->LongAPIName(it); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), name); - if (api.size() && api == it) - gtk_combo_box_set_active(GTK_COMBO_BOX(widget), i); - else - gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); - i++; - } - } -} - -static void populateSubtypeWidget(SettingsCB* settingsCB, const std::string& device) -{ - gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(settingsCB->subtype))); - - auto dev = RegisterDevice::instance().Device(device); - GtkComboBox* widget = settingsCB->subtype; - if (dev) - { - int port = 1 - settingsCB->player; - int sel = 0; - if (!LoadSetting(nullptr, port, device, N_DEV_SUBTYPE, sel)) - { - changedSubtype[std::make_pair(port, device)] = sel; - } - - for (auto subtype : dev->SubTypes()) - { - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), subtype.c_str()); - } - gtk_combo_box_set_active(GTK_COMBO_BOX(widget), sel); - } -} - -static void deviceChanged(GtkComboBox* widget, gpointer data) -{ - SettingsCB* settingsCB = (SettingsCB*)data; - gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - int player = settingsCB->player; - std::string s; - - if (active > 0) - s = RegisterDevice::instance().Name(active - 1); - - settingsCB->device = s; - populateApiWidget(settingsCB, s); - populateSubtypeWidget(settingsCB, s); - - if (player == 0) - conf.Port[1] = s; - else - conf.Port[0] = s; - -} - -static void apiChanged(GtkComboBox* widget, gpointer data) -{ - SettingsCB* settingsCB = (SettingsCB*)data; - int player = settingsCB->player; - gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - - auto& name = settingsCB->device; - auto dev = RegisterDevice::instance().Device(name); - if (dev) - { - auto apis = dev->ListAPIs(); - auto it = apis.begin(); - std::advance(it, active); - if (it != apis.end()) - { - int port = 1 - player; - auto pair = std::make_pair(port, name); - auto itAPI = changedAPIs.find(pair); - - if (itAPI != changedAPIs.end()) - itAPI->second = *it; - else - changedAPIs[pair] = *it; - settingsCB->api = *it; - } - } -} - -static void subtypeChanged(GtkComboBox* widget, gpointer data) -{ - SettingsCB* settingsCB = (SettingsCB*)data; - int player = settingsCB->player; - gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - - auto& name = settingsCB->device; - auto dev = RegisterDevice::instance().Device(name); - if (dev) - { - int port = 1 - player; - changedSubtype[std::make_pair(port, name)] = active; - } -} - -static void configureApi(GtkWidget* widget, gpointer data) -{ - SettingsCB* settingsCB = (SettingsCB*)data; - int player = settingsCB->player; - - auto& name = settingsCB->device; - auto dev = RegisterDevice::instance().Device(name); - - if (dev) - { - int port = 1 - player; - auto& api = settingsCB->api; - - GtkWidget* dlg = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), "dlg")); - [[maybe_unused]]int res = dev->Configure(port, api, dlg); - } -} - -GtkWidget* new_combobox(const char* label, GtkWidget* vbox, bool scrollable) -{ - GtkWidget *rs_hbox, *rs_label, *rs_cb; - - rs_hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), rs_hbox, FALSE, TRUE, 0); - - rs_label = gtk_label_new(label); - gtk_box_pack_start(GTK_BOX(rs_hbox), rs_label, FALSE, TRUE, 5); - gtk_label_set_justify(GTK_LABEL(rs_label), GTK_JUSTIFY_RIGHT); - - rs_cb = gtk_combo_box_text_new(); - if (!scrollable) - gtk_box_pack_start(GTK_BOX(rs_hbox), rs_cb, TRUE, TRUE, 5); - else - { - GtkWidget* sw = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(sw), rs_cb); - gtk_box_pack_start(GTK_BOX(rs_hbox), sw, TRUE, TRUE, 5); - } - return rs_cb; -} - -static GtkWidget* new_frame(const char* label, GtkWidget* box) -{ - GtkWidget* ro_frame = gtk_frame_new(NULL); - gtk_box_pack_start(GTK_BOX(box), ro_frame, TRUE, FALSE, 0); - - GtkWidget* ro_label = gtk_label_new(label); - gtk_frame_set_label_widget(GTK_FRAME(ro_frame), ro_label); - gtk_label_set_use_markup(GTK_LABEL(ro_label), TRUE); - - GtkWidget* vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(ro_frame), vbox); - return vbox; -} - - -void USBconfigure() -{ - ScopedCoreThreadPause paused_core; - - USBsetSettingsDir(); - RegisterDevice::Register(); - LoadConfig(); - SettingsCB settingsCB[2]; - settingsCB[0].player = 0; - settingsCB[1].player = 1; - - const char* players[] = {"Player 1:", "Player 2:"}; - - GtkWidget *rs_cb, *vbox; - - // Create the dialog window - GtkWidget* dlg = gtk_dialog_new_with_buttons( - "USB Settings", NULL, GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE); - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - GtkWidget* main_vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(dlg_area_box), main_vbox); - - /*** Device type ***/ - vbox = new_frame("Select device type:", main_vbox); - - std::string devs[2] = {conf.Port[1], conf.Port[0]}; - /*** Devices' Comboboxes ***/ - for (int ply = 0; ply < 2; ply++) - { - settingsCB[ply].device = devs[ply]; - - rs_cb = new_combobox(players[ply], vbox); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), "None"); - gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), 0); - - auto devices = RegisterDevice::instance().Names(); - int idx = 0; - for (auto& device : devices) - { - auto deviceProxy = RegisterDevice::instance().Device(device); - if (!deviceProxy) - { - continue; - } - auto name = deviceProxy->Name(); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), name); - idx++; - if (devs[ply] == device) - gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), idx); - } - g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(deviceChanged), (gpointer)&settingsCB[ply]); - } - - /*** APIs ***/ - vbox = new_frame("Select device API:", main_vbox); - - /*** API Comboboxes ***/ - for (int ply = 0; ply < 2; ply++) - { - rs_cb = new_combobox(players[ply], vbox); - settingsCB[ply].combo = GTK_COMBO_BOX(rs_cb); - //gtk_combo_box_set_active (GTK_COMBO_BOX (rs_cb), sel_idx); - g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(apiChanged), (gpointer)&settingsCB[ply]); - - GtkWidget* hbox = gtk_widget_get_parent(rs_cb); - GtkWidget* button = gtk_button_new_with_label("Configure"); - gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_icon_name("gtk-preferences", GTK_ICON_SIZE_BUTTON)); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5); - - g_signal_connect(button, "clicked", G_CALLBACK(configureApi), (gpointer)&settingsCB[ply]); - g_object_set_data(G_OBJECT(button), "dlg", dlg); - - populateApiWidget(&settingsCB[ply], devs[ply]); - } - - /** Emulated device / Wheel type **/ - vbox = new_frame("Emulated device:", main_vbox); - - for (int ply = 0; ply < 2; ply++) - { - rs_cb = new_combobox(players[ply], vbox); - settingsCB[ply].subtype = GTK_COMBO_BOX(rs_cb); - g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(subtypeChanged), (gpointer)&settingsCB[ply]); - - populateSubtypeWidget(&settingsCB[ply], devs[ply]); - } - - gtk_widget_show_all(dlg); - - // Modal loop - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - gtk_widget_destroy(dlg); - - // Wait for all gtk events to be consumed ... - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); - - if (result == GTK_RESPONSE_OK) - { - SaveConfig(); - CreateDevices(); - } - // ClearAPIs(); - paused_core.AllowResume(); -} - -void CALLBACK USBabout() -{ -} diff --git a/pcsx2/USB/linux/config.cpp b/pcsx2/USB/linux/config.cpp deleted file mode 100644 index 4ea1162639..0000000000 --- a/pcsx2/USB/linux/config.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "config.h" - -#include "USB/configuration.h" -#include "USB/deviceproxy.h" -#include "USB/usb-pad/padproxy.h" -#include "USB/usb-mic/audiodeviceproxy.h" -#include "common/Console.h" - -void SysMessage_stderr(const char* fmt, ...) -{ - va_list arglist; - - va_start(arglist, fmt); - Console.Warning(fmt, arglist); - va_end(arglist); -} diff --git a/pcsx2/USB/linux/config.h b/pcsx2/USB/linux/config.h deleted file mode 100644 index 6152ec76b1..0000000000 --- a/pcsx2/USB/linux/config.h +++ /dev/null @@ -1,21 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef LINUXCONFIG_H -#define LINUXCONFIG_H -#include -#include - -#endif diff --git a/pcsx2/USB/linux/ini.c b/pcsx2/USB/linux/ini.c deleted file mode 100644 index 85d146ddab..0000000000 --- a/pcsx2/USB/linux/ini.c +++ /dev/null @@ -1,594 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include // NULL -#include // sprintf() -#include // va_start(), va_end(), vsprintf() -//#include "logfile.h" -#include "actualfile.h" -#include "ini.h" - -//#define VERBOSE_FUNCTION_INI 1 -const char INIext[] = ".ini"; -const char INInewext[] = ".new"; - -// Returns: position where new extensions should be added. -int INIRemoveExt(const char *argname, char *tempname) { - int i; - int j; - int k; - - i = 0; - while((i <= INIMAXLEN) && (*(argname + i) != 0)) { - *(tempname + i) = *(argname + i); - i++; - } // ENDWHILE- Copying the argument name into a temporary area; - *(tempname + i) = 0; // And 0-terminate - k = i; - k--; - - j = 0; - while((j <= INIMAXLEN) && (INIext[j] != 0)) j++; - j--; - - while((j >= 0) && (*(tempname + k) == INIext[j])) { - k--; - j--; - } // ENDWHILE- Comparing the ending characters to the INI ext. - if(j < 0) { - k++; - i = k; - *(tempname + i) = 0; // 0-terminate, cutting off ".ini" - } // ENDIF- Do we have a match? Then remove the end chars. - - return(i); -} // END INIRemoveExt() - - -void INIAddInExt(char *tempname, int temppos) { - int i; - - i = 0; - while((i + temppos < INIMAXLEN) && (INIext[i] != 0)) { - *(tempname + temppos + i) = INIext[i]; - i++; - } // ENDWHILE- Attaching extenstion to filename - *(tempname + temppos + i) = 0; // And 0-terminate -} // END INIAddInExt() - - -void INIAddOutExt(char *tempname, int temppos) { - int i; - - i = 0; - while((i + temppos < INIMAXLEN) && (INInewext[i] != 0)) { - *(tempname + temppos + i) = INInewext[i]; - i++; - } // ENDWHILE- Attaching extenstion to filename - *(tempname + temppos + i) = 0; // And 0-terminate -} // END INIAddInExt() - - -// Returns number of bytes read to get line (0 means end-of-file) -int INIReadLine(ACTUALHANDLE infile, char *buffer) { - int charcount = 0; - int i = 0; - char tempin[2] = {0}; - int retflag = 0; - - while((i < INIMAXLEN) && (retflag < 2)) { - const int retval = ActualFileRead(infile, 1, tempin); - charcount++; - if(retval != 1) { - retflag = 2; - charcount--; - - } else if(tempin[0] == '\n') { - retflag = 2; - - } else if(tempin[0] >= ' ') { - *(buffer + i) = tempin[0]; - i++; - } // ENDLONGIF- How do we react to the next character? - } // ENDWHILE- Loading up on characters until an End-of-Line appears - *(buffer + i) = 0; // And 0-terminate - - return(charcount); -} // END INIReadLine() -// Note: Do we need to back-skip a char if something other \n follows \r? - - -// Returns: number of bytes to get to start of section (or -1) -int INIFindSection(ACTUALHANDLE infile, const char *section) { - int charcount = 0; - int retflag = 0; - char scanbuffer[INIMAXLEN+1]; - - while(retflag == 0) { - const int retval = INIReadLine(infile, scanbuffer); - if(retval == 0) return(-1); // EOF? Stop here. - - if(scanbuffer[0] == '[') { - int i = 0; - while((i < INIMAXLEN) && - (*(section + i) != 0) && - (*(section + i) == scanbuffer[i + 1])) i++; - if((i < INIMAXLEN - 2) && (*(section + i) == 0)) { - if((scanbuffer[i + 1] == ']') && (scanbuffer[i + 2] == 0)) { - retflag = 1; - } // ENDIF- End marks look good? Return successful. - } // ENDIF- Do we have a section match? - } // ENDIF- Does this look like a section header? - - if(retflag == 0) charcount += retval; - } // ENDWHILE- Scanning lines for the correct [Section] header. - - return(charcount); -} // END INIFindSection() - -// Returns: number of bytes to get to start of keyword (or -1) -int INIFindKeyword(ACTUALHANDLE infile, const char *keyword, char *buffer) { - int charcount = 0; - int retflag = 0; - char scanbuffer[INIMAXLEN+1]; - - while(retflag == 0) { - int retval = INIReadLine(infile, scanbuffer); - if(retval == 0) return(-1); // EOF? Stop here. - if(scanbuffer[0] == '[') return(-1); // New section? Stop here. - - int i = 0; - while((i < INIMAXLEN) && - (*(keyword + i) != 0) && - (*(keyword + i) == scanbuffer[i])) i++; - if((i < INIMAXLEN - 2) && (*(keyword + i) == 0)) { - if(scanbuffer[i] == '=') { - retflag = 1; - if(buffer != NULL) { - i++; - int j = 0; - while((i < INIMAXLEN) && (scanbuffer[i] != 0)) { - *(buffer + j) = scanbuffer[i]; - i++; - j++; - } // ENDWHILE- Copying the value out to the outbound buffer. - *(buffer + j) = 0; // And 0-terminate. - } // ENDIF- Return the value as well? - } // ENDIF- End marks look good? Return successful. - } // ENDIF- Do we have a section match? - - if(retflag == 0) charcount += retval; - } // ENDWHILE- Scanning lines for the correct [Section] header. - - return(charcount); -} // END INIFindKeyWord() - - -// Returns: number of bytes left to write... (from charcount back) -int INICopy(ACTUALHANDLE infile, ACTUALHANDLE outfile, int charcount) { - char buffer[4096]; - int chunk = 4096; - - int i = charcount; - chunk = 4096; - if(i < chunk) chunk = i; - while(chunk > 0) { - int retval = ActualFileRead(infile, chunk, buffer); - if(retval <= 0) return(i); // Trouble? Stop here. - if(retval < chunk) chunk = retval; // Short block? Note it. - - retval = ActualFileWrite(outfile, chunk, buffer); - if(retval <= 0) return(i); // Trouble? Stop here. - i -= retval; - if(retval < chunk) return(i); // Short block written? Stop here. - - chunk = 4096; - if(i < chunk) chunk = i; - } // ENDWHILE- Copying a section of file across, one chunk at a time. - - return(0); -} // END INICopyToPos() - - -int INISaveString(const char *file, const char *section, const char *keyword, const char *value) { - char inname[INIMAXLEN+1]; - char outname[INIMAXLEN+1]; - int filepos; - ACTUALHANDLE infile; - ACTUALHANDLE outfile; - int i; - int retval; - char templine[INIMAXLEN+1]; - - if(file == NULL) return(-1); - if(section == NULL) return(-1); - if(keyword == NULL) return(-1); - if(value == NULL) return(-1); - - filepos = INIRemoveExt(file, inname); - for(i = 0; i <= filepos; i++) outname[i] = inname[i]; - INIAddInExt(inname, filepos); - INIAddOutExt(outname, filepos); - - filepos = 0; - infile = ActualFileOpenForRead(inname); - if(infile == ACTUALHANDLENULL) { - outfile = ActualFileOpenForWrite(inname); - if(outfile == ACTUALHANDLENULL) return(-1); // Just a bad name? Abort. - - sprintf(templine, "[%s]\r\n", section); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - if(retval < i) { - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(inname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - - sprintf(templine, "%s=%s\r\n", keyword, value); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - if(retval < i) { - ActualFileDelete(inname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - return(0); - } // ENDIF- No input file? Create a brand new .ini file then. - - retval = INIFindSection(infile, section); - if(retval < 0) { - outfile = ActualFileOpenForWrite(outname); - if(outfile == ACTUALHANDLENULL) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't open a temp file? Abort - - ActualFileSeek(infile, 0); // Move ini to beginning of file... - INICopy(infile, outfile, 0x0FFFFFFF); // Copy the whole file out... - - sprintf(templine, "\r\n[%s]\r\n", section); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - if(retval < i) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - - sprintf(templine, "%s=%s\r\n", keyword, value); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - if(retval < i) { - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - - ActualFileDelete(inname); - ActualFileRename(outname, inname); - return(0); - } // ENDIF- Couldn't find the section? Make a new one! - - filepos = retval; - ActualFileSeek(infile, filepos); - filepos += INIReadLine(infile, templine); // Get section line's byte count - - retval = INIFindKeyword(infile, keyword, NULL); - if(retval < 0) { - ActualFileSeek(infile, filepos); - retval = INIReadLine(infile, templine); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; - while((retval > 0) && (templine[i] == '=')) { - filepos += retval; - retval = INIReadLine(infile, templine); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; - } // ENDWHILE- skimming to the bottom of the section - - outfile = ActualFileOpenForWrite(outname); - if(outfile == ACTUALHANDLENULL) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't open a temp file? Abort - - ActualFileSeek(infile, 0); - retval = INICopy(infile, outfile, filepos); - if(retval > 0) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing everything up to keyword? Abort. - - sprintf(templine, "%s=%s\r\n", keyword, value); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - if(retval < i) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - - } else { - filepos += retval; // Position just before old version of keyword - - outfile = ActualFileOpenForWrite(outname); - if(outfile == ACTUALHANDLENULL) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't open a temp file? Abort - - ActualFileSeek(infile, 0); - retval = INICopy(infile, outfile, filepos); - if(retval > 0) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing everything up to keyword? Abort. - - INIReadLine(infile, templine); // Read past old keyword/value... - - // Replace with new value - sprintf(templine, "%s=%s\r\n", keyword, value); - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - if(retval < i) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - } // ENDIF- Need to add a new keyword? - - INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(inname); - ActualFileRename(outname, inname); - return(0); -} // END INISaveString() - - -int INILoadString(const char *file, const char *section, const char *keyword, char *buffer) { - char inname[INIMAXLEN+1]; - int filepos; - ACTUALHANDLE infile; - int retval; - - if(file == NULL) return(-1); - if(section == NULL) return(-1); - if(keyword == NULL) return(-1); - if(buffer == NULL) return(-1); - - filepos = INIRemoveExt(file, inname); - INIAddInExt(inname, filepos); - - filepos = 0; - infile = ActualFileOpenForRead(inname); - if(infile == ACTUALHANDLENULL) return(-1); - - retval = INIFindSection(infile, section); - if(retval < 0) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Didn't find it? Abort. - - retval = INIFindKeyword(infile, keyword, buffer); - if(retval < 0) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Didn't find it? Abort. - - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(0); -} // END INILoadString() - - -int INIRemove(const char *file, const char *section, const char *keyword) { - char inname[INIMAXLEN+1]; - char outname[INIMAXLEN+1]; - int filepos; - ACTUALHANDLE infile; - ACTUALHANDLE outfile; - char templine[INIMAXLEN+1]; - int i; - int retval; - - if(file == NULL) return(-1); - if(section == NULL) return(-1); - - - filepos = INIRemoveExt(file, inname); - for(i = 0; i <= filepos; i++) outname[i] = inname[i]; - INIAddInExt(inname, filepos); - INIAddOutExt(outname, filepos); - - infile = ActualFileOpenForRead(inname); - if(infile == ACTUALHANDLENULL) return(-1); - - retval = INIFindSection(infile, section); - if(retval == -1) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't even find the section? Abort - - filepos = retval; - if(keyword == NULL) { - outfile = ActualFileOpenForWrite(outname); - if(outfile == ACTUALHANDLENULL) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't open a temp file? Abort - - ActualFileSeek(infile, 0); - retval = INICopy(infile, outfile, filepos); - if(retval > 0) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing everything up to the section? Abort. - - templine[0] = 0; - retval = 1; - while((retval > 0) && (templine[0] != '[')) { - retval = INIReadLine(infile, templine); - } // ENDWHILE- Read to the start of the next section... or EOF. - - if(templine[0] == '[') { - i = 0; - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - retval = ActualFileWrite(outfile, i, templine); - if(retval < i) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing it out? Abort. - } // ENDIF- Are there other sections after this one? Save them then. - - } else { - filepos = retval; - ActualFileSeek(infile, filepos); - filepos += INIReadLine(infile, templine); // Get section line's byte count - - retval = INIFindKeyword(infile, keyword, NULL); - if(retval == -1) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't find the keyword? Abort - filepos += retval; - - outfile = ActualFileOpenForWrite(outname); - if(outfile == ACTUALHANDLENULL) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - return(-1); - } // ENDIF- Couldn't open a temp file? Abort - - ActualFileSeek(infile, 0); - retval = INICopy(infile, outfile, filepos); - if(retval > 0) { - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(outname); - return(-1); - } // ENDIF- Trouble writing everything up to keyword? Abort. - - INIReadLine(infile, templine); // Read (and discard) the keyword line - } // ENDIF- Wipe out the whole section? Or just a keyword? - - INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file - ActualFileClose(infile); - infile = ACTUALHANDLENULL; - ActualFileClose(outfile); - outfile = ACTUALHANDLENULL; - ActualFileDelete(inname); - ActualFileRename(outname, inname); - return(0); -} // END INIRemove() - - -int INISaveUInt(const char *file, const char *section, const char *keyword, unsigned int value) { - char numvalue[INIMAXLEN+1]; - - sprintf(numvalue, "%u", value); - return(INISaveString(file, section, keyword, numvalue)); -} // END INISaveUInt() - - -int INILoadUInt(const char *file, const char *section, const char *keyword, unsigned int *buffer) { - char numvalue[INIMAXLEN+1]; - int retval; - unsigned int value; - // unsigned int sign; // Not needed in unsigned numbers - int pos; - - if(buffer == NULL) return(-1); - *(buffer) = 0; - - retval = INILoadString(file, section, keyword, numvalue); - if(retval < 0) return(retval); - - value = 0; - // sign = 1; // Start positive - pos = 0; - - // Note: skip leading spaces? (Shouldn't have to, I hope) - - // if(numvalue[pos] == '-') { - // pos++; - // sign = -1; - // } // ENDIF- Negative sign check - - while((pos < INIMAXLEN) && (numvalue[pos] != 0)) { - if(value > (0xFFFFFFFF / 10)) return(-1); // Overflow? - - if((numvalue[pos] >= '0') && (numvalue[pos] <= '9')) { - value *= 10; - value += numvalue[pos] - '0'; - pos++; - } else { - numvalue[pos] = 0; - } // ENDIF- Add a digit in? Or stop searching for digits? - } // ENDWHILE- Adding digits of info to our ever-increasing value - - // value *= sign - *(buffer) = value; - return(0); -} // END INILoadUInt() diff --git a/pcsx2/USB/linux/ini.h b/pcsx2/USB/linux/ini.h deleted file mode 100644 index 184f6257c4..0000000000 --- a/pcsx2/USB/linux/ini.h +++ /dev/null @@ -1,61 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef INI_H -#define INI_H - - -// #ifndef __LINUX__ -// #ifdef __linux__ -// #define __LINUX__ -// #endif /* __linux__ */ -// #endif /* No __LINUX__ */ - -// #define CDVDdefs - - -// File format: -// [section] -// keyword=value - -// file - Name of the INI file -// section - Section within the file -// keyword - Identifier for a value -// value - value to store with a keyword in a section in the file -// buffer - place to retrieve the value of a keyword - -// return values: 0 = success, -1 = failure - - -// #define VERBOSE_FUNCTION_INI - -#define INIMAXLEN 255 - -#if __cplusplus -extern "C" { -#endif -extern int INISaveString(const char* file, const char* section, const char* keyword, const char* value); -extern int INILoadString(const char* file, const char* section, const char* keyword, char* buffer); - -extern int INISaveUInt(const char* file, const char* section, const char* keyword, unsigned int value); -extern int INILoadUInt(const char* file, const char* section, const char* keyword, unsigned int* buffer); - -// NULL in the keyword below removes the whole section. -extern int INIRemove(const char* file, const char* section, const char* keyword); - -#if __cplusplus -} -#endif -#endif /* INI_H */ diff --git a/pcsx2/USB/linux/util.cpp b/pcsx2/USB/linux/util.cpp deleted file mode 100644 index 990a5e6ec8..0000000000 --- a/pcsx2/USB/linux/util.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "util.h" -#include -#include -#include - -bool file_exists(std::string path) -{ - struct stat s; - return !stat(path.c_str(), &s) && !S_ISDIR(s.st_mode); -} - -bool dir_exists(std::string path) -{ - struct stat s; - return !stat(path.c_str(), &s) && S_ISDIR(s.st_mode); -} diff --git a/pcsx2/USB/platcompat.h b/pcsx2/USB/platcompat.h deleted file mode 100644 index ee7e7db9a1..0000000000 --- a/pcsx2/USB/platcompat.h +++ /dev/null @@ -1,132 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once - -// Annoying defines -// --------------------------------------------------------------------- -// make sure __POSIX__ is defined for all systems where we assume POSIX -// compliance -#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__) || defined(__LINUX__) -#if !defined(__POSIX__) -#define __POSIX__ 1 -#endif -#endif - -#ifndef EXPORT_C_ -#ifdef _MSC_VER -#define EXPORT_C_(type) extern "C" type CALLBACK -#elif defined(__i386__) -#define EXPORT_C_(type) extern "C" __attribute__((stdcall, visibility("default"))) type -#else -#define EXPORT_C_(type) extern "C" __attribute__((visibility("default"))) type -//#define EXPORT_C_(type) extern "C" __attribute__((stdcall,visibility("default"))) type -#endif -#endif - -#ifdef _WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#define wfopen _wfopen -#define fseeko64 _fseeki64 -#define ftello64 _ftelli64 -#define TSTDSTRING std::wstring -#define TSTDSTRINGSTREAM std::wstringstream -#define TSTDTOSTRING std::to_wstring - -//FIXME narrow string fmt -#define SFMTs "S" - -#define __builtin_constant_p(p) false - -void SysMessageW(const wchar_t* fmt, ...); -#define SysMessage SysMessageW - -#else //_WIN32 - -#define MAX_PATH PATH_MAX -#define __inline inline - -//#ifndef TEXT -//#define TEXT(x) L##x -//#endif -//FIXME narrow string fmt -#define SFMTs "s" -#define TEXT(val) val -#define TCHAR char -#define wfopen fopen -#define TSTDSTRING std::string -#define TSTDSTRINGSTREAM std::stringstream -#define TSTDTOSTRING std::to_string - -void SysMessage(const char* fmt, ...); - -#endif //_WIN32 - -#if __MINGW32__ - -#define DBL_EPSILON 2.2204460492503131e-16 -#define FLT_EPSILON 1.1920928955078125e-7f - -template -errno_t mbstowcs_s( - size_t* pReturnValue, - wchar_t (&wcstr)[size], - const char* mbstr, - size_t count) -{ - return mbstowcs_s(pReturnValue, wcstr, size, mbstr, count); -} - -template -errno_t wcstombs_s( - size_t* pReturnValue, - char (&mbstr)[size], - const wchar_t* wcstr, - size_t count) -{ - return wcstombs_s(pReturnValue, mbstr, size, wcstr, count); -} - -#endif //__MINGW32__ - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0]))) -#endif - -#include - -template -constexpr std::size_t countof(const T (&)[N]) noexcept -{ - return N; -} - -template -constexpr std::size_t countof(const T N) -{ - return N.size(); -} - -//TODO Idk, used only in desc.h and struct USBDescriptor should be already packed anyway -#if defined(_WIN32) && !defined(__MINGW32__) -#define PACK(def, name) __pragma(pack(push, 1)) def name __pragma(pack(pop)) -#elif defined(__clang__) -#define PACK(def, name) def __attribute__((packed)) name -#else -#define PACK(def, name) def __attribute__((gcc_struct, packed)) name -#endif diff --git a/pcsx2/USB/proxybase.h b/pcsx2/USB/proxybase.h deleted file mode 100644 index 7f01e5ce1e..0000000000 --- a/pcsx2/USB/proxybase.h +++ /dev/null @@ -1,25 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -//TODO Maybe too much inheritance? -class ProxyBase -{ -public: - ProxyBase() {} - virtual ~ProxyBase() {} - virtual const TCHAR* Name() const = 0; - virtual int Configure(int port, const char* dev_type, void* data) = 0; -}; diff --git a/pcsx2/USB/qemu-usb/USBinternal.h b/pcsx2/USB/qemu-usb/USBinternal.h index 2a8650c5c0..fac37ef702 100644 --- a/pcsx2/USB/qemu-usb/USBinternal.h +++ b/pcsx2/USB/qemu-usb/USBinternal.h @@ -13,10 +13,9 @@ * If not, see . */ -#ifndef USBINTERNAL_H -#define USBINTERNAL_H +#pragma once -#include "vl.h" +#include "USB/qemu-usb/qusb.h" /* Dump packet contents. */ //#define DEBUG_PACKET @@ -28,8 +27,8 @@ #define OHCI_MAX_PORTS 15 // status regs from 0x0c54 but usb snooping // reg is at 0x0c80, so only 11 ports? -extern int64_t usb_frame_time; -extern int64_t usb_bit_time; +extern int64_t g_usb_frame_time; +extern int64_t g_usb_bit_time; typedef struct OHCIPort { @@ -42,7 +41,6 @@ typedef uint32_t target_phys_addr_t; typedef struct OHCIState { target_phys_addr_t mem_base; - int mem; uint32_t num_ports; uint64_t eof_timer; @@ -106,7 +104,7 @@ struct ohci_hcca const typeof(((type *) 0)->member) *__mptr = (ptr); \ (type *) ((char *) __mptr - offsetof(type, member));}) */ -#define CONTAINER_OF(p, type, field) ((type*)((char*)p - ((ptrdiff_t) & ((type*)0)->field))) +#define USB_CONTAINER_OF(p, type, field) ((type*)((char*)p - ((ptrdiff_t) & ((type*)0)->field))) #define HCCA_WRITEBACK_OFFSET 128 //offsetof(struct ohci_hcca, frame) #define HCCA_WRITEBACK_SIZE 8 /* frame, pad, done */ @@ -306,4 +304,3 @@ void ohci_hard_reset(OHCIState* ohci); void ohci_soft_reset(OHCIState* ohci); int ohci_bus_start(OHCIState* ohci); void ohci_bus_stop(OHCIState* ohci); -#endif diff --git a/pcsx2/USB/qemu-usb/bus.cpp b/pcsx2/USB/qemu-usb/bus.cpp index 989b9e5534..036e6e80cd 100644 --- a/pcsx2/USB/qemu-usb/bus.cpp +++ b/pcsx2/USB/qemu-usb/bus.cpp @@ -14,8 +14,8 @@ */ #include "PrecompiledHeader.h" -#include "USBinternal.h" -#include "vl.h" +#include "USB/qemu-usb/USBinternal.h" +#include "USB/qemu-usb/qusb.h" #define USB_DEVICE_GET_CLASS(p) (&p->klass) diff --git a/pcsx2/USB/qemu-usb/core.cpp b/pcsx2/USB/qemu-usb/core.cpp index 416fac3766..45b1ae3e51 100644 --- a/pcsx2/USB/qemu-usb/core.cpp +++ b/pcsx2/USB/qemu-usb/core.cpp @@ -25,10 +25,9 @@ */ #include "PrecompiledHeader.h" -#include "USB/platcompat.h" -#include "vl.h" -#include "iov.h" -//#include "trace.h" +#include "USB/qemu-usb/qusb.h" +#include "USB/qemu-usb/iov.h" +#include void usb_pick_speed(USBPort* port) { @@ -39,9 +38,8 @@ void usb_pick_speed(USBPort* port) USB_SPEED_LOW, }; USBDevice* udev = port->dev; - int i; - for (i = 0; i < (int)ARRAY_SIZE(speeds); i++) + for (u32 i = 0; i < std::size(speeds); i++) { if ((udev->speedmask & (1 << speeds[i])) && (port->speedmask & (1 << speeds[i]))) @@ -568,7 +566,7 @@ void usb_cancel_packet(USBPacket* p) void usb_packet_init(USBPacket* p) { - qemu_iovec_init(&p->iov, 1); + qemu_iovec_init(&p->iov); } static const char* usb_packet_state_name(USBPacketState state) @@ -581,7 +579,7 @@ static const char* usb_packet_state_name(USBPacketState state) /*[USB_PACKET_COMPLETE] =*/"complete", /*[USB_PACKET_CANCELED] =*/"canceled", }; - if (state < ARRAY_SIZE(name)) + if (static_cast(state) < std::size(name)) { return name[state]; } diff --git a/pcsx2/USB/qemu-usb/desc.cpp b/pcsx2/USB/qemu-usb/desc.cpp index b51eb191bc..62ee4d68a4 100644 --- a/pcsx2/USB/qemu-usb/desc.cpp +++ b/pcsx2/USB/qemu-usb/desc.cpp @@ -14,10 +14,8 @@ */ #include "PrecompiledHeader.h" -#include "vl.h" -#include "desc.h" -#include "glib.h" -//#include "trace.h" +#include "USB/qemu-usb/qusb.h" +#include "USB/qemu-usb/desc.h" /* ------------------------------------------------------------------ */ diff --git a/pcsx2/USB/qemu-usb/desc.h b/pcsx2/USB/qemu-usb/desc.h index 1df5e974b2..17802bbc30 100644 --- a/pcsx2/USB/qemu-usb/desc.h +++ b/pcsx2/USB/qemu-usb/desc.h @@ -15,12 +15,11 @@ #pragma once -#include +#include #include -#include "USB/platcompat.h" /* binary representation */ -PACK( +#pragma pack(push, 1) typedef struct USBDescriptor { uint8_t bLength; uint8_t bDescriptorType; @@ -124,8 +123,8 @@ PACK( } u; } cap; } u; - }, - USBDescriptor); + } USBDescriptor; +#pragma pack(pop) struct USBDescID { diff --git a/pcsx2/USB/qemu-usb/glib.cpp b/pcsx2/USB/qemu-usb/glib.cpp deleted file mode 100644 index 222fbd94a2..0000000000 --- a/pcsx2/USB/qemu-usb/glib.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "glib.h" -#include -#include - -#define SIZE_OVERFLOWS(a, b) (G_UNLIKELY((b) > 0 && (a) > G_MAXSIZE / (b))) - -/** - * g_malloc: - * @n_bytes: the number of bytes to allocate - * - * Allocates @n_bytes bytes of memory. - * If @n_bytes is 0 it returns %NULL. - * - * Returns: a pointer to the allocated memory - */ -void* my_g_malloc(size_t n_bytes) -{ - if (G_LIKELY(n_bytes)) - { - void* mem; - - mem = malloc(n_bytes); - //TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0)); - if (mem) - return mem; - - //g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", - // G_STRLOC, n_bytes); - } - - //TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0)); - - return NULL; -} -/** - * g_malloc0: - * @n_bytes: the number of bytes to allocate - * - * Allocates @n_bytes bytes of memory, initialized to 0's. - * If @n_bytes is 0 it returns %NULL. - * - * Returns: a pointer to the allocated memory - */ -void* my_g_malloc0(size_t n_bytes) -{ - if (G_LIKELY(n_bytes)) - { - void* mem; - - mem = calloc(1, n_bytes); - //TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0)); - if (mem) - return mem; - - //g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", - // G_STRLOC, n_bytes); - } - - //TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0)); - - return NULL; -} -/** - * g_malloc_n: - * @n_blocks: the number of blocks to allocate - * @n_block_bytes: the size of each block in bytes - * - * This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, - * but care is taken to detect possible overflow during multiplication. - * - * Since: 2.24 - * Returns: a pointer to the allocated memory - */ -void* my_g_malloc_n(size_t n_blocks, - size_t n_block_bytes) -{ - if (SIZE_OVERFLOWS(n_blocks, n_block_bytes)) - { - //g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", - // G_STRLOC, n_blocks, n_block_bytes); - } - - return my_g_malloc(n_blocks * n_block_bytes); -} - -/** - * g_realloc: - * @mem: (nullable): the memory to reallocate - * @n_bytes: new size of the memory in bytes - * - * Reallocates the memory pointed to by @mem, so that it now has space for - * @n_bytes bytes of memory. It returns the new address of the memory, which may - * have been moved. @mem may be %NULL, in which case it's considered to - * have zero-length. @n_bytes may be 0, in which case %NULL will be returned - * and @mem will be freed unless it is %NULL. - * - * Returns: the new address of the allocated memory - */ -void* my_g_realloc(void* mem, - size_t n_bytes) -{ - if (G_LIKELY(n_bytes)) - { - void* newmem = realloc(mem, n_bytes); - //TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0)); - if (newmem) - return newmem; - - //g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", - // G_STRLOC, n_bytes); - } - - if (mem) - free(mem); - - //TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0)); - - return NULL; -} - -/** - * g_realloc_n: - * @mem: (nullable): the memory to reallocate - * @n_blocks: the number of blocks to allocate - * @n_block_bytes: the size of each block in bytes - * - * This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, - * but care is taken to detect possible overflow during multiplication. - * - * Since: 2.24 - * Returns: the new address of the allocated memory - */ -void* my_g_realloc_n(void* mem, - size_t n_blocks, - size_t n_block_bytes) -{ - if (SIZE_OVERFLOWS(n_blocks, n_block_bytes)) - { - //g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", - // G_STRLOC, n_blocks, n_block_bytes); - } - - return my_g_realloc(mem, n_blocks * n_block_bytes); -} diff --git a/pcsx2/USB/qemu-usb/glib.h b/pcsx2/USB/qemu-usb/glib.h deleted file mode 100644 index 3d0120ecd4..0000000000 --- a/pcsx2/USB/qemu-usb/glib.h +++ /dev/null @@ -1,47 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef GLIB_H -#define GLIB_H -#include -#include - -#define G_MAXSIZE G_MAXUINT64 - -#define G_MAXUINT64 0xffffffffffffffffUL -#define G_MAXUINT32 ((uint32_t)0xffffffff) - -void* my_g_malloc0(size_t n_bytes); -void* my_g_malloc(size_t n_bytes); -void* my_g_malloc_n(size_t n_blocks, - size_t n_block_bytes); -void* my_g_realloc_n(void* mem, - size_t n_blocks, - size_t n_block_bytes); - -#define my_g_free free - -#define G_LIKELY(expr) (expr) -#define G_UNLIKELY(expr) (expr) - -#define my_G_NEW(struct_type, n_structs, func) \ - ((struct_type*)my_g_##func##_n((n_structs), sizeof(struct_type))) -#define my_G_RENEW(struct_type, mem, n_structs, func) \ - ((struct_type*)my_g_##func##_n(mem, (n_structs), sizeof(struct_type))) - -#define my_g_new(struct_type, n_structs) my_G_NEW(struct_type, n_structs, malloc) -#define my_g_renew(struct_type, mem, n_structs) my_G_RENEW(struct_type, mem, n_structs, realloc) - -#endif diff --git a/pcsx2/USB/qemu-usb/hid.cpp b/pcsx2/USB/qemu-usb/hid.cpp index 617bbfcbd1..b330698c1e 100644 --- a/pcsx2/USB/qemu-usb/hid.cpp +++ b/pcsx2/USB/qemu-usb/hid.cpp @@ -748,14 +748,14 @@ int hid_keyboard_poll(HIDState* hs, uint8_t* buf, int len) buf[1] = 0; if (hs->kbd.keys > 6) { - memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); + memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, std::min(8, len) - 2); } else { - memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); + memcpy(buf + 2, hs->kbd.key, std::min(8, len) - 2); } - return MIN(8, len); + return std::min(8, len); } int hid_keyboard_write(HIDState* hs, uint8_t* buf, int len) diff --git a/pcsx2/USB/qemu-usb/hid.h b/pcsx2/USB/qemu-usb/hid.h index e349393c95..7e7c3c20ab 100644 --- a/pcsx2/USB/qemu-usb/hid.h +++ b/pcsx2/USB/qemu-usb/hid.h @@ -13,9 +13,8 @@ * If not, see . */ -#ifndef QEMU_HID_H -#define QEMU_HID_H -#include "vl.h" +#pragma once +#include /* include/ui/console.h */ /* keyboard/mouse support */ @@ -343,5 +342,3 @@ void hid_pointer_activate(HIDState* hs); int hid_pointer_poll(HIDState* hs, uint8_t* buf, int len); int hid_keyboard_poll(HIDState* hs, uint8_t* buf, int len); int hid_keyboard_write(HIDState* hs, uint8_t* buf, int len); - -#endif /* QEMU_HID_H */ diff --git a/pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.cpp b/pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.cpp deleted file mode 100644 index 437552f01b..0000000000 --- a/pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.cpp +++ /dev/null @@ -1,544 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "input-keymap-linux-to-qcode.h" -const QKeyCode qemu_input_map_linux_to_qcode[] = { - Q_KEY_CODE_UNMAPPED, /* linux:0 (KEY_RESERVED) -> linux:0 (KEY_RESERVED) -> qcode:Q_KEY_CODE_UNMAPPED (unmapped) */ - Q_KEY_CODE_ESC, /* linux:1 (KEY_ESC) -> linux:1 (KEY_ESC) -> qcode:Q_KEY_CODE_ESC (esc) */ - Q_KEY_CODE_1, /* linux:2 (KEY_1) -> linux:2 (KEY_1) -> qcode:Q_KEY_CODE_1 (1) */ - Q_KEY_CODE_2, /* linux:3 (KEY_2) -> linux:3 (KEY_2) -> qcode:Q_KEY_CODE_2 (2) */ - Q_KEY_CODE_3, /* linux:4 (KEY_3) -> linux:4 (KEY_3) -> qcode:Q_KEY_CODE_3 (3) */ - Q_KEY_CODE_4, /* linux:5 (KEY_4) -> linux:5 (KEY_4) -> qcode:Q_KEY_CODE_4 (4) */ - Q_KEY_CODE_5, /* linux:6 (KEY_5) -> linux:6 (KEY_5) -> qcode:Q_KEY_CODE_5 (5) */ - Q_KEY_CODE_6, /* linux:7 (KEY_6) -> linux:7 (KEY_6) -> qcode:Q_KEY_CODE_6 (6) */ - Q_KEY_CODE_7, /* linux:8 (KEY_7) -> linux:8 (KEY_7) -> qcode:Q_KEY_CODE_7 (7) */ - Q_KEY_CODE_8, /* linux:9 (KEY_8) -> linux:9 (KEY_8) -> qcode:Q_KEY_CODE_8 (8) */ - Q_KEY_CODE_9, /* linux:10 (KEY_9) -> linux:10 (KEY_9) -> qcode:Q_KEY_CODE_9 (9) */ - Q_KEY_CODE_0, /* linux:11 (KEY_0) -> linux:11 (KEY_0) -> qcode:Q_KEY_CODE_0 (0) */ - Q_KEY_CODE_MINUS, /* linux:12 (KEY_MINUS) -> linux:12 (KEY_MINUS) -> qcode:Q_KEY_CODE_MINUS (minus) */ - Q_KEY_CODE_EQUAL, /* linux:13 (KEY_EQUAL) -> linux:13 (KEY_EQUAL) -> qcode:Q_KEY_CODE_EQUAL (equal) */ - Q_KEY_CODE_BACKSPACE, /* linux:14 (KEY_BACKSPACE) -> linux:14 (KEY_BACKSPACE) -> qcode:Q_KEY_CODE_BACKSPACE (backspace) */ - Q_KEY_CODE_TAB, /* linux:15 (KEY_TAB) -> linux:15 (KEY_TAB) -> qcode:Q_KEY_CODE_TAB (tab) */ - Q_KEY_CODE_Q, /* linux:16 (KEY_Q) -> linux:16 (KEY_Q) -> qcode:Q_KEY_CODE_Q (q) */ - Q_KEY_CODE_W, /* linux:17 (KEY_W) -> linux:17 (KEY_W) -> qcode:Q_KEY_CODE_W (w) */ - Q_KEY_CODE_E, /* linux:18 (KEY_E) -> linux:18 (KEY_E) -> qcode:Q_KEY_CODE_E (e) */ - Q_KEY_CODE_R, /* linux:19 (KEY_R) -> linux:19 (KEY_R) -> qcode:Q_KEY_CODE_R (r) */ - Q_KEY_CODE_T, /* linux:20 (KEY_T) -> linux:20 (KEY_T) -> qcode:Q_KEY_CODE_T (t) */ - Q_KEY_CODE_Y, /* linux:21 (KEY_Y) -> linux:21 (KEY_Y) -> qcode:Q_KEY_CODE_Y (y) */ - Q_KEY_CODE_U, /* linux:22 (KEY_U) -> linux:22 (KEY_U) -> qcode:Q_KEY_CODE_U (u) */ - Q_KEY_CODE_I, /* linux:23 (KEY_I) -> linux:23 (KEY_I) -> qcode:Q_KEY_CODE_I (i) */ - Q_KEY_CODE_O, /* linux:24 (KEY_O) -> linux:24 (KEY_O) -> qcode:Q_KEY_CODE_O (o) */ - Q_KEY_CODE_P, /* linux:25 (KEY_P) -> linux:25 (KEY_P) -> qcode:Q_KEY_CODE_P (p) */ - Q_KEY_CODE_BRACKET_LEFT, /* linux:26 (KEY_LEFTBRACE) -> linux:26 (KEY_LEFTBRACE) -> qcode:Q_KEY_CODE_BRACKET_LEFT (bracket_left) */ - Q_KEY_CODE_BRACKET_RIGHT, /* linux:27 (KEY_RIGHTBRACE) -> linux:27 (KEY_RIGHTBRACE) -> qcode:Q_KEY_CODE_BRACKET_RIGHT (bracket_right) */ - Q_KEY_CODE_RET, /* linux:28 (KEY_ENTER) -> linux:28 (KEY_ENTER) -> qcode:Q_KEY_CODE_RET (ret) */ - Q_KEY_CODE_CTRL, /* linux:29 (KEY_LEFTCTRL) -> linux:29 (KEY_LEFTCTRL) -> qcode:Q_KEY_CODE_CTRL (ctrl) */ - Q_KEY_CODE_A, /* linux:30 (KEY_A) -> linux:30 (KEY_A) -> qcode:Q_KEY_CODE_A (a) */ - Q_KEY_CODE_S, /* linux:31 (KEY_S) -> linux:31 (KEY_S) -> qcode:Q_KEY_CODE_S (s) */ - Q_KEY_CODE_D, /* linux:32 (KEY_D) -> linux:32 (KEY_D) -> qcode:Q_KEY_CODE_D (d) */ - Q_KEY_CODE_F, /* linux:33 (KEY_F) -> linux:33 (KEY_F) -> qcode:Q_KEY_CODE_F (f) */ - Q_KEY_CODE_G, /* linux:34 (KEY_G) -> linux:34 (KEY_G) -> qcode:Q_KEY_CODE_G (g) */ - Q_KEY_CODE_H, /* linux:35 (KEY_H) -> linux:35 (KEY_H) -> qcode:Q_KEY_CODE_H (h) */ - Q_KEY_CODE_J, /* linux:36 (KEY_J) -> linux:36 (KEY_J) -> qcode:Q_KEY_CODE_J (j) */ - Q_KEY_CODE_K, /* linux:37 (KEY_K) -> linux:37 (KEY_K) -> qcode:Q_KEY_CODE_K (k) */ - Q_KEY_CODE_L, /* linux:38 (KEY_L) -> linux:38 (KEY_L) -> qcode:Q_KEY_CODE_L (l) */ - Q_KEY_CODE_SEMICOLON, /* linux:39 (KEY_SEMICOLON) -> linux:39 (KEY_SEMICOLON) -> qcode:Q_KEY_CODE_SEMICOLON (semicolon) */ - Q_KEY_CODE_APOSTROPHE, /* linux:40 (KEY_APOSTROPHE) -> linux:40 (KEY_APOSTROPHE) -> qcode:Q_KEY_CODE_APOSTROPHE (apostrophe) */ - Q_KEY_CODE_GRAVE_ACCENT, /* linux:41 (KEY_GRAVE) -> linux:41 (KEY_GRAVE) -> qcode:Q_KEY_CODE_GRAVE_ACCENT (grave_accent) */ - Q_KEY_CODE_SHIFT, /* linux:42 (KEY_LEFTSHIFT) -> linux:42 (KEY_LEFTSHIFT) -> qcode:Q_KEY_CODE_SHIFT (shift) */ - Q_KEY_CODE_BACKSLASH, /* linux:43 (KEY_BACKSLASH) -> linux:43 (KEY_BACKSLASH) -> qcode:Q_KEY_CODE_BACKSLASH (backslash) */ - Q_KEY_CODE_Z, /* linux:44 (KEY_Z) -> linux:44 (KEY_Z) -> qcode:Q_KEY_CODE_Z (z) */ - Q_KEY_CODE_X, /* linux:45 (KEY_X) -> linux:45 (KEY_X) -> qcode:Q_KEY_CODE_X (x) */ - Q_KEY_CODE_C, /* linux:46 (KEY_C) -> linux:46 (KEY_C) -> qcode:Q_KEY_CODE_C (c) */ - Q_KEY_CODE_V, /* linux:47 (KEY_V) -> linux:47 (KEY_V) -> qcode:Q_KEY_CODE_V (v) */ - Q_KEY_CODE_B, /* linux:48 (KEY_B) -> linux:48 (KEY_B) -> qcode:Q_KEY_CODE_B (b) */ - Q_KEY_CODE_N, /* linux:49 (KEY_N) -> linux:49 (KEY_N) -> qcode:Q_KEY_CODE_N (n) */ - Q_KEY_CODE_M, /* linux:50 (KEY_M) -> linux:50 (KEY_M) -> qcode:Q_KEY_CODE_M (m) */ - Q_KEY_CODE_COMMA, /* linux:51 (KEY_COMMA) -> linux:51 (KEY_COMMA) -> qcode:Q_KEY_CODE_COMMA (comma) */ - Q_KEY_CODE_DOT, /* linux:52 (KEY_DOT) -> linux:52 (KEY_DOT) -> qcode:Q_KEY_CODE_DOT (dot) */ - Q_KEY_CODE_SLASH, /* linux:53 (KEY_SLASH) -> linux:53 (KEY_SLASH) -> qcode:Q_KEY_CODE_SLASH (slash) */ - Q_KEY_CODE_SHIFT_R, /* linux:54 (KEY_RIGHTSHIFT) -> linux:54 (KEY_RIGHTSHIFT) -> qcode:Q_KEY_CODE_SHIFT_R (shift_r) */ - Q_KEY_CODE_KP_MULTIPLY, /* linux:55 (KEY_KPASTERISK) -> linux:55 (KEY_KPASTERISK) -> qcode:Q_KEY_CODE_KP_MULTIPLY (kp_multiply) */ - Q_KEY_CODE_ALT, /* linux:56 (KEY_LEFTALT) -> linux:56 (KEY_LEFTALT) -> qcode:Q_KEY_CODE_ALT (alt) */ - Q_KEY_CODE_SPC, /* linux:57 (KEY_SPACE) -> linux:57 (KEY_SPACE) -> qcode:Q_KEY_CODE_SPC (spc) */ - Q_KEY_CODE_CAPS_LOCK, /* linux:58 (KEY_CAPSLOCK) -> linux:58 (KEY_CAPSLOCK) -> qcode:Q_KEY_CODE_CAPS_LOCK (caps_lock) */ - Q_KEY_CODE_F1, /* linux:59 (KEY_F1) -> linux:59 (KEY_F1) -> qcode:Q_KEY_CODE_F1 (f1) */ - Q_KEY_CODE_F2, /* linux:60 (KEY_F2) -> linux:60 (KEY_F2) -> qcode:Q_KEY_CODE_F2 (f2) */ - Q_KEY_CODE_F3, /* linux:61 (KEY_F3) -> linux:61 (KEY_F3) -> qcode:Q_KEY_CODE_F3 (f3) */ - Q_KEY_CODE_F4, /* linux:62 (KEY_F4) -> linux:62 (KEY_F4) -> qcode:Q_KEY_CODE_F4 (f4) */ - Q_KEY_CODE_F5, /* linux:63 (KEY_F5) -> linux:63 (KEY_F5) -> qcode:Q_KEY_CODE_F5 (f5) */ - Q_KEY_CODE_F6, /* linux:64 (KEY_F6) -> linux:64 (KEY_F6) -> qcode:Q_KEY_CODE_F6 (f6) */ - Q_KEY_CODE_F7, /* linux:65 (KEY_F7) -> linux:65 (KEY_F7) -> qcode:Q_KEY_CODE_F7 (f7) */ - Q_KEY_CODE_F8, /* linux:66 (KEY_F8) -> linux:66 (KEY_F8) -> qcode:Q_KEY_CODE_F8 (f8) */ - Q_KEY_CODE_F9, /* linux:67 (KEY_F9) -> linux:67 (KEY_F9) -> qcode:Q_KEY_CODE_F9 (f9) */ - Q_KEY_CODE_F10, /* linux:68 (KEY_F10) -> linux:68 (KEY_F10) -> qcode:Q_KEY_CODE_F10 (f10) */ - Q_KEY_CODE_NUM_LOCK, /* linux:69 (KEY_NUMLOCK) -> linux:69 (KEY_NUMLOCK) -> qcode:Q_KEY_CODE_NUM_LOCK (num_lock) */ - Q_KEY_CODE_SCROLL_LOCK, /* linux:70 (KEY_SCROLLLOCK) -> linux:70 (KEY_SCROLLLOCK) -> qcode:Q_KEY_CODE_SCROLL_LOCK (scroll_lock) */ - Q_KEY_CODE_KP_7, /* linux:71 (KEY_KP7) -> linux:71 (KEY_KP7) -> qcode:Q_KEY_CODE_KP_7 (kp_7) */ - Q_KEY_CODE_KP_8, /* linux:72 (KEY_KP8) -> linux:72 (KEY_KP8) -> qcode:Q_KEY_CODE_KP_8 (kp_8) */ - Q_KEY_CODE_KP_9, /* linux:73 (KEY_KP9) -> linux:73 (KEY_KP9) -> qcode:Q_KEY_CODE_KP_9 (kp_9) */ - Q_KEY_CODE_KP_SUBTRACT, /* linux:74 (KEY_KPMINUS) -> linux:74 (KEY_KPMINUS) -> qcode:Q_KEY_CODE_KP_SUBTRACT (kp_subtract) */ - Q_KEY_CODE_KP_4, /* linux:75 (KEY_KP4) -> linux:75 (KEY_KP4) -> qcode:Q_KEY_CODE_KP_4 (kp_4) */ - Q_KEY_CODE_KP_5, /* linux:76 (KEY_KP5) -> linux:76 (KEY_KP5) -> qcode:Q_KEY_CODE_KP_5 (kp_5) */ - Q_KEY_CODE_KP_6, /* linux:77 (KEY_KP6) -> linux:77 (KEY_KP6) -> qcode:Q_KEY_CODE_KP_6 (kp_6) */ - Q_KEY_CODE_KP_ADD, /* linux:78 (KEY_KPPLUS) -> linux:78 (KEY_KPPLUS) -> qcode:Q_KEY_CODE_KP_ADD (kp_add) */ - Q_KEY_CODE_KP_1, /* linux:79 (KEY_KP1) -> linux:79 (KEY_KP1) -> qcode:Q_KEY_CODE_KP_1 (kp_1) */ - Q_KEY_CODE_KP_2, /* linux:80 (KEY_KP2) -> linux:80 (KEY_KP2) -> qcode:Q_KEY_CODE_KP_2 (kp_2) */ - Q_KEY_CODE_KP_3, /* linux:81 (KEY_KP3) -> linux:81 (KEY_KP3) -> qcode:Q_KEY_CODE_KP_3 (kp_3) */ - Q_KEY_CODE_KP_0, /* linux:82 (KEY_KP0) -> linux:82 (KEY_KP0) -> qcode:Q_KEY_CODE_KP_0 (kp_0) */ - Q_KEY_CODE_KP_DECIMAL, /* linux:83 (KEY_KPDOT) -> linux:83 (KEY_KPDOT) -> qcode:Q_KEY_CODE_KP_DECIMAL (kp_decimal) */ - Q_KEY_CODE_UNMAPPED, /* linux:84 (unnamed) -> linux:84 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:85 (KEY_ZENKAKUHANKAKU) -> linux:85 (KEY_ZENKAKUHANKAKU) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_LESS, /* linux:86 (KEY_102ND) -> linux:86 (KEY_102ND) -> qcode:Q_KEY_CODE_LESS (less) */ - Q_KEY_CODE_F11, /* linux:87 (KEY_F11) -> linux:87 (KEY_F11) -> qcode:Q_KEY_CODE_F11 (f11) */ - Q_KEY_CODE_F12, /* linux:88 (KEY_F12) -> linux:88 (KEY_F12) -> qcode:Q_KEY_CODE_F12 (f12) */ - Q_KEY_CODE_RO, /* linux:89 (KEY_RO) -> linux:89 (KEY_RO) -> qcode:Q_KEY_CODE_RO (ro) */ - Q_KEY_CODE_UNMAPPED, /* linux:90 (KEY_KATAKANA) -> linux:90 (KEY_KATAKANA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_HIRAGANA, /* linux:91 (KEY_HIRAGANA) -> linux:91 (KEY_HIRAGANA) -> qcode:Q_KEY_CODE_HIRAGANA (hiragana) */ - Q_KEY_CODE_HENKAN, /* linux:92 (KEY_HENKAN) -> linux:92 (KEY_HENKAN) -> qcode:Q_KEY_CODE_HENKAN (henkan) */ - Q_KEY_CODE_KATAKANAHIRAGANA, /* linux:93 (KEY_KATAKANAHIRAGANA) -> linux:93 (KEY_KATAKANAHIRAGANA) -> qcode:Q_KEY_CODE_KATAKANAHIRAGANA (katakanahiragana) */ - Q_KEY_CODE_MUHENKAN, /* linux:94 (KEY_MUHENKAN) -> linux:94 (KEY_MUHENKAN) -> qcode:Q_KEY_CODE_MUHENKAN (muhenkan) */ - Q_KEY_CODE_UNMAPPED, /* linux:95 (KEY_KPJPCOMMA) -> linux:95 (KEY_KPJPCOMMA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_KP_ENTER, /* linux:96 (KEY_KPENTER) -> linux:96 (KEY_KPENTER) -> qcode:Q_KEY_CODE_KP_ENTER (kp_enter) */ - Q_KEY_CODE_CTRL_R, /* linux:97 (KEY_RIGHTCTRL) -> linux:97 (KEY_RIGHTCTRL) -> qcode:Q_KEY_CODE_CTRL_R (ctrl_r) */ - Q_KEY_CODE_KP_DIVIDE, /* linux:98 (KEY_KPSLASH) -> linux:98 (KEY_KPSLASH) -> qcode:Q_KEY_CODE_KP_DIVIDE (kp_divide) */ - Q_KEY_CODE_SYSRQ, /* linux:99 (KEY_SYSRQ) -> linux:99 (KEY_SYSRQ) -> qcode:Q_KEY_CODE_SYSRQ (sysrq) */ - Q_KEY_CODE_ALT_R, /* linux:100 (KEY_RIGHTALT) -> linux:100 (KEY_RIGHTALT) -> qcode:Q_KEY_CODE_ALT_R (alt_r) */ - Q_KEY_CODE_LF, /* linux:101 (KEY_LINEFEED) -> linux:101 (KEY_LINEFEED) -> qcode:Q_KEY_CODE_LF (lf) */ - Q_KEY_CODE_HOME, /* linux:102 (KEY_HOME) -> linux:102 (KEY_HOME) -> qcode:Q_KEY_CODE_HOME (home) */ - Q_KEY_CODE_UP, /* linux:103 (KEY_UP) -> linux:103 (KEY_UP) -> qcode:Q_KEY_CODE_UP (up) */ - Q_KEY_CODE_PGUP, /* linux:104 (KEY_PAGEUP) -> linux:104 (KEY_PAGEUP) -> qcode:Q_KEY_CODE_PGUP (pgup) */ - Q_KEY_CODE_LEFT, /* linux:105 (KEY_LEFT) -> linux:105 (KEY_LEFT) -> qcode:Q_KEY_CODE_LEFT (left) */ - Q_KEY_CODE_RIGHT, /* linux:106 (KEY_RIGHT) -> linux:106 (KEY_RIGHT) -> qcode:Q_KEY_CODE_RIGHT (right) */ - Q_KEY_CODE_END, /* linux:107 (KEY_END) -> linux:107 (KEY_END) -> qcode:Q_KEY_CODE_END (end) */ - Q_KEY_CODE_DOWN, /* linux:108 (KEY_DOWN) -> linux:108 (KEY_DOWN) -> qcode:Q_KEY_CODE_DOWN (down) */ - Q_KEY_CODE_PGDN, /* linux:109 (KEY_PAGEDOWN) -> linux:109 (KEY_PAGEDOWN) -> qcode:Q_KEY_CODE_PGDN (pgdn) */ - Q_KEY_CODE_INSERT, /* linux:110 (KEY_INSERT) -> linux:110 (KEY_INSERT) -> qcode:Q_KEY_CODE_INSERT (insert) */ - Q_KEY_CODE_DELETE, /* linux:111 (KEY_DELETE) -> linux:111 (KEY_DELETE) -> qcode:Q_KEY_CODE_DELETE (delete) */ - Q_KEY_CODE_UNMAPPED, /* linux:112 (KEY_MACRO) -> linux:112 (KEY_MACRO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_AUDIOMUTE, /* linux:113 (KEY_MUTE) -> linux:113 (KEY_MUTE) -> qcode:Q_KEY_CODE_AUDIOMUTE (audiomute) */ - Q_KEY_CODE_VOLUMEDOWN, /* linux:114 (KEY_VOLUMEDOWN) -> linux:114 (KEY_VOLUMEDOWN) -> qcode:Q_KEY_CODE_VOLUMEDOWN (volumedown) */ - Q_KEY_CODE_VOLUMEUP, /* linux:115 (KEY_VOLUMEUP) -> linux:115 (KEY_VOLUMEUP) -> qcode:Q_KEY_CODE_VOLUMEUP (volumeup) */ - Q_KEY_CODE_POWER, /* linux:116 (KEY_POWER) -> linux:116 (KEY_POWER) -> qcode:Q_KEY_CODE_POWER (power) */ - Q_KEY_CODE_KP_EQUALS, /* linux:117 (KEY_KPEQUAL) -> linux:117 (KEY_KPEQUAL) -> qcode:Q_KEY_CODE_KP_EQUALS (kp_equals) */ - Q_KEY_CODE_UNMAPPED, /* linux:118 (KEY_KPPLUSMINUS) -> linux:118 (KEY_KPPLUSMINUS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_PAUSE, /* linux:119 (KEY_PAUSE) -> linux:119 (KEY_PAUSE) -> qcode:Q_KEY_CODE_PAUSE (pause) */ - Q_KEY_CODE_UNMAPPED, /* linux:120 (KEY_SCALE) -> linux:120 (KEY_SCALE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_KP_COMMA, /* linux:121 (KEY_KPCOMMA) -> linux:121 (KEY_KPCOMMA) -> qcode:Q_KEY_CODE_KP_COMMA (kp_comma) */ - Q_KEY_CODE_UNMAPPED, /* linux:122 (KEY_HANGEUL) -> linux:122 (KEY_HANGEUL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:123 (KEY_HANJA) -> linux:123 (KEY_HANJA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_YEN, /* linux:124 (KEY_YEN) -> linux:124 (KEY_YEN) -> qcode:Q_KEY_CODE_YEN (yen) */ - Q_KEY_CODE_META_L, /* linux:125 (KEY_LEFTMETA) -> linux:125 (KEY_LEFTMETA) -> qcode:Q_KEY_CODE_META_L (meta_l) */ - Q_KEY_CODE_META_R, /* linux:126 (KEY_RIGHTMETA) -> linux:126 (KEY_RIGHTMETA) -> qcode:Q_KEY_CODE_META_R (meta_r) */ - Q_KEY_CODE_COMPOSE, /* linux:127 (KEY_COMPOSE) -> linux:127 (KEY_COMPOSE) -> qcode:Q_KEY_CODE_COMPOSE (compose) */ - Q_KEY_CODE_STOP, /* linux:128 (KEY_STOP) -> linux:128 (KEY_STOP) -> qcode:Q_KEY_CODE_STOP (stop) */ - Q_KEY_CODE_AGAIN, /* linux:129 (KEY_AGAIN) -> linux:129 (KEY_AGAIN) -> qcode:Q_KEY_CODE_AGAIN (again) */ - Q_KEY_CODE_PROPS, /* linux:130 (KEY_PROPS) -> linux:130 (KEY_PROPS) -> qcode:Q_KEY_CODE_PROPS (props) */ - Q_KEY_CODE_UNDO, /* linux:131 (KEY_UNDO) -> linux:131 (KEY_UNDO) -> qcode:Q_KEY_CODE_UNDO (undo) */ - Q_KEY_CODE_FRONT, /* linux:132 (KEY_FRONT) -> linux:132 (KEY_FRONT) -> qcode:Q_KEY_CODE_FRONT (front) */ - Q_KEY_CODE_COPY, /* linux:133 (KEY_COPY) -> linux:133 (KEY_COPY) -> qcode:Q_KEY_CODE_COPY (copy) */ - Q_KEY_CODE_OPEN, /* linux:134 (KEY_OPEN) -> linux:134 (KEY_OPEN) -> qcode:Q_KEY_CODE_OPEN (open) */ - Q_KEY_CODE_PASTE, /* linux:135 (KEY_PASTE) -> linux:135 (KEY_PASTE) -> qcode:Q_KEY_CODE_PASTE (paste) */ - Q_KEY_CODE_FIND, /* linux:136 (KEY_FIND) -> linux:136 (KEY_FIND) -> qcode:Q_KEY_CODE_FIND (find) */ - Q_KEY_CODE_CUT, /* linux:137 (KEY_CUT) -> linux:137 (KEY_CUT) -> qcode:Q_KEY_CODE_CUT (cut) */ - Q_KEY_CODE_HELP, /* linux:138 (KEY_HELP) -> linux:138 (KEY_HELP) -> qcode:Q_KEY_CODE_HELP (help) */ - Q_KEY_CODE_MENU, /* linux:139 (KEY_MENU) -> linux:139 (KEY_MENU) -> qcode:Q_KEY_CODE_MENU (menu) */ - Q_KEY_CODE_CALCULATOR, /* linux:140 (KEY_CALC) -> linux:140 (KEY_CALC) -> qcode:Q_KEY_CODE_CALCULATOR (calculator) */ - Q_KEY_CODE_UNMAPPED, /* linux:141 (KEY_SETUP) -> linux:141 (KEY_SETUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_SLEEP, /* linux:142 (KEY_SLEEP) -> linux:142 (KEY_SLEEP) -> qcode:Q_KEY_CODE_SLEEP (sleep) */ - Q_KEY_CODE_WAKE, /* linux:143 (KEY_WAKEUP) -> linux:143 (KEY_WAKEUP) -> qcode:Q_KEY_CODE_WAKE (wake) */ - Q_KEY_CODE_UNMAPPED, /* linux:144 (KEY_FILE) -> linux:144 (KEY_FILE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:145 (KEY_SENDFILE) -> linux:145 (KEY_SENDFILE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:146 (KEY_DELETEFILE) -> linux:146 (KEY_DELETEFILE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:147 (KEY_XFER) -> linux:147 (KEY_XFER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:148 (KEY_PROG1) -> linux:148 (KEY_PROG1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:149 (KEY_PROG2) -> linux:149 (KEY_PROG2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:150 (KEY_WWW) -> linux:150 (KEY_WWW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:151 (KEY_MSDOS) -> linux:151 (KEY_MSDOS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:152 (KEY_SCREENLOCK) -> linux:152 (KEY_SCREENLOCK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:153 (KEY_DIRECTION) -> linux:153 (KEY_DIRECTION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:154 (KEY_CYCLEWINDOWS) -> linux:154 (KEY_CYCLEWINDOWS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_MAIL, /* linux:155 (KEY_MAIL) -> linux:155 (KEY_MAIL) -> qcode:Q_KEY_CODE_MAIL (mail) */ - Q_KEY_CODE_AC_BOOKMARKS, /* linux:156 (KEY_BOOKMARKS) -> linux:156 (KEY_BOOKMARKS) -> qcode:Q_KEY_CODE_AC_BOOKMARKS (ac_bookmarks) */ - Q_KEY_CODE_COMPUTER, /* linux:157 (KEY_COMPUTER) -> linux:157 (KEY_COMPUTER) -> qcode:Q_KEY_CODE_COMPUTER (computer) */ - Q_KEY_CODE_AC_BACK, /* linux:158 (KEY_BACK) -> linux:158 (KEY_BACK) -> qcode:Q_KEY_CODE_AC_BACK (ac_back) */ - Q_KEY_CODE_AC_FORWARD, /* linux:159 (KEY_FORWARD) -> linux:159 (KEY_FORWARD) -> qcode:Q_KEY_CODE_AC_FORWARD (ac_forward) */ - Q_KEY_CODE_UNMAPPED, /* linux:160 (KEY_CLOSECD) -> linux:160 (KEY_CLOSECD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:161 (KEY_EJECTCD) -> linux:161 (KEY_EJECTCD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:162 (KEY_EJECTCLOSECD) -> linux:162 (KEY_EJECTCLOSECD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_AUDIONEXT, /* linux:163 (KEY_NEXTSONG) -> linux:163 (KEY_NEXTSONG) -> qcode:Q_KEY_CODE_AUDIONEXT (audionext) */ - Q_KEY_CODE_AUDIOPLAY, /* linux:164 (KEY_PLAYPAUSE) -> linux:164 (KEY_PLAYPAUSE) -> qcode:Q_KEY_CODE_AUDIOPLAY (audioplay) */ - Q_KEY_CODE_AUDIOPREV, /* linux:165 (KEY_PREVIOUSSONG) -> linux:165 (KEY_PREVIOUSSONG) -> qcode:Q_KEY_CODE_AUDIOPREV (audioprev) */ - Q_KEY_CODE_AUDIOSTOP, /* linux:166 (KEY_STOPCD) -> linux:166 (KEY_STOPCD) -> qcode:Q_KEY_CODE_AUDIOSTOP (audiostop) */ - Q_KEY_CODE_UNMAPPED, /* linux:167 (KEY_RECORD) -> linux:167 (KEY_RECORD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:168 (KEY_REWIND) -> linux:168 (KEY_REWIND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:169 (KEY_PHONE) -> linux:169 (KEY_PHONE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:170 (KEY_ISO) -> linux:170 (KEY_ISO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:171 (KEY_CONFIG) -> linux:171 (KEY_CONFIG) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_AC_HOME, /* linux:172 (KEY_HOMEPAGE) -> linux:172 (KEY_HOMEPAGE) -> qcode:Q_KEY_CODE_AC_HOME (ac_home) */ - Q_KEY_CODE_AC_REFRESH, /* linux:173 (KEY_REFRESH) -> linux:173 (KEY_REFRESH) -> qcode:Q_KEY_CODE_AC_REFRESH (ac_refresh) */ - Q_KEY_CODE_UNMAPPED, /* linux:174 (KEY_EXIT) -> linux:174 (KEY_EXIT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:175 (KEY_MOVE) -> linux:175 (KEY_MOVE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:176 (KEY_EDIT) -> linux:176 (KEY_EDIT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:177 (KEY_SCROLLUP) -> linux:177 (KEY_SCROLLUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:178 (KEY_SCROLLDOWN) -> linux:178 (KEY_SCROLLDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:179 (KEY_KPLEFTPAREN) -> linux:179 (KEY_KPLEFTPAREN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:180 (KEY_KPRIGHTPAREN) -> linux:180 (KEY_KPRIGHTPAREN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:181 (KEY_NEW) -> linux:181 (KEY_NEW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:182 (KEY_REDO) -> linux:182 (KEY_REDO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:183 (KEY_F13) -> linux:183 (KEY_F13) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:184 (KEY_F14) -> linux:184 (KEY_F14) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:185 (KEY_F15) -> linux:185 (KEY_F15) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:186 (KEY_F16) -> linux:186 (KEY_F16) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:187 (KEY_F17) -> linux:187 (KEY_F17) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:188 (KEY_F18) -> linux:188 (KEY_F18) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:189 (KEY_F19) -> linux:189 (KEY_F19) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:190 (KEY_F20) -> linux:190 (KEY_F20) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:191 (KEY_F21) -> linux:191 (KEY_F21) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:192 (KEY_F22) -> linux:192 (KEY_F22) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:193 (KEY_F23) -> linux:193 (KEY_F23) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:194 (KEY_F24) -> linux:194 (KEY_F24) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:195 (unnamed) -> linux:195 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:196 (unnamed) -> linux:196 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:197 (unnamed) -> linux:197 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:198 (unnamed) -> linux:198 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:199 (unnamed) -> linux:199 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:200 (KEY_PLAYCD) -> linux:200 (KEY_PLAYCD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:201 (KEY_PAUSECD) -> linux:201 (KEY_PAUSECD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:202 (KEY_PROG3) -> linux:202 (KEY_PROG3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:203 (KEY_PROG4) -> linux:203 (KEY_PROG4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:204 (KEY_DASHBOARD) -> linux:204 (KEY_DASHBOARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:205 (KEY_SUSPEND) -> linux:205 (KEY_SUSPEND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:206 (KEY_CLOSE) -> linux:206 (KEY_CLOSE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:207 (KEY_PLAY) -> linux:207 (KEY_PLAY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:208 (KEY_FASTFORWARD) -> linux:208 (KEY_FASTFORWARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:209 (KEY_BASSBOOST) -> linux:209 (KEY_BASSBOOST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:210 (KEY_PRINT) -> linux:210 (KEY_PRINT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:211 (KEY_HP) -> linux:211 (KEY_HP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:212 (KEY_CAMERA) -> linux:212 (KEY_CAMERA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:213 (KEY_SOUND) -> linux:213 (KEY_SOUND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:214 (KEY_QUESTION) -> linux:214 (KEY_QUESTION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:215 (KEY_EMAIL) -> linux:215 (KEY_EMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:216 (KEY_CHAT) -> linux:216 (KEY_CHAT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:217 (KEY_SEARCH) -> linux:217 (KEY_SEARCH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:218 (KEY_CONNECT) -> linux:218 (KEY_CONNECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:219 (KEY_FINANCE) -> linux:219 (KEY_FINANCE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:220 (KEY_SPORT) -> linux:220 (KEY_SPORT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:221 (KEY_SHOP) -> linux:221 (KEY_SHOP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:222 (KEY_ALTERASE) -> linux:222 (KEY_ALTERASE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:223 (KEY_CANCEL) -> linux:223 (KEY_CANCEL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:224 (KEY_BRIGHTNESSDOWN) -> linux:224 (KEY_BRIGHTNESSDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:225 (KEY_BRIGHTNESSUP) -> linux:225 (KEY_BRIGHTNESSUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_MEDIASELECT, /* linux:226 (KEY_MEDIA) -> linux:226 (KEY_MEDIA) -> qcode:Q_KEY_CODE_MEDIASELECT (mediaselect) */ - Q_KEY_CODE_UNMAPPED, /* linux:227 (KEY_SWITCHVIDEOMODE) -> linux:227 (KEY_SWITCHVIDEOMODE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:228 (KEY_KBDILLUMTOGGLE) -> linux:228 (KEY_KBDILLUMTOGGLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:229 (KEY_KBDILLUMDOWN) -> linux:229 (KEY_KBDILLUMDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:230 (KEY_KBDILLUMUP) -> linux:230 (KEY_KBDILLUMUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:231 (KEY_SEND) -> linux:231 (KEY_SEND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:232 (KEY_REPLY) -> linux:232 (KEY_REPLY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:233 (KEY_FORWARDMAIL) -> linux:233 (KEY_FORWARDMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:234 (KEY_SAVE) -> linux:234 (KEY_SAVE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:235 (KEY_DOCUMENTS) -> linux:235 (KEY_DOCUMENTS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:236 (KEY_BATTERY) -> linux:236 (KEY_BATTERY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:237 (KEY_BLUETOOTH) -> linux:237 (KEY_BLUETOOTH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:238 (KEY_WLAN) -> linux:238 (KEY_WLAN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:239 (KEY_UWB) -> linux:239 (KEY_UWB) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:240 (KEY_UNKNOWN) -> linux:240 (KEY_UNKNOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:241 (KEY_VIDEO_NEXT) -> linux:241 (KEY_VIDEO_NEXT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:242 (KEY_VIDEO_PREV) -> linux:242 (KEY_VIDEO_PREV) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:243 (KEY_BRIGHTNESS_CYCLE) -> linux:243 (KEY_BRIGHTNESS_CYCLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:244 (KEY_BRIGHTNESS_ZERO) -> linux:244 (KEY_BRIGHTNESS_ZERO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:245 (KEY_DISPLAY_OFF) -> linux:245 (KEY_DISPLAY_OFF) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:246 (KEY_WIMAX) -> linux:246 (KEY_WIMAX) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:247 (unnamed) -> linux:247 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:248 (unnamed) -> linux:248 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:249 (unnamed) -> linux:249 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:250 (unnamed) -> linux:250 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:251 (unnamed) -> linux:251 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:252 (unnamed) -> linux:252 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:253 (unnamed) -> linux:253 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:254 (unnamed) -> linux:254 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:255 (unnamed) -> linux:255 (unnamed) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:256 (BTN_0) -> linux:256 (BTN_0) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:257 (BTN_1) -> linux:257 (BTN_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:258 (BTN_2) -> linux:258 (BTN_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:259 (BTN_3) -> linux:259 (BTN_3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:260 (BTN_4) -> linux:260 (BTN_4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:261 (BTN_5) -> linux:261 (BTN_5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:262 (BTN_6) -> linux:262 (BTN_6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:263 (BTN_7) -> linux:263 (BTN_7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:264 (BTN_8) -> linux:264 (BTN_8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:265 (BTN_9) -> linux:265 (BTN_9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:266 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:267 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:268 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:269 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:270 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:271 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:272 (BTN_LEFT) -> linux:272 (BTN_LEFT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:273 (BTN_RIGHT) -> linux:273 (BTN_RIGHT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:274 (BTN_MIDDLE) -> linux:274 (BTN_MIDDLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:275 (BTN_SIDE) -> linux:275 (BTN_SIDE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:276 (BTN_EXTRA) -> linux:276 (BTN_EXTRA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:277 (BTN_FORWARD) -> linux:277 (BTN_FORWARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:278 (BTN_BACK) -> linux:278 (BTN_BACK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:279 (BTN_TASK) -> linux:279 (BTN_TASK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:280 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:281 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:282 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:283 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:284 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:285 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:286 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:287 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:288 (BTN_TRIGGER) -> linux:288 (BTN_TRIGGER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:289 (BTN_THUMB) -> linux:289 (BTN_THUMB) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:290 (BTN_THUMB2) -> linux:290 (BTN_THUMB2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:291 (BTN_TOP) -> linux:291 (BTN_TOP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:292 (BTN_TOP2) -> linux:292 (BTN_TOP2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:293 (BTN_PINKIE) -> linux:293 (BTN_PINKIE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:294 (BTN_BASE) -> linux:294 (BTN_BASE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:295 (BTN_BASE2) -> linux:295 (BTN_BASE2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:296 (BTN_BASE3) -> linux:296 (BTN_BASE3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:297 (BTN_BASE4) -> linux:297 (BTN_BASE4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:298 (BTN_BASE5) -> linux:298 (BTN_BASE5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:299 (BTN_BASE6) -> linux:299 (BTN_BASE6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:300 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:301 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:302 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:303 (BTN_DEAD) -> linux:303 (BTN_DEAD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:304 (BTN_A) -> linux:304 (BTN_A) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:305 (BTN_B) -> linux:305 (BTN_B) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:306 (BTN_C) -> linux:306 (BTN_C) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:307 (BTN_X) -> linux:307 (BTN_X) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:308 (BTN_Y) -> linux:308 (BTN_Y) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:309 (BTN_Z) -> linux:309 (BTN_Z) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:310 (BTN_TL) -> linux:310 (BTN_TL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:311 (BTN_TR) -> linux:311 (BTN_TR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:312 (BTN_TL2) -> linux:312 (BTN_TL2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:313 (BTN_TR2) -> linux:313 (BTN_TR2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:314 (BTN_SELECT) -> linux:314 (BTN_SELECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:315 (BTN_START) -> linux:315 (BTN_START) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:316 (BTN_MODE) -> linux:316 (BTN_MODE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:317 (BTN_THUMBL) -> linux:317 (BTN_THUMBL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:318 (BTN_THUMBR) -> linux:318 (BTN_THUMBR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:319 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:320 (BTN_TOOL_PEN) -> linux:320 (BTN_TOOL_PEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:321 (BTN_TOOL_RUBBER) -> linux:321 (BTN_TOOL_RUBBER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:322 (BTN_TOOL_BRUSH) -> linux:322 (BTN_TOOL_BRUSH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:323 (BTN_TOOL_PENCIL) -> linux:323 (BTN_TOOL_PENCIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:324 (BTN_TOOL_AIRBRUSH) -> linux:324 (BTN_TOOL_AIRBRUSH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:325 (BTN_TOOL_FINGER) -> linux:325 (BTN_TOOL_FINGER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:326 (BTN_TOOL_MOUSE) -> linux:326 (BTN_TOOL_MOUSE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:327 (BTN_TOOL_LENS) -> linux:327 (BTN_TOOL_LENS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:328 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:329 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:330 (BTN_TOUCH) -> linux:330 (BTN_TOUCH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:331 (BTN_STYLUS) -> linux:331 (BTN_STYLUS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:332 (BTN_STYLUS2) -> linux:332 (BTN_STYLUS2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:333 (BTN_TOOL_DOUBLETAP) -> linux:333 (BTN_TOOL_DOUBLETAP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:334 (BTN_TOOL_TRIPLETAP) -> linux:334 (BTN_TOOL_TRIPLETAP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:335 (BTN_TOOL_QUADTAP) -> linux:335 (BTN_TOOL_QUADTAP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:336 (BTN_GEAR_DOWN) -> linux:336 (BTN_GEAR_DOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:337 (BTN_GEAR_UP) -> linux:337 (BTN_GEAR_UP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:338 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:339 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:340 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:341 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:342 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:343 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:344 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:345 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:346 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:347 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:348 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:349 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:350 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:351 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:352 (KEY_OK) -> linux:352 (KEY_OK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:353 (KEY_SELECT) -> linux:353 (KEY_SELECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:354 (KEY_GOTO) -> linux:354 (KEY_GOTO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:355 (KEY_CLEAR) -> linux:355 (KEY_CLEAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:356 (KEY_POWER2) -> linux:356 (KEY_POWER2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:357 (KEY_OPTION) -> linux:357 (KEY_OPTION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:358 (KEY_INFO) -> linux:358 (KEY_INFO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:359 (KEY_TIME) -> linux:359 (KEY_TIME) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:360 (KEY_VENDOR) -> linux:360 (KEY_VENDOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:361 (KEY_ARCHIVE) -> linux:361 (KEY_ARCHIVE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:362 (KEY_PROGRAM) -> linux:362 (KEY_PROGRAM) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:363 (KEY_CHANNEL) -> linux:363 (KEY_CHANNEL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:364 (KEY_FAVORITES) -> linux:364 (KEY_FAVORITES) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:365 (KEY_EPG) -> linux:365 (KEY_EPG) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:366 (KEY_PVR) -> linux:366 (KEY_PVR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:367 (KEY_MHP) -> linux:367 (KEY_MHP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:368 (KEY_LANGUAGE) -> linux:368 (KEY_LANGUAGE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:369 (KEY_TITLE) -> linux:369 (KEY_TITLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:370 (KEY_SUBTITLE) -> linux:370 (KEY_SUBTITLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:371 (KEY_ANGLE) -> linux:371 (KEY_ANGLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:372 (KEY_ZOOM) -> linux:372 (KEY_ZOOM) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:373 (KEY_MODE) -> linux:373 (KEY_MODE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:374 (KEY_KEYBOARD) -> linux:374 (KEY_KEYBOARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:375 (KEY_SCREEN) -> linux:375 (KEY_SCREEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:376 (KEY_PC) -> linux:376 (KEY_PC) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:377 (KEY_TV) -> linux:377 (KEY_TV) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:378 (KEY_TV2) -> linux:378 (KEY_TV2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:379 (KEY_VCR) -> linux:379 (KEY_VCR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:380 (KEY_VCR2) -> linux:380 (KEY_VCR2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:381 (KEY_SAT) -> linux:381 (KEY_SAT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:382 (KEY_SAT2) -> linux:382 (KEY_SAT2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:383 (KEY_CD) -> linux:383 (KEY_CD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:384 (KEY_TAPE) -> linux:384 (KEY_TAPE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:385 (KEY_RADIO) -> linux:385 (KEY_RADIO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:386 (KEY_TUNER) -> linux:386 (KEY_TUNER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:387 (KEY_PLAYER) -> linux:387 (KEY_PLAYER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:388 (KEY_TEXT) -> linux:388 (KEY_TEXT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:389 (KEY_DVD) -> linux:389 (KEY_DVD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:390 (KEY_AUX) -> linux:390 (KEY_AUX) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:391 (KEY_MP3) -> linux:391 (KEY_MP3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:392 (KEY_AUDIO) -> linux:392 (KEY_AUDIO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:393 (KEY_VIDEO) -> linux:393 (KEY_VIDEO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:394 (KEY_DIRECTORY) -> linux:394 (KEY_DIRECTORY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:395 (KEY_LIST) -> linux:395 (KEY_LIST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:396 (KEY_MEMO) -> linux:396 (KEY_MEMO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:397 (KEY_CALENDAR) -> linux:397 (KEY_CALENDAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:398 (KEY_RED) -> linux:398 (KEY_RED) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:399 (KEY_GREEN) -> linux:399 (KEY_GREEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:400 (KEY_YELLOW) -> linux:400 (KEY_YELLOW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:401 (KEY_BLUE) -> linux:401 (KEY_BLUE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:402 (KEY_CHANNELUP) -> linux:402 (KEY_CHANNELUP) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:403 (KEY_CHANNELDOWN) -> linux:403 (KEY_CHANNELDOWN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:404 (KEY_FIRST) -> linux:404 (KEY_FIRST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:405 (KEY_LAST) -> linux:405 (KEY_LAST) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:406 (KEY_AB) -> linux:406 (KEY_AB) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:407 (KEY_NEXT) -> linux:407 (KEY_NEXT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:408 (KEY_RESTART) -> linux:408 (KEY_RESTART) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:409 (KEY_SLOW) -> linux:409 (KEY_SLOW) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:410 (KEY_SHUFFLE) -> linux:410 (KEY_SHUFFLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:411 (KEY_BREAK) -> linux:411 (KEY_BREAK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:412 (KEY_PREVIOUS) -> linux:412 (KEY_PREVIOUS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:413 (KEY_DIGITS) -> linux:413 (KEY_DIGITS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:414 (KEY_TEEN) -> linux:414 (KEY_TEEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:415 (KEY_TWEN) -> linux:415 (KEY_TWEN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:416 (KEY_VIDEOPHONE) -> linux:416 (KEY_VIDEOPHONE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:417 (KEY_GAMES) -> linux:417 (KEY_GAMES) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:418 (KEY_ZOOMIN) -> linux:418 (KEY_ZOOMIN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:419 (KEY_ZOOMOUT) -> linux:419 (KEY_ZOOMOUT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:420 (KEY_ZOOMRESET) -> linux:420 (KEY_ZOOMRESET) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:421 (KEY_WORDPROCESSOR) -> linux:421 (KEY_WORDPROCESSOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:422 (KEY_EDITOR) -> linux:422 (KEY_EDITOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:423 (KEY_SPREADSHEET) -> linux:423 (KEY_SPREADSHEET) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:424 (KEY_GRAPHICSEDITOR) -> linux:424 (KEY_GRAPHICSEDITOR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:425 (KEY_PRESENTATION) -> linux:425 (KEY_PRESENTATION) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:426 (KEY_DATABASE) -> linux:426 (KEY_DATABASE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:427 (KEY_NEWS) -> linux:427 (KEY_NEWS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:428 (KEY_VOICEMAIL) -> linux:428 (KEY_VOICEMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:429 (KEY_ADDRESSBOOK) -> linux:429 (KEY_ADDRESSBOOK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:430 (KEY_MESSENGER) -> linux:430 (KEY_MESSENGER) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:431 (KEY_DISPLAYTOGGLE) -> linux:431 (KEY_DISPLAYTOGGLE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:432 (KEY_SPELLCHECK) -> linux:432 (KEY_SPELLCHECK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:433 (KEY_LOGOFF) -> linux:433 (KEY_LOGOFF) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:434 (KEY_DOLLAR) -> linux:434 (KEY_DOLLAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:435 (KEY_EURO) -> linux:435 (KEY_EURO) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:436 (KEY_FRAMEBACK) -> linux:436 (KEY_FRAMEBACK) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:437 (KEY_FRAMEFORWARD) -> linux:437 (KEY_FRAMEFORWARD) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:438 (KEY_CONTEXT_MENU) -> linux:438 (KEY_CONTEXT_MENU) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:439 (KEY_MEDIA_REPEAT) -> linux:439 (KEY_MEDIA_REPEAT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:440 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:441 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:442 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:443 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:444 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:445 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:446 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:447 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:448 (KEY_DEL_EOL) -> linux:448 (KEY_DEL_EOL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:449 (KEY_DEL_EOS) -> linux:449 (KEY_DEL_EOS) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:450 (KEY_INS_LINE) -> linux:450 (KEY_INS_LINE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:451 (KEY_DEL_LINE) -> linux:451 (KEY_DEL_LINE) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:452 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:453 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:454 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:455 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:456 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:457 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:458 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:459 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:460 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:461 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:462 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:463 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:464 (KEY_FN) -> linux:464 (KEY_FN) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:465 (KEY_FN_ESC) -> linux:465 (KEY_FN_ESC) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:466 (KEY_FN_F1) -> linux:466 (KEY_FN_F1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:467 (KEY_FN_F2) -> linux:467 (KEY_FN_F2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:468 (KEY_FN_F3) -> linux:468 (KEY_FN_F3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:469 (KEY_FN_F4) -> linux:469 (KEY_FN_F4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:470 (KEY_FN_F5) -> linux:470 (KEY_FN_F5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:471 (KEY_FN_F6) -> linux:471 (KEY_FN_F6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:472 (KEY_FN_F7) -> linux:472 (KEY_FN_F7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:473 (KEY_FN_F8) -> linux:473 (KEY_FN_F8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:474 (KEY_FN_F9) -> linux:474 (KEY_FN_F9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:475 (KEY_FN_F10) -> linux:475 (KEY_FN_F10) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:476 (KEY_FN_F11) -> linux:476 (KEY_FN_F11) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:477 (KEY_FN_F12) -> linux:477 (KEY_FN_F12) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:478 (KEY_FN_1) -> linux:478 (KEY_FN_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:479 (KEY_FN_2) -> linux:479 (KEY_FN_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:480 (KEY_FN_D) -> linux:480 (KEY_FN_D) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:481 (KEY_FN_E) -> linux:481 (KEY_FN_E) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:482 (KEY_FN_F) -> linux:482 (KEY_FN_F) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:483 (KEY_FN_S) -> linux:483 (KEY_FN_S) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:484 (KEY_FN_B) -> linux:484 (KEY_FN_B) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:485 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:486 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:487 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:488 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:489 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:490 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:491 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:492 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:493 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:494 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:495 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:496 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:497 (KEY_BRL_DOT1) -> linux:497 (KEY_BRL_DOT1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:498 (KEY_BRL_DOT2) -> linux:498 (KEY_BRL_DOT2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:499 (KEY_BRL_DOT3) -> linux:499 (KEY_BRL_DOT3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:500 (KEY_BRL_DOT4) -> linux:500 (KEY_BRL_DOT4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:501 (KEY_BRL_DOT5) -> linux:501 (KEY_BRL_DOT5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:502 (KEY_BRL_DOT6) -> linux:502 (KEY_BRL_DOT6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:503 (KEY_BRL_DOT7) -> linux:503 (KEY_BRL_DOT7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:504 (KEY_BRL_DOT8) -> linux:504 (KEY_BRL_DOT8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:505 (KEY_BRL_DOT9) -> linux:505 (KEY_BRL_DOT9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:506 (KEY_BRL_DOT10) -> linux:506 (KEY_BRL_DOT10) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:507 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:508 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:509 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:510 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:511 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:512 (KEY_NUMERIC_0) -> linux:512 (KEY_NUMERIC_0) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:513 (KEY_NUMERIC_1) -> linux:513 (KEY_NUMERIC_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:514 (KEY_NUMERIC_2) -> linux:514 (KEY_NUMERIC_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:515 (KEY_NUMERIC_3) -> linux:515 (KEY_NUMERIC_3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:516 (KEY_NUMERIC_4) -> linux:516 (KEY_NUMERIC_4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:517 (KEY_NUMERIC_5) -> linux:517 (KEY_NUMERIC_5) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:518 (KEY_NUMERIC_6) -> linux:518 (KEY_NUMERIC_6) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:519 (KEY_NUMERIC_7) -> linux:519 (KEY_NUMERIC_7) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:520 (KEY_NUMERIC_8) -> linux:520 (KEY_NUMERIC_8) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:521 (KEY_NUMERIC_9) -> linux:521 (KEY_NUMERIC_9) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:522 (KEY_NUMERIC_STAR) -> linux:522 (KEY_NUMERIC_STAR) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:523 (KEY_NUMERIC_POUND) -> linux:523 (KEY_NUMERIC_POUND) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* linux:524 (KEY_RFKILL) -> linux:524 (KEY_RFKILL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ -}; -const size_t qemu_input_map_linux_to_qcode_len = sizeof(qemu_input_map_linux_to_qcode) / sizeof(qemu_input_map_linux_to_qcode[0]); diff --git a/pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.h b/pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.h deleted file mode 100644 index 818a6529a1..0000000000 --- a/pcsx2/USB/qemu-usb/input-keymap-linux-to-qcode.h +++ /dev/null @@ -1,18 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "hid.h" -extern const QKeyCode qemu_input_map_linux_to_qcode[]; -extern const size_t qemu_input_map_linux_to_qcode_len; diff --git a/pcsx2/USB/qemu-usb/input-keymap-qcode-to-qnum.cpp b/pcsx2/USB/qemu-usb/input-keymap-qcode-to-qnum.cpp index 0cd5cdf476..77121a1b09 100644 --- a/pcsx2/USB/qemu-usb/input-keymap-qcode-to-qnum.cpp +++ b/pcsx2/USB/qemu-usb/input-keymap-qcode-to-qnum.cpp @@ -17,7 +17,7 @@ #include "input-keymap.h" //TODO how much does std::map kill perf if any? -const std::map qemu_input_map_qcode_to_qnum = { +static const std::map qemu_input_map_qcode_to_qnum = { {Q_KEY_CODE_0, 0xb}, /* qcode:Q_KEY_CODE_0 (0) -> linux:11 (KEY_0) -> qnum:11 */ {Q_KEY_CODE_1, 0x2}, /* qcode:Q_KEY_CODE_1 (1) -> linux:2 (KEY_1) -> qnum:2 */ {Q_KEY_CODE_2, 0x3}, /* qcode:Q_KEY_CODE_2 (2) -> linux:3 (KEY_2) -> qnum:3 */ diff --git a/pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.cpp b/pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.cpp deleted file mode 100644 index 3bedb6e5f6..0000000000 --- a/pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -/* - * This file is auto-generated from keymaps.csv on 2018-12-22 15:48 - * To re-generate, run: - * keymap-gen --lang=stdc++ --varname=qemu_input_map_win32_to_qcode code-map keymaps.csv win32 qcode -*/ -#include "PrecompiledHeader.h" -#include "input-keymap-win32-to-qcode.h" -const std::array qemu_input_map_win32_to_qcode = { - Q_KEY_CODE_UNMAPPED, /* win32:0 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:1 (VK_LBUTTON) -> linux:256 (BTN_0) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:2 (VK_RBUTTON) -> linux:257 (BTN_1) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:3 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:4 (VK_MBUTTON) -> linux:258 (BTN_2) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:5 (VK_XBUTTON1) -> linux:259 (BTN_3) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:6 (VK_XBUTTON2) -> linux:260 (BTN_4) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:7 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_BACKSPACE, /* win32:8 (VK_BACK) -> linux:14 (KEY_BACKSPACE) -> qcode:Q_KEY_CODE_BACKSPACE (backspace) */ - Q_KEY_CODE_TAB, /* win32:9 (VK_TAB) -> linux:15 (KEY_TAB) -> qcode:Q_KEY_CODE_TAB (tab) */ - Q_KEY_CODE_UNMAPPED, /* win32:10 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:11 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:12 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_RET, /* win32:13 (VK_RETURN) -> linux:28 (KEY_ENTER) -> qcode:Q_KEY_CODE_RET (ret) */ - Q_KEY_CODE_UNMAPPED, /* win32:14 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:15 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_SHIFT, /* win32:16 (VK_LSHIFT) -> linux:42 (KEY_LEFTSHIFT) -> qcode:Q_KEY_CODE_SHIFT (shift) */ - Q_KEY_CODE_CTRL, /* win32:17 (VK_CONTROL) -> linux:29 (KEY_LEFTCTRL) -> qcode:Q_KEY_CODE_CTRL (ctrl) */ - Q_KEY_CODE_ALT, /* win32:18 (VK_MENU) -> linux:56 (KEY_LEFTALT) -> qcode:Q_KEY_CODE_ALT (alt) */ - Q_KEY_CODE_PAUSE, /* win32:19 (VK_PAUSE) -> linux:119 (KEY_PAUSE) -> qcode:Q_KEY_CODE_PAUSE (pause) */ - Q_KEY_CODE_CAPS_LOCK, /* win32:20 (VK_CAPITAL) -> linux:58 (KEY_CAPSLOCK) -> qcode:Q_KEY_CODE_CAPS_LOCK (caps_lock) */ - Q_KEY_CODE_UNMAPPED, /* win32:21 (VK_HANGEUL) -> linux:122 (KEY_HANGEUL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:22 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:23 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:24 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:25 (VK_HANJA) -> linux:123 (KEY_HANJA) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:26 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_ESC, /* win32:27 (VK_ESCAPE) -> linux:1 (KEY_ESC) -> qcode:Q_KEY_CODE_ESC (esc) */ - Q_KEY_CODE_UNMAPPED, /* win32:28 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:29 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:30 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:31 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_SPC, /* win32:32 (VK_SPACE) -> linux:57 (KEY_SPACE) -> qcode:Q_KEY_CODE_SPC (spc) */ - Q_KEY_CODE_PGUP, /* win32:33 (VK_PRIOR) -> linux:104 (KEY_PAGEUP) -> qcode:Q_KEY_CODE_PGUP (pgup) */ - Q_KEY_CODE_PGDN, /* win32:34 (VK_NEXT) -> linux:109 (KEY_PAGEDOWN) -> qcode:Q_KEY_CODE_PGDN (pgdn) */ - Q_KEY_CODE_END, /* win32:35 (VK_END) -> linux:107 (KEY_END) -> qcode:Q_KEY_CODE_END (end) */ - Q_KEY_CODE_HOME, /* win32:36 (VK_HOME) -> linux:102 (KEY_HOME) -> qcode:Q_KEY_CODE_HOME (home) */ - Q_KEY_CODE_LEFT, /* win32:37 (VK_LEFT) -> linux:105 (KEY_LEFT) -> qcode:Q_KEY_CODE_LEFT (left) */ - Q_KEY_CODE_UP, /* win32:38 (VK_UP) -> linux:103 (KEY_UP) -> qcode:Q_KEY_CODE_UP (up) */ - Q_KEY_CODE_RIGHT, /* win32:39 (VK_RIGHT) -> linux:106 (KEY_RIGHT) -> qcode:Q_KEY_CODE_RIGHT (right) */ - Q_KEY_CODE_DOWN, /* win32:40 (VK_DOWN) -> linux:108 (KEY_DOWN) -> qcode:Q_KEY_CODE_DOWN (down) */ - Q_KEY_CODE_UNMAPPED, /* win32:41 (VK_SELECT) -> linux:353 (KEY_SELECT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:42 (VK_PRINT) -> linux:210 (KEY_PRINT) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:43 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_SYSRQ, /* win32:44 (VK_SNAPSHOT) -> linux:99 (KEY_SYSRQ) -> qcode:Q_KEY_CODE_SYSRQ (sysrq) */ - Q_KEY_CODE_INSERT, /* win32:45 (VK_INSERT) -> linux:110 (KEY_INSERT) -> qcode:Q_KEY_CODE_INSERT (insert) */ - Q_KEY_CODE_DELETE, /* win32:46 (VK_DELETE) -> linux:111 (KEY_DELETE) -> qcode:Q_KEY_CODE_DELETE (delete) */ - Q_KEY_CODE_HELP, /* win32:47 (VK_HELP) -> linux:138 (KEY_HELP) -> qcode:Q_KEY_CODE_HELP (help) */ - Q_KEY_CODE_0, /* win32:48 (VK_0) -> linux:11 (KEY_0) -> qcode:Q_KEY_CODE_0 (0) */ - Q_KEY_CODE_1, /* win32:49 (VK_1) -> linux:2 (KEY_1) -> qcode:Q_KEY_CODE_1 (1) */ - Q_KEY_CODE_2, /* win32:50 (VK_2) -> linux:3 (KEY_2) -> qcode:Q_KEY_CODE_2 (2) */ - Q_KEY_CODE_3, /* win32:51 (VK_3) -> linux:4 (KEY_3) -> qcode:Q_KEY_CODE_3 (3) */ - Q_KEY_CODE_4, /* win32:52 (VK_4) -> linux:5 (KEY_4) -> qcode:Q_KEY_CODE_4 (4) */ - Q_KEY_CODE_5, /* win32:53 (VK_5) -> linux:6 (KEY_5) -> qcode:Q_KEY_CODE_5 (5) */ - Q_KEY_CODE_6, /* win32:54 (VK_6) -> linux:7 (KEY_6) -> qcode:Q_KEY_CODE_6 (6) */ - Q_KEY_CODE_7, /* win32:55 (VK_7) -> linux:8 (KEY_7) -> qcode:Q_KEY_CODE_7 (7) */ - Q_KEY_CODE_8, /* win32:56 (VK_8) -> linux:9 (KEY_8) -> qcode:Q_KEY_CODE_8 (8) */ - Q_KEY_CODE_9, /* win32:57 (VK_9) -> linux:10 (KEY_9) -> qcode:Q_KEY_CODE_9 (9) */ - Q_KEY_CODE_UNMAPPED, /* win32:58 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:59 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:60 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:61 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:62 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:63 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:64 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_A, /* win32:65 (VK_A) -> linux:30 (KEY_A) -> qcode:Q_KEY_CODE_A (a) */ - Q_KEY_CODE_B, /* win32:66 (VK_B) -> linux:48 (KEY_B) -> qcode:Q_KEY_CODE_B (b) */ - Q_KEY_CODE_C, /* win32:67 (VK_C) -> linux:46 (KEY_C) -> qcode:Q_KEY_CODE_C (c) */ - Q_KEY_CODE_D, /* win32:68 (VK_D) -> linux:32 (KEY_D) -> qcode:Q_KEY_CODE_D (d) */ - Q_KEY_CODE_E, /* win32:69 (VK_E) -> linux:18 (KEY_E) -> qcode:Q_KEY_CODE_E (e) */ - Q_KEY_CODE_F, /* win32:70 (VK_F) -> linux:33 (KEY_F) -> qcode:Q_KEY_CODE_F (f) */ - Q_KEY_CODE_G, /* win32:71 (VK_G) -> linux:34 (KEY_G) -> qcode:Q_KEY_CODE_G (g) */ - Q_KEY_CODE_H, /* win32:72 (VK_H) -> linux:35 (KEY_H) -> qcode:Q_KEY_CODE_H (h) */ - Q_KEY_CODE_I, /* win32:73 (VK_I) -> linux:23 (KEY_I) -> qcode:Q_KEY_CODE_I (i) */ - Q_KEY_CODE_J, /* win32:74 (VK_J) -> linux:36 (KEY_J) -> qcode:Q_KEY_CODE_J (j) */ - Q_KEY_CODE_K, /* win32:75 (VK_K) -> linux:37 (KEY_K) -> qcode:Q_KEY_CODE_K (k) */ - Q_KEY_CODE_L, /* win32:76 (VK_L) -> linux:38 (KEY_L) -> qcode:Q_KEY_CODE_L (l) */ - Q_KEY_CODE_M, /* win32:77 (VK_M) -> linux:50 (KEY_M) -> qcode:Q_KEY_CODE_M (m) */ - Q_KEY_CODE_N, /* win32:78 (VK_N) -> linux:49 (KEY_N) -> qcode:Q_KEY_CODE_N (n) */ - Q_KEY_CODE_O, /* win32:79 (VK_O) -> linux:24 (KEY_O) -> qcode:Q_KEY_CODE_O (o) */ - Q_KEY_CODE_P, /* win32:80 (VK_P) -> linux:25 (KEY_P) -> qcode:Q_KEY_CODE_P (p) */ - Q_KEY_CODE_Q, /* win32:81 (VK_Q) -> linux:16 (KEY_Q) -> qcode:Q_KEY_CODE_Q (q) */ - Q_KEY_CODE_R, /* win32:82 (VK_R) -> linux:19 (KEY_R) -> qcode:Q_KEY_CODE_R (r) */ - Q_KEY_CODE_S, /* win32:83 (VK_S) -> linux:31 (KEY_S) -> qcode:Q_KEY_CODE_S (s) */ - Q_KEY_CODE_T, /* win32:84 (VK_T) -> linux:20 (KEY_T) -> qcode:Q_KEY_CODE_T (t) */ - Q_KEY_CODE_U, /* win32:85 (VK_U) -> linux:22 (KEY_U) -> qcode:Q_KEY_CODE_U (u) */ - Q_KEY_CODE_V, /* win32:86 (VK_V) -> linux:47 (KEY_V) -> qcode:Q_KEY_CODE_V (v) */ - Q_KEY_CODE_W, /* win32:87 (VK_W) -> linux:17 (KEY_W) -> qcode:Q_KEY_CODE_W (w) */ - Q_KEY_CODE_X, /* win32:88 (VK_X) -> linux:45 (KEY_X) -> qcode:Q_KEY_CODE_X (x) */ - Q_KEY_CODE_Y, /* win32:89 (VK_Y) -> linux:21 (KEY_Y) -> qcode:Q_KEY_CODE_Y (y) */ - Q_KEY_CODE_Z, /* win32:90 (VK_Z) -> linux:44 (KEY_Z) -> qcode:Q_KEY_CODE_Z (z) */ - Q_KEY_CODE_META_L, /* win32:91 (VK_LWIN) -> linux:125 (KEY_LEFTMETA) -> qcode:Q_KEY_CODE_META_L (meta_l) */ - Q_KEY_CODE_META_R, /* win32:92 (VK_RWIN) -> linux:126 (KEY_RIGHTMETA) -> qcode:Q_KEY_CODE_META_R (meta_r) */ - Q_KEY_CODE_COMPOSE, /* win32:93 (VK_APPS) -> linux:127 (KEY_COMPOSE) -> qcode:Q_KEY_CODE_COMPOSE (compose) */ - Q_KEY_CODE_UNMAPPED, /* win32:94 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_SLEEP, /* win32:95 (VK_SLEEP) -> linux:142 (KEY_SLEEP) -> qcode:Q_KEY_CODE_SLEEP (sleep) */ - Q_KEY_CODE_KP_0, /* win32:96 (VK_NUMPAD0) -> linux:82 (KEY_KP0) -> qcode:Q_KEY_CODE_KP_0 (kp_0) */ - Q_KEY_CODE_KP_1, /* win32:97 (VK_NUMPAD1) -> linux:79 (KEY_KP1) -> qcode:Q_KEY_CODE_KP_1 (kp_1) */ - Q_KEY_CODE_KP_2, /* win32:98 (VK_NUMPAD2) -> linux:80 (KEY_KP2) -> qcode:Q_KEY_CODE_KP_2 (kp_2) */ - Q_KEY_CODE_KP_3, /* win32:99 (VK_NUMPAD3) -> linux:81 (KEY_KP3) -> qcode:Q_KEY_CODE_KP_3 (kp_3) */ - Q_KEY_CODE_KP_4, /* win32:100 (VK_NUMPAD4) -> linux:75 (KEY_KP4) -> qcode:Q_KEY_CODE_KP_4 (kp_4) */ - Q_KEY_CODE_KP_5, /* win32:101 (VK_NUMPAD5) -> linux:76 (KEY_KP5) -> qcode:Q_KEY_CODE_KP_5 (kp_5) */ - Q_KEY_CODE_KP_6, /* win32:102 (VK_NUMPAD6) -> linux:77 (KEY_KP6) -> qcode:Q_KEY_CODE_KP_6 (kp_6) */ - Q_KEY_CODE_KP_7, /* win32:103 (VK_NUMPAD7) -> linux:71 (KEY_KP7) -> qcode:Q_KEY_CODE_KP_7 (kp_7) */ - Q_KEY_CODE_KP_8, /* win32:104 (VK_NUMPAD8) -> linux:72 (KEY_KP8) -> qcode:Q_KEY_CODE_KP_8 (kp_8) */ - Q_KEY_CODE_KP_9, /* win32:105 (VK_NUMPAD9) -> linux:73 (KEY_KP9) -> qcode:Q_KEY_CODE_KP_9 (kp_9) */ - Q_KEY_CODE_KP_MULTIPLY, /* win32:106 (VK_MULTIPLY) -> linux:55 (KEY_KPASTERISK) -> qcode:Q_KEY_CODE_KP_MULTIPLY (kp_multiply) */ - Q_KEY_CODE_KP_ADD, /* win32:107 (VK_ADD) -> linux:78 (KEY_KPPLUS) -> qcode:Q_KEY_CODE_KP_ADD (kp_add) */ - Q_KEY_CODE_KP_COMMA, /* win32:108 (VK_SEPARATOR??) -> linux:121 (KEY_KPCOMMA) -> qcode:Q_KEY_CODE_KP_COMMA (kp_comma) */ - Q_KEY_CODE_KP_SUBTRACT, /* win32:109 (VK_SUBTRACT) -> linux:74 (KEY_KPMINUS) -> qcode:Q_KEY_CODE_KP_SUBTRACT (kp_subtract) */ - Q_KEY_CODE_KP_DECIMAL, /* win32:110 (VK_DECIMAL) -> linux:83 (KEY_KPDOT) -> qcode:Q_KEY_CODE_KP_DECIMAL (kp_decimal) */ - Q_KEY_CODE_KP_DIVIDE, /* win32:111 (VK_DIVIDE) -> linux:98 (KEY_KPSLASH) -> qcode:Q_KEY_CODE_KP_DIVIDE (kp_divide) */ - Q_KEY_CODE_F1, /* win32:112 (VK_F1) -> linux:59 (KEY_F1) -> qcode:Q_KEY_CODE_F1 (f1) */ - Q_KEY_CODE_F2, /* win32:113 (VK_F2) -> linux:60 (KEY_F2) -> qcode:Q_KEY_CODE_F2 (f2) */ - Q_KEY_CODE_F3, /* win32:114 (VK_F3) -> linux:61 (KEY_F3) -> qcode:Q_KEY_CODE_F3 (f3) */ - Q_KEY_CODE_F4, /* win32:115 (VK_F4) -> linux:62 (KEY_F4) -> qcode:Q_KEY_CODE_F4 (f4) */ - Q_KEY_CODE_F5, /* win32:116 (VK_F5) -> linux:63 (KEY_F5) -> qcode:Q_KEY_CODE_F5 (f5) */ - Q_KEY_CODE_F6, /* win32:117 (VK_F6) -> linux:64 (KEY_F6) -> qcode:Q_KEY_CODE_F6 (f6) */ - Q_KEY_CODE_F7, /* win32:118 (VK_F7) -> linux:65 (KEY_F7) -> qcode:Q_KEY_CODE_F7 (f7) */ - Q_KEY_CODE_F8, /* win32:119 (VK_F8) -> linux:66 (KEY_F8) -> qcode:Q_KEY_CODE_F8 (f8) */ - Q_KEY_CODE_F9, /* win32:120 (VK_F9) -> linux:67 (KEY_F9) -> qcode:Q_KEY_CODE_F9 (f9) */ - Q_KEY_CODE_F10, /* win32:121 (VK_F10) -> linux:68 (KEY_F10) -> qcode:Q_KEY_CODE_F10 (f10) */ - Q_KEY_CODE_F11, /* win32:122 (VK_F11) -> linux:87 (KEY_F11) -> qcode:Q_KEY_CODE_F11 (f11) */ - Q_KEY_CODE_F12, /* win32:123 (VK_F12) -> linux:88 (KEY_F12) -> qcode:Q_KEY_CODE_F12 (f12) */ - Q_KEY_CODE_UNMAPPED, /* win32:124 (VK_F13) -> linux:183 (KEY_F13) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:125 (VK_F14) -> linux:184 (KEY_F14) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:126 (VK_F15) -> linux:185 (KEY_F15) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:127 (VK_F16) -> linux:186 (KEY_F16) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:128 (VK_F17) -> linux:187 (KEY_F17) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:129 (VK_F18) -> linux:188 (KEY_F18) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:130 (VK_F19) -> linux:189 (KEY_F19) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:131 (VK_F20) -> linux:190 (KEY_F20) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:132 (VK_F21) -> linux:191 (KEY_F21) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:133 (VK_F22) -> linux:192 (KEY_F22) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:134 (VK_F23) -> linux:193 (KEY_F23) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:135 (VK_F24) -> linux:194 (KEY_F24) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:136 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:137 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:138 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:139 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:140 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:141 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:142 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:143 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_NUM_LOCK, /* win32:144 (VK_NUMLOCK) -> linux:69 (KEY_NUMLOCK) -> qcode:Q_KEY_CODE_NUM_LOCK (num_lock) */ - Q_KEY_CODE_SCROLL_LOCK, /* win32:145 (VK_SCROLL) -> linux:70 (KEY_SCROLLLOCK) -> qcode:Q_KEY_CODE_SCROLL_LOCK (scroll_lock) */ - Q_KEY_CODE_UNMAPPED, /* win32:146 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:147 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:148 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:149 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:150 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:151 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:152 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:153 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:154 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:155 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:156 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:157 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:158 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:159 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_SHIFT, /* win32:160 (VK_LSHIFT) -> linux:42 (KEY_LEFTSHIFT) -> qcode:Q_KEY_CODE_SHIFT (shift) */ - Q_KEY_CODE_SHIFT_R, /* win32:161 (VK_RSHIFT) -> linux:54 (KEY_RIGHTSHIFT) -> qcode:Q_KEY_CODE_SHIFT_R (shift_r) */ - Q_KEY_CODE_CTRL, /* win32:162 (VK_CONTROL) -> linux:29 (KEY_LEFTCTRL) -> qcode:Q_KEY_CODE_CTRL (ctrl) */ - Q_KEY_CODE_CTRL_R, /* win32:163 (VK_RCONTROL) -> linux:97 (KEY_RIGHTCTRL) -> qcode:Q_KEY_CODE_CTRL_R (ctrl_r) */ - Q_KEY_CODE_ALT, /* win32:164 (VK_MENU) -> linux:56 (KEY_LEFTALT) -> qcode:Q_KEY_CODE_ALT (alt) */ - Q_KEY_CODE_ALT_R, /* win32:165 (VK_RMENU) -> linux:100 (KEY_RIGHTALT) -> qcode:Q_KEY_CODE_ALT_R (alt_r) */ - Q_KEY_CODE_AC_BACK, /* win32:166 (VK_BROWSER_BACK) -> linux:158 (KEY_BACK) -> qcode:Q_KEY_CODE_AC_BACK (ac_back) */ - Q_KEY_CODE_AC_FORWARD, /* win32:167 (VK_BROWSER_FORWARD) -> linux:159 (KEY_FORWARD) -> qcode:Q_KEY_CODE_AC_FORWARD (ac_forward) */ - Q_KEY_CODE_AC_REFRESH, /* win32:168 (VK_BROWSER_REFRESH) -> linux:173 (KEY_REFRESH) -> qcode:Q_KEY_CODE_AC_REFRESH (ac_refresh) */ - Q_KEY_CODE_STOP, /* win32:169 (VK_BROWSER_STOP) -> linux:128 (KEY_STOP) -> qcode:Q_KEY_CODE_STOP (stop) */ - Q_KEY_CODE_UNMAPPED, /* win32:170 (VK_BROWSER_SEARCH) -> linux:217 (KEY_SEARCH) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:171 (VK_BROWSER_FAVOURITES) -> linux:364 (KEY_FAVORITES) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_AC_HOME, /* win32:172 (VK_BROWSER_HOME) -> linux:172 (KEY_HOMEPAGE) -> qcode:Q_KEY_CODE_AC_HOME (ac_home) */ - Q_KEY_CODE_AUDIOMUTE, /* win32:173 (VK_VOLUME_MUTE) -> linux:113 (KEY_MUTE) -> qcode:Q_KEY_CODE_AUDIOMUTE (audiomute) */ - Q_KEY_CODE_VOLUMEDOWN, /* win32:174 (VK_VOLUME_DOWN) -> linux:114 (KEY_VOLUMEDOWN) -> qcode:Q_KEY_CODE_VOLUMEDOWN (volumedown) */ - Q_KEY_CODE_VOLUMEUP, /* win32:175 (VK_VOLUME_UP) -> linux:115 (KEY_VOLUMEUP) -> qcode:Q_KEY_CODE_VOLUMEUP (volumeup) */ - Q_KEY_CODE_AUDIONEXT, /* win32:176 (VK_MEDIA_NEXT_TRACK) -> linux:163 (KEY_NEXTSONG) -> qcode:Q_KEY_CODE_AUDIONEXT (audionext) */ - Q_KEY_CODE_AUDIOPREV, /* win32:177 (VK_MEDIA_PREV_TRACK) -> linux:165 (KEY_PREVIOUSSONG) -> qcode:Q_KEY_CODE_AUDIOPREV (audioprev) */ - Q_KEY_CODE_AUDIOSTOP, /* win32:178 (VK_MEDIA_STOP) -> linux:166 (KEY_STOPCD) -> qcode:Q_KEY_CODE_AUDIOSTOP (audiostop) */ - Q_KEY_CODE_AUDIOPLAY, /* win32:179 (VK_MEDIA_PLAY_PAUSE) -> linux:164 (KEY_PLAYPAUSE) -> qcode:Q_KEY_CODE_AUDIOPLAY (audioplay) */ - Q_KEY_CODE_UNMAPPED, /* win32:180 (VK_LAUNCH_MAIL) -> linux:215 (KEY_EMAIL) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:181 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:182 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:183 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:184 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:185 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_SEMICOLON, /* win32:186 (VK_OEM_1) -> linux:39 (KEY_SEMICOLON) -> qcode:Q_KEY_CODE_SEMICOLON (semicolon) */ - Q_KEY_CODE_EQUAL, /* win32:187 (VK_OEM_PLUS) -> linux:13 (KEY_EQUAL) -> qcode:Q_KEY_CODE_EQUAL (equal) */ - Q_KEY_CODE_COMMA, /* win32:188 (VK_OEM_COMMA) -> linux:51 (KEY_COMMA) -> qcode:Q_KEY_CODE_COMMA (comma) */ - Q_KEY_CODE_MINUS, /* win32:189 (VK_OEM_MINUS) -> linux:12 (KEY_MINUS) -> qcode:Q_KEY_CODE_MINUS (minus) */ - Q_KEY_CODE_DOT, /* win32:190 (VK_OEM_PERIOD) -> linux:52 (KEY_DOT) -> qcode:Q_KEY_CODE_DOT (dot) */ - Q_KEY_CODE_SLASH, /* win32:191 (VK_OEM_2) -> linux:53 (KEY_SLASH) -> qcode:Q_KEY_CODE_SLASH (slash) */ - Q_KEY_CODE_GRAVE_ACCENT, /* win32:192 (VK_OEM_3) -> linux:41 (KEY_GRAVE) -> qcode:Q_KEY_CODE_GRAVE_ACCENT (grave_accent) */ - Q_KEY_CODE_UNMAPPED, /* win32:193 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:194 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:195 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:196 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:197 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:198 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:199 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:200 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:201 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:202 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:203 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:204 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:205 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:206 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:207 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:208 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:209 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:210 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:211 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:212 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:213 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:214 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:215 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:216 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:217 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:218 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_BRACKET_LEFT, /* win32:219 (VK_OEM_4) -> linux:26 (KEY_LEFTBRACE) -> qcode:Q_KEY_CODE_BRACKET_LEFT (bracket_left) */ - Q_KEY_CODE_BACKSLASH, /* win32:220 (VK_OEM_5) -> linux:43 (KEY_BACKSLASH) -> qcode:Q_KEY_CODE_BACKSLASH (backslash) */ - Q_KEY_CODE_BRACKET_RIGHT, /* win32:221 (VK_OEM_6) -> linux:27 (KEY_RIGHTBRACE) -> qcode:Q_KEY_CODE_BRACKET_RIGHT (bracket_right) */ - Q_KEY_CODE_APOSTROPHE, /* win32:222 (VK_OEM_7) -> linux:40 (KEY_APOSTROPHE) -> qcode:Q_KEY_CODE_APOSTROPHE (apostrophe) */ - Q_KEY_CODE_UNMAPPED, /* win32:223 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:224 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_LESS, /* win32:225 (VK_OEM_102) -> linux:86 (KEY_102ND) -> qcode:Q_KEY_CODE_LESS (less) */ - Q_KEY_CODE_UNMAPPED, /* win32:226 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:227 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:228 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:229 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:230 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:231 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:232 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:233 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:234 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:235 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:236 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:237 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:238 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:239 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:240 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:241 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:242 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:243 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:244 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:245 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:246 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:247 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:248 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:249 (unnamed) -> linux:None (unnamed) -> qcode:None (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:250 (VK_PLAY) -> linux:207 (KEY_PLAY) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ - Q_KEY_CODE_UNMAPPED, /* win32:251 (VK_ZOOM) -> linux:372 (KEY_ZOOM) -> qcode:Q_KEY_CODE_UNMAPPED (unnamed) */ -}; diff --git a/pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.h b/pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.h deleted file mode 100644 index 024661b1df..0000000000 --- a/pcsx2/USB/qemu-usb/input-keymap-win32-to-qcode.h +++ /dev/null @@ -1,24 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -/* - * This file is auto-generated from keymaps.csv on 2018-12-22 15:48 - * Database checksum sha256(ef8f29f4e4294479e2789aa61e410c4b0464d4f0ad16bcc1526086a4f123bc10) - * To re-generate, run: - * keymap-gen --lang=stdc++ --varname=qemu_input_map_win32_to_qcode code-map keymaps.csv win32 qcode -*/ -#include -#include "hid.h" -extern const std::array qemu_input_map_win32_to_qcode; diff --git a/pcsx2/USB/qemu-usb/input-keymap.h b/pcsx2/USB/qemu-usb/input-keymap.h index b8f065df8d..0287e64ab6 100644 --- a/pcsx2/USB/qemu-usb/input-keymap.h +++ b/pcsx2/USB/qemu-usb/input-keymap.h @@ -13,10 +13,8 @@ * If not, see . */ -#include #include "hid.h" -extern const std::map qemu_input_map_qcode_to_qnum; int qemu_input_qcode_to_number(const QKeyCode value); int qemu_input_key_value_to_number(const KeyValue* value); int qemu_input_key_value_to_scancode(const KeyValue* value, bool down, int* codes); diff --git a/pcsx2/USB/qemu-usb/iov.cpp b/pcsx2/USB/qemu-usb/iov.cpp index ad428f557c..47a8b66523 100644 --- a/pcsx2/USB/qemu-usb/iov.cpp +++ b/pcsx2/USB/qemu-usb/iov.cpp @@ -17,15 +17,14 @@ */ #include "PrecompiledHeader.h" -#include "vl.h" -//#include "qemu-common.h" -#include "iov.h" -#include "glib.h" -//#include "qemu/sockets.h" -//#include "qemu/cutils.h" +#include "USB/qemu-usb/iov.h" +#include #include +#include -size_t iov_from_buf_full(const struct iovec* iov, unsigned int iov_cnt, + // TODO(Stenzek): Rewrite this stuff to LGPL. + +size_t iov_from_buf(const struct usb_iovec* iov, unsigned int iov_cnt, size_t offset, const void* buf, size_t bytes) { size_t done; @@ -34,7 +33,7 @@ size_t iov_from_buf_full(const struct iovec* iov, unsigned int iov_cnt, { if (offset < iov[i].iov_len) { - size_t len = MIN(iov[i].iov_len - offset, bytes - done); + size_t len = std::min(iov[i].iov_len - offset, bytes - done); memcpy((char*)iov[i].iov_base + offset, (char*)buf + done, len); done += len; offset = 0; @@ -48,7 +47,7 @@ size_t iov_from_buf_full(const struct iovec* iov, unsigned int iov_cnt, return done; } -size_t iov_to_buf_full(const struct iovec* iov, const unsigned int iov_cnt, +size_t iov_to_buf(const struct usb_iovec* iov, const unsigned int iov_cnt, size_t offset, void* buf, size_t bytes) { size_t done; @@ -57,7 +56,7 @@ size_t iov_to_buf_full(const struct iovec* iov, const unsigned int iov_cnt, { if (offset < iov[i].iov_len) { - size_t len = MIN(iov[i].iov_len - offset, bytes - done); + size_t len = std::min(iov[i].iov_len - offset, bytes - done); memcpy((char*)buf + done, (char*)iov[i].iov_base + offset, len); done += len; offset = 0; @@ -71,7 +70,7 @@ size_t iov_to_buf_full(const struct iovec* iov, const unsigned int iov_cnt, return done; } -size_t iov_memset(const struct iovec* iov, const unsigned int iov_cnt, +size_t iov_memset(const struct usb_iovec* iov, const unsigned int iov_cnt, size_t offset, int fillc, size_t bytes) { size_t done; @@ -80,7 +79,7 @@ size_t iov_memset(const struct iovec* iov, const unsigned int iov_cnt, { if (offset < iov[i].iov_len) { - size_t len = MIN(iov[i].iov_len - offset, bytes - done); + size_t len = std::min(iov[i].iov_len - offset, bytes - done); memset((char*)iov[i].iov_base + offset, fillc, len); done += len; offset = 0; @@ -94,67 +93,17 @@ size_t iov_memset(const struct iovec* iov, const unsigned int iov_cnt, return done; } -size_t iov_size(const struct iovec* iov, const unsigned int iov_cnt) -{ - size_t len; - unsigned int i; - - len = 0; - for (i = 0; i < iov_cnt; i++) - { - len += iov[i].iov_len; - } - return len; -} - -unsigned iov_copy(struct iovec* dst_iov, unsigned int dst_iov_cnt, - const struct iovec* iov, unsigned int iov_cnt, - size_t offset, size_t bytes) -{ - size_t len; - unsigned int i, j; - for (i = 0, j = 0; - i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) - { - if (offset >= iov[i].iov_len) - { - offset -= iov[i].iov_len; - continue; - } - len = MIN(bytes, iov[i].iov_len - offset); - - dst_iov[j].iov_base = (char*)iov[i].iov_base + offset; - dst_iov[j].iov_len = len; - j++; - bytes -= len; - offset = 0; - } - assert(offset == 0); - return j; -} /* io vectors */ -void qemu_iovec_init(QEMUIOVector* qiov, int alloc_hint) +void qemu_iovec_init(QEMUIOVector* qiov) { - qiov->iov = my_g_new(struct iovec, alloc_hint); + qiov->iov = (struct usb_iovec*)std::malloc(sizeof(usb_iovec)); qiov->niov = 0; - qiov->nalloc = alloc_hint; + qiov->nalloc = 1; qiov->size = 0; } -void qemu_iovec_init_external(QEMUIOVector* qiov, struct iovec* iov, int niov) -{ - int i; - - qiov->iov = iov; - qiov->niov = niov; - qiov->nalloc = -1; - qiov->size = 0; - for (i = 0; i < niov; i++) - qiov->size += iov[i].iov_len; -} - void qemu_iovec_add(QEMUIOVector* qiov, void* base, size_t len) { assert(qiov->nalloc != -1); @@ -162,7 +111,7 @@ void qemu_iovec_add(QEMUIOVector* qiov, void* base, size_t len) if (qiov->niov == qiov->nalloc) { qiov->nalloc = 2 * qiov->nalloc + 1; - qiov->iov = my_g_renew(struct iovec, qiov->iov, qiov->nalloc); + qiov->iov = (struct usb_iovec*)std::realloc(qiov->iov, sizeof(usb_iovec) * qiov->nalloc); } qiov->iov[qiov->niov].iov_base = base; qiov->iov[qiov->niov].iov_len = len; @@ -170,90 +119,12 @@ void qemu_iovec_add(QEMUIOVector* qiov, void* base, size_t len) ++qiov->niov; } -/* - * Concatenates (partial) iovecs from src_iov to the end of dst. - * It starts copying after skipping `soffset' bytes at the - * beginning of src and adds individual vectors from src to - * dst copies up to `sbytes' bytes total, or up to the end - * of src_iov if it comes first. This way, it is okay to specify - * very large value for `sbytes' to indicate "up to the end - * of src". - * Only vector pointers are processed, not the actual data buffers. - */ -size_t qemu_iovec_concat_iov(QEMUIOVector* dst, - struct iovec* src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes) -{ - unsigned int i; - size_t done; - - if (!sbytes) - { - return 0; - } - assert(dst->nalloc != -1); - for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) - { - if (soffset < src_iov[i].iov_len) - { - size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done); - qemu_iovec_add(dst, (char*)src_iov[i].iov_base + soffset, len); - done += len; - soffset = 0; - } - else - { - soffset -= src_iov[i].iov_len; - } - } - assert(soffset == 0); /* offset beyond end of src */ - - return done; -} - -/* - * Concatenates (partial) iovecs from src to the end of dst. - * It starts copying after skipping `soffset' bytes at the - * beginning of src and adds individual vectors from src to - * dst copies up to `sbytes' bytes total, or up to the end - * of src if it comes first. This way, it is okay to specify - * very large value for `sbytes' to indicate "up to the end - * of src". - * Only vector pointers are processed, not the actual data buffers. - */ -void qemu_iovec_concat(QEMUIOVector* dst, - QEMUIOVector* src, size_t soffset, size_t sbytes) -{ - qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes); -} - -/* - * Check if the contents of the iovecs are all zero - */ -/*bool qemu_iovec_is_zero(QEMUIOVector *qiov) -{ - int i; - for (i = 0; i < qiov->niov; i++) { - size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long)); - uint8_t *ptr = (uint8_t *)qiov->iov[i].iov_base; - if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) { - return false; - } - for (; offs < qiov->iov[i].iov_len; offs++) { - if (ptr[offs]) { - return false; - } - } - } - return true; -}*/ - void qemu_iovec_destroy(QEMUIOVector* qiov) { assert(qiov->nalloc != -1); qemu_iovec_reset(qiov); - my_g_free(qiov->iov); + std::free(qiov->iov); qiov->nalloc = 0; qiov->iov = NULL; } @@ -265,161 +136,3 @@ void qemu_iovec_reset(QEMUIOVector* qiov) qiov->niov = 0; qiov->size = 0; } - -size_t qemu_iovec_to_buf(QEMUIOVector* qiov, size_t offset, - void* buf, size_t bytes) -{ - return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes); -} - -size_t qemu_iovec_from_buf(QEMUIOVector* qiov, size_t offset, - const void* buf, size_t bytes) -{ - return iov_from_buf(qiov->iov, qiov->niov, offset, buf, bytes); -} - -size_t qemu_iovec_memset(QEMUIOVector* qiov, size_t offset, - int fillc, size_t bytes) -{ - return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes); -} - -/** - * Check that I/O vector contents are identical - * - * The IO vectors must have the same structure (same length of all parts). - * A typical usage is to compare vectors created with qemu_iovec_clone(). - * - * @a: I/O vector - * @b: I/O vector - * @ret: Offset to first mismatching byte or -1 if match - */ -ssize_t qemu_iovec_compare(QEMUIOVector* a, QEMUIOVector* b) -{ - int i; - ssize_t offset = 0; - - assert(a->niov == b->niov); - for (i = 0; i < a->niov; i++) - { - size_t len = 0; - uint8_t* p = (uint8_t*)a->iov[i].iov_base; - uint8_t* q = (uint8_t*)b->iov[i].iov_base; - - assert(a->iov[i].iov_len == b->iov[i].iov_len); - while (len < a->iov[i].iov_len && *p++ == *q++) - { - len++; - } - - offset += len; - - if (len != a->iov[i].iov_len) - { - return offset; - } - } - return -1; -} - -typedef struct -{ - int src_index; - struct iovec* src_iov; - void* dest_base; -} IOVectorSortElem; - -static int sortelem_cmp_src_base(const void* a, const void* b) -{ - const IOVectorSortElem* elem_a = (const IOVectorSortElem*)a; - const IOVectorSortElem* elem_b = (const IOVectorSortElem*)b; - - /* Don't overflow */ - if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) - { - return -1; - } - else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) - { - return 1; - } - else - { - return 0; - } -} - -static int sortelem_cmp_src_index(const void* a, const void* b) -{ - const IOVectorSortElem* elem_a = (const IOVectorSortElem*)a; - const IOVectorSortElem* elem_b = (const IOVectorSortElem*)b; - - return elem_a->src_index - elem_b->src_index; -} - -size_t iov_discard_front(struct iovec** iov, unsigned int* iov_cnt, - size_t bytes) -{ - size_t total = 0; - struct iovec* cur; - - for (cur = *iov; *iov_cnt > 0; cur++) - { - if (cur->iov_len > bytes) - { - cur->iov_base = (uint8_t*)cur->iov_base + bytes; - cur->iov_len -= bytes; - total += bytes; - break; - } - - bytes -= cur->iov_len; - total += cur->iov_len; - *iov_cnt -= 1; - } - - *iov = cur; - return total; -} - -size_t iov_discard_back(struct iovec* iov, unsigned int* iov_cnt, - size_t bytes) -{ - size_t total = 0; - struct iovec* cur; - - if (*iov_cnt == 0) - { - return 0; - } - - cur = iov + (*iov_cnt - 1); - - while (*iov_cnt > 0) - { - if (cur->iov_len > bytes) - { - cur->iov_len -= bytes; - total += bytes; - break; - } - - bytes -= cur->iov_len; - total += cur->iov_len; - cur--; - *iov_cnt -= 1; - } - - return total; -} - -void qemu_iovec_discard_back(QEMUIOVector* qiov, size_t bytes) -{ - unsigned int niov = qiov->niov; - - assert(qiov->size >= bytes); - assert(iov_discard_back(qiov->iov, &niov, bytes) == bytes); - - qiov->niov = niov; - qiov->size -= bytes; -} diff --git a/pcsx2/USB/qemu-usb/iov.h b/pcsx2/USB/qemu-usb/iov.h index 6798b7eff8..64e306341d 100644 --- a/pcsx2/USB/qemu-usb/iov.h +++ b/pcsx2/USB/qemu-usb/iov.h @@ -11,24 +11,13 @@ * the COPYING file in the top-level directory. */ -#include "USB/platcompat.h" +#pragma once -#ifndef IOV_H -#define IOV_H - -#if !defined(_BITS_UIO_H) && !defined(__iovec_defined) /* /usr/include/bits/uio.h */ -struct iovec +struct usb_iovec { void* iov_base; size_t iov_len; }; -#endif - -/** - * count and return data size, in bytes, of an iovec - * starting at `iov' of `iov_cnt' number of elements. - */ -size_t iov_size(const struct iovec* iov, const unsigned int iov_cnt); /** * Copy from single continuous buffer to scatter-gather vector of buffers @@ -47,43 +36,11 @@ size_t iov_size(const struct iovec* iov, const unsigned int iov_cnt); * such "large" value is -1 (sinice size_t is unsigned), * so specifying `-1' as `bytes' means 'up to the end of iovec'. */ -size_t iov_from_buf_full(const struct iovec* iov, unsigned int iov_cnt, +size_t iov_from_buf(const struct usb_iovec* iov, unsigned int iov_cnt, size_t offset, const void* buf, size_t bytes); -size_t iov_to_buf_full(const struct iovec* iov, const unsigned int iov_cnt, +size_t iov_to_buf(const struct usb_iovec* iov, const unsigned int iov_cnt, size_t offset, void* buf, size_t bytes); -static inline size_t -iov_from_buf(const struct iovec* iov, unsigned int iov_cnt, - size_t offset, const void* buf, size_t bytes) -{ - if (__builtin_constant_p(bytes) && iov_cnt && - offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) - { - memcpy((char*)iov[0].iov_base + offset, buf, bytes); - return bytes; - } - else - { - return iov_from_buf_full(iov, iov_cnt, offset, buf, bytes); - } -} - -static inline size_t -iov_to_buf(const struct iovec* iov, const unsigned int iov_cnt, - size_t offset, void* buf, size_t bytes) -{ - if (__builtin_constant_p(bytes) && iov_cnt && - offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) - { - memcpy(buf, (char*)iov[0].iov_base + offset, bytes); - return bytes; - } - else - { - return iov_to_buf_full(iov, iov_cnt, offset, buf, bytes); - } -} - /** * Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements, * starting at byte offset `start', to value `fillc', repeating it @@ -94,91 +51,18 @@ iov_to_buf(const struct iovec* iov, const unsigned int iov_cnt, * min(size, iov_size(iov) - offset). * Again, it is okay to use large value for `bytes' to mean "up to the end". */ -size_t iov_memset(const struct iovec* iov, const unsigned int iov_cnt, +size_t iov_memset(const struct usb_iovec* iov, const unsigned int iov_cnt, size_t offset, int fillc, size_t bytes); -/* - * Send/recv data from/to iovec buffers directly - * - * `offset' bytes in the beginning of iovec buffer are skipped and - * next `bytes' bytes are used, which must be within data of iovec. - * - * r = iov_send_recv(sockfd, iov, iovcnt, offset, bytes, true); - * - * is logically equivalent to - * - * char *buf = malloc(bytes); - * iov_to_buf(iov, iovcnt, offset, buf, bytes); - * r = send(sockfd, buf, bytes, 0); - * free(buf); - * - * For iov_send_recv() _whole_ area being sent or received - * should be within the iovec, not only beginning of it. - */ -ssize_t iov_send_recv(int sockfd, const struct iovec* iov, unsigned iov_cnt, - size_t offset, size_t bytes, bool do_send); -#define iov_recv(sockfd, iov, iov_cnt, offset, bytes) \ - iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, false) -#define iov_send(sockfd, iov, iov_cnt, offset, bytes) \ - iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, true) - -/** - * Produce a text hexdump of iovec `iov' with `iov_cnt' number of elements - * in file `fp', prefixing each line with `prefix' and processing not more - * than `limit' data bytes. - */ -void iov_hexdump(const struct iovec* iov, const unsigned int iov_cnt, - FILE* fp, const char* prefix, size_t limit); - -/* - * Partial copy of vector from iov to dst_iov (data is not copied). - * dst_iov overlaps iov at a specified offset. - * size of dst_iov is at most bytes. dst vector count is returned. - */ -unsigned iov_copy(struct iovec* dst_iov, unsigned int dst_iov_cnt, - const struct iovec* iov, unsigned int iov_cnt, - size_t offset, size_t bytes); - -/* - * Remove a given number of bytes from the front or back of a vector. - * This may update iov and/or iov_cnt to exclude iovec elements that are - * no longer required. - * - * The number of bytes actually discarded is returned. This number may be - * smaller than requested if the vector is too small. - */ -size_t iov_discard_front(struct iovec** iov, unsigned int* iov_cnt, - size_t bytes); -size_t iov_discard_back(struct iovec* iov, unsigned int* iov_cnt, - size_t bytes); - typedef struct QEMUIOVector { - struct iovec* iov; + struct usb_iovec* iov; int niov; int nalloc; size_t size; } QEMUIOVector; -void qemu_iovec_init(QEMUIOVector* qiov, int alloc_hint); -void qemu_iovec_init_external(QEMUIOVector* qiov, struct iovec* iov, int niov); +void qemu_iovec_init(QEMUIOVector* qiov); void qemu_iovec_add(QEMUIOVector* qiov, void* base, size_t len); -void qemu_iovec_concat(QEMUIOVector* dst, - QEMUIOVector* src, size_t soffset, size_t sbytes); -size_t qemu_iovec_concat_iov(QEMUIOVector* dst, - struct iovec* src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes); -bool qemu_iovec_is_zero(QEMUIOVector* qiov); void qemu_iovec_destroy(QEMUIOVector* qiov); void qemu_iovec_reset(QEMUIOVector* qiov); -size_t qemu_iovec_to_buf(QEMUIOVector* qiov, size_t offset, - void* buf, size_t bytes); -size_t qemu_iovec_from_buf(QEMUIOVector* qiov, size_t offset, - const void* buf, size_t bytes); -size_t qemu_iovec_memset(QEMUIOVector* qiov, size_t offset, - int fillc, size_t bytes); -ssize_t qemu_iovec_compare(QEMUIOVector* a, QEMUIOVector* b); -void qemu_iovec_clone(QEMUIOVector* dest, const QEMUIOVector* src, void* buf); -void qemu_iovec_discard_back(QEMUIOVector* qiov, size_t bytes); - -#endif diff --git a/pcsx2/USB/qemu-usb/qusb.h b/pcsx2/USB/qemu-usb/qusb.h index 53d57af491..525ff2970f 100644 --- a/pcsx2/USB/qemu-usb/qusb.h +++ b/pcsx2/USB/qemu-usb/qusb.h @@ -22,8 +22,9 @@ * THE SOFTWARE. */ -#include "iov.h" -#include "queue.h" +#pragma once +#include "USB/qemu-usb/iov.h" +#include "USB/qemu-usb/queue.h" #define USB_TOKEN_SETUP 0x2d #define USB_TOKEN_IN 0x69 /* device -> host */ @@ -388,9 +389,6 @@ typedef struct USBDeviceClass int streams); void (*free_streams)(USBDevice* dev, USBEndpoint** eps, int nr_eps); - int (*open)(USBDevice* dev); - void (*close)(USBDevice* dev); - const char* product_desc; const USBDesc* usb_desc; bool attached_settable; diff --git a/pcsx2/USB/qemu-usb/usb-hub.cpp b/pcsx2/USB/qemu-usb/usb-hub.cpp deleted file mode 100644 index 83d18686ac..0000000000 --- a/pcsx2/USB/qemu-usb/usb-hub.cpp +++ /dev/null @@ -1,594 +0,0 @@ -/* - * QEMU USB HUB emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "vl.h" - -//#define DEBUG - -#define MAX_PORTS 8 - -typedef struct USBHubPort -{ - USBPort port; - uint16_t wPortStatus; - uint16_t wPortChange; -} USBHubPort; - -typedef struct USBHubState -{ - USBDevice dev; - int nb_ports; - USBHubPort ports[MAX_PORTS]; -} USBHubState; - -#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) -#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) -#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) -#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) -#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) -#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) -#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) - -#define PORT_STAT_CONNECTION 0x0001 -#define PORT_STAT_ENABLE 0x0002 -#define PORT_STAT_SUSPEND 0x0004 -#define PORT_STAT_OVERCURRENT 0x0008 -#define PORT_STAT_RESET 0x0010 -#define PORT_STAT_POWER 0x0100 -#define PORT_STAT_LOW_SPEED 0x0200 -#define PORT_STAT_HIGH_SPEED 0x0400 -#define PORT_STAT_TEST 0x0800 -#define PORT_STAT_INDICATOR 0x1000 - -#define PORT_STAT_C_CONNECTION 0x0001 -#define PORT_STAT_C_ENABLE 0x0002 -#define PORT_STAT_C_SUSPEND 0x0004 -#define PORT_STAT_C_OVERCURRENT 0x0008 -#define PORT_STAT_C_RESET 0x0010 - -#define PORT_CONNECTION 0 -#define PORT_ENABLE 1 -#define PORT_SUSPEND 2 -#define PORT_OVERCURRENT 3 -#define PORT_RESET 4 -#define PORT_POWER 8 -#define PORT_LOWSPEED 9 -#define PORT_HIGHSPEED 10 -#define PORT_C_CONNECTION 16 -#define PORT_C_ENABLE 17 -#define PORT_C_SUSPEND 18 -#define PORT_C_OVERCURRENT 19 -#define PORT_C_RESET 20 -#define PORT_TEST 21 -#define PORT_INDICATOR 22 - -/* same as Linux kernel root hubs */ - -static const uint8_t qemu_hub_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x01, /* u16 bcdUSB; v1.1 */ - - 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x00, 0x00, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x01, 0x01, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -/* XXX: patch interrupt size */ -static const uint8_t qemu_hub_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* u8 if_bInterfaceSubClass; */ - 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x00, /* u8 if_iInterface; */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ - 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - -static const uint8_t qemu_hub_hub_descriptor[] = - { - 0x00, /* u8 bLength; patched in later */ - 0x29, /* u8 bDescriptorType; Hub-descriptor */ - 0x00, /* u8 bNbrPorts; (patched later) */ - 0x0a, /* u16 wHubCharacteristics; */ - 0x00, /* (per-port OC, no power switching) */ - 0x01, /* u8 bPwrOn2pwrGood; 2ms */ - 0x00 /* u8 bHubContrCurrent; 0 mA */ - - /* DeviceRemovable and PortPwrCtrlMask patched in later */ -}; - -static void usb_hub_attach(USBPort* port1, USBDevice* dev) -{ - USBHubState* s = (USBHubState*)port1->opaque; - USBHubPort* port = (USBHubPort*)&s->ports[port1->index]; - - if (dev) - { - if (port->port.dev) - usb_attach(port1, NULL); - - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (dev->speed == USB_SPEED_LOW) - port->wPortStatus |= PORT_STAT_LOW_SPEED; - else - port->wPortStatus &= ~PORT_STAT_LOW_SPEED; - port->port.dev = dev; - /* send the attach message */ - dev->handle_packet(dev, - USB_MSG_ATTACH, 0, 0, NULL, 0); - } - else - { - dev = port->port.dev; - if (dev) - { - port->wPortStatus &= ~PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->wPortStatus & PORT_STAT_ENABLE) - { - port->wPortStatus &= ~PORT_STAT_ENABLE; - port->wPortChange |= PORT_STAT_C_ENABLE; - } - /* send the detach message */ - dev->handle_packet(dev, - USB_MSG_DETACH, 0, 0, NULL, 0); - port->port.dev = NULL; - } - } -} - -static void usb_hub_handle_reset(USBDevice* dev) -{ - /* XXX: do it */ -} - -static int usb_hub_handle_control(USBDevice* dev, int request, int value, - int index, int length, uint8_t* data) -{ - USBHubState* s = (USBHubState*)dev; - int ret; - - switch (request) - { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) - { - dev->remote_wakeup = 0; - } - else - { - goto fail; - } - ret = 0; - break; - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0 && index != 0x81) - { /* clear ep halt */ - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) - { - dev->remote_wakeup = 1; - } - else - { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) - { - case USB_DT_DEVICE: - memcpy(data, qemu_hub_dev_descriptor, - sizeof(qemu_hub_dev_descriptor)); - ret = sizeof(qemu_hub_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_hub_config_descriptor, - sizeof(qemu_hub_config_descriptor)); - - /* status change endpoint size based on number - * of ports */ - data[22] = (s->nb_ports + 1 + 7) / 8; - - ret = sizeof(qemu_hub_config_descriptor); - break; - case USB_DT_STRING: - switch (value & 0xff) - { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "314159"); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB Hub"); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "PCSX2/QEMU"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_INTERFACE: - data[0] = 0; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_INTERFACE: - ret = 0; - break; - /* usb specific requests */ - case GetHubStatus: - data[0] = 0; - data[1] = 0; - data[2] = 0; - data[3] = 0; - ret = 4; - break; - case GetPortStatus: - { - unsigned int n = index - 1; - USBHubPort* port; - if (n >= s->nb_ports) - goto fail; - port = &s->ports[n]; - data[0] = port->wPortStatus; - data[1] = port->wPortStatus >> 8; - data[2] = port->wPortChange; - data[3] = port->wPortChange >> 8; - ret = 4; - } - break; - case SetHubFeature: - case ClearHubFeature: - if (value == 0 || value == 1) - { - } - else - { - goto fail; - } - ret = 0; - break; - case SetPortFeature: - { - unsigned int n = index - 1; - USBHubPort* port; - USBDevice* dev; - if (n >= s->nb_ports) - goto fail; - port = &s->ports[n]; - dev = port->port.dev; - switch (value) - { - case PORT_SUSPEND: - port->wPortStatus |= PORT_STAT_SUSPEND; - break; - case PORT_RESET: - if (dev) - { - dev->handle_packet(dev, - USB_MSG_RESET, 0, 0, NULL, 0); - port->wPortChange |= PORT_STAT_C_RESET; - /* set enable bit */ - port->wPortStatus |= PORT_STAT_ENABLE; - } - break; - case PORT_POWER: - break; - default: - goto fail; - } - ret = 0; - } - break; - case ClearPortFeature: - { - unsigned int n = index - 1; - USBHubPort* port; - USBDevice* dev; - if (n >= s->nb_ports) - goto fail; - port = &s->ports[n]; - dev = port->port.dev; - switch (value) - { - case PORT_ENABLE: - port->wPortStatus &= ~PORT_STAT_ENABLE; - break; - case PORT_C_ENABLE: - port->wPortChange &= ~PORT_STAT_C_ENABLE; - break; - case PORT_SUSPEND: - port->wPortStatus &= ~PORT_STAT_SUSPEND; - break; - case PORT_C_SUSPEND: - port->wPortChange &= ~PORT_STAT_C_SUSPEND; - break; - case PORT_C_CONNECTION: - port->wPortChange &= ~PORT_STAT_C_CONNECTION; - break; - case PORT_C_OVERCURRENT: - port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; - break; - case PORT_C_RESET: - port->wPortChange &= ~PORT_STAT_C_RESET; - break; - default: - goto fail; - } - ret = 0; - } - break; - case GetHubDescriptor: - { - unsigned int n, limit, var_hub_size = 0; - memcpy(data, qemu_hub_hub_descriptor, - sizeof(qemu_hub_hub_descriptor)); - data[2] = s->nb_ports; - - /* fill DeviceRemovable bits */ - limit = ((s->nb_ports + 1 + 7) / 8) + 7; - for (n = 7; n < limit; n++) - { - data[n] = 0x00; - var_hub_size++; - } - - /* fill PortPwrCtrlMask bits */ - limit = limit + ((s->nb_ports + 7) / 8); - for (; n < limit; n++) - { - data[n] = 0xff; - var_hub_size++; - } - - ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; - data[0] = ret; - break; - } - default: - fail: - ret = USB_RET_STALL; - break; - } - return ret; -} - -static int usb_hub_handle_data(USBDevice* dev, int pid, - uint8_t devep, uint8_t* data, int len) -{ - int ret = 0; - switch (pid) - { - case USB_TOKEN_IN: - if (devep == 1) - { - USBHubState* s = (USBHubState*)dev; - unsigned int status; - int i, n; - n = (s->nb_ports + 1 + 7) / 8; - if (len == 1) - { /* FreeBSD workaround */ - n = 1; - } - else if (n > len) - { - return USB_RET_BABBLE; - } - status = 0; - for (i = 0; i < s->nb_ports; i++) - { - USBHubPort* port = &s->ports[i]; - if (port->wPortChange) - status |= (1 << (i + 1)); - } - if (status != 0) - { - for (i = 0; i < n; i++) - { - data[i] = status >> (8 * i); - } - ret = n; - } - else - { - ret = USB_RET_NAK; /* usb11 11.13.1 */ - } - } - else - { - goto fail; - } - break; - case USB_TOKEN_OUT: - default: - fail: - ret = USB_RET_STALL; - break; - } - return ret; -} - -static int usb_hub_broadcast_packet(USBHubState* s, int pid, - uint8_t devaddr, uint8_t devep, - uint8_t* data, int len) -{ - for (int i = 0; i < s->nb_ports; i++) - { - USBHubPort* port = &s->ports[i]; - USBDevice* dev = port->port.dev; - if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) - { - int ret = dev->handle_packet(dev, pid, - devaddr, devep, - data, len); - if (ret != USB_RET_NODEV) - { - return ret; - } - } - } - return USB_RET_NODEV; -} - -static int usb_hub_handle_packet(USBDevice* dev, int pid, - uint8_t devaddr, uint8_t devep, - uint8_t* data, int len) -{ - USBHubState* s = (USBHubState*)dev; - - if (dev->state == USB_STATE_DEFAULT && - dev->addr != 0 && - devaddr != dev->addr && - (pid == USB_TOKEN_SETUP || - pid == USB_TOKEN_OUT || - pid == USB_TOKEN_IN)) - { - /* broadcast the packet to the devices */ - return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len); - } - return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); -} - -static void usb_hub_handle_destroy(USBDevice* dev) -{ - USBHubState* s = (USBHubState*)dev; - - free(s); -} - -USBDevice* usb_hub_init(int nb_ports) -{ - if (nb_ports > MAX_PORTS) - return NULL; - USBHubState* s = (USBHubState*)qemu_mallocz(sizeof(USBHubState)); - if (!s) - return NULL; - s->dev.speed = USB_SPEED_FULL; - s->dev.handle_packet = usb_hub_handle_packet; - - /* generic USB device init */ - s->dev.handle_reset = usb_hub_handle_reset; - s->dev.handle_control = usb_hub_handle_control; - s->dev.handle_data = usb_hub_handle_data; - s->dev.handle_destroy = usb_hub_handle_destroy; - - strncpy(s->dev.devname, "QEMU USB Hub", sizeof(s->dev.devname)); - - s->nb_ports = nb_ports; - for (int i = 0; i < s->nb_ports; i++) - { - USBHubPort* port = &s->ports[i]; - port->port.opaque = s; - port->port.index = i; - port->port.attach = usb_hub_attach; - port->wPortStatus = PORT_STAT_POWER; - port->wPortChange = 0; - } - return (USBDevice*)s; -} diff --git a/pcsx2/USB/qemu-usb/usb-ohci.cpp b/pcsx2/USB/qemu-usb/usb-ohci.cpp index 5e7748c130..1e01997169 100644 --- a/pcsx2/USB/qemu-usb/usb-ohci.cpp +++ b/pcsx2/USB/qemu-usb/usb-ohci.cpp @@ -29,19 +29,20 @@ //typedef CPUReadMemoryFunc #include "PrecompiledHeader.h" -#include "vl.h" -#include "queue.h" -#include "USBinternal.h" +#include "USB/qemu-usb/qusb.h" +#include "USB/qemu-usb/queue.h" +#include "USB/qemu-usb/USBinternal.h" +#include "IopMem.h" #define DMA_DIRECTION_TO_DEVICE 0 #define DMA_DIRECTION_FROM_DEVICE 1 #define ED_LINK_LIMIT 32 -int64_t last_cycle = 0; +extern int64_t g_usb_last_cycle; #define MIN_IRQ_INTERVAL 64 /* hack */ -extern int64_t get_clock(); -extern int get_ticks_per_second(); +extern int64_t usb_get_clock(); +extern int usb_get_ticks_per_second(); extern void usbIrq(int); //#define DEBUG_PACKET @@ -49,6 +50,27 @@ extern void usbIrq(int); static void ohci_async_cancel_device(OHCIState* ohci, USBDevice* dev); +static uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + union + { + uint64_t ll; + struct + { + uint32_t low, high; + } l; + } u, res; + uint64_t rl, rh; + + u.ll = a; + rl = (uint64_t)u.l.low * (uint64_t)b; + rh = (uint64_t)u.l.high * (uint64_t)b; + rh += (rl >> 32); + res.l.high = rh / c; + res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; + return res.ll; +} + /* Update IRQ levels */ static inline void ohci_intr_update(OHCIState* ohci) { @@ -61,10 +83,10 @@ static inline void ohci_intr_update(OHCIState* ohci) if (level) { - if ((get_clock() - last_cycle) > MIN_IRQ_INTERVAL) + if ((usb_get_clock() - g_usb_last_cycle) > MIN_IRQ_INTERVAL) { usbIrq(1); - last_cycle = get_clock(); + g_usb_last_cycle = usb_get_clock(); } } } @@ -334,68 +356,43 @@ void ohci_hard_reset(OHCIState* ohci) ohci_roothub_reset(ohci); } -#define le32_to_cpu(x) (x) -#define cpu_to_le32(x) (x) -#define le16_to_cpu(x) (x) -#define cpu_to_le16(x) (x) - /* Get an array of dwords from main memory */ -static inline int get_dwords(uint32_t addr, uint32_t* buf, int num) +__fi static int get_dwords(uint32_t addr, uint32_t* buf, uint32_t num) { - int i; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) - { - if (cpu_physical_memory_rw(addr, (uint8_t*)buf, sizeof(*buf), 0)) - return 0; - *buf = le32_to_cpu(*buf); - } + if ((addr + (num * sizeof(uint32_t))) > sizeof(iopMem->Main)) + return 0; + std::memcpy(buf, iopMem->Main + addr, num * sizeof(uint32_t)); return 1; } /* Get an array of words from main memory */ -static inline int get_words(uint32_t addr, uint16_t* buf, int num) +__fi static int get_words(uint32_t addr, uint16_t* buf, uint32_t num) { - int i; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) - { - if (cpu_physical_memory_rw(addr, (uint8_t*)buf, sizeof(*buf), 0)) - return 0; - *buf = le16_to_cpu(*buf); - } + if ((addr + (num * sizeof(uint16_t))) > sizeof(iopMem->Main)) + return 0; + std::memcpy(buf, iopMem->Main + addr, num * sizeof(uint16_t)); return 1; } /* Put an array of dwords in to main memory */ -static inline int put_dwords(uint32_t addr, uint32_t* buf, int num) +__fi static int put_dwords(uint32_t addr, uint32_t* buf, uint32_t num) { - int i; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) - { - uint32_t tmp = cpu_to_le32(*buf); - if (cpu_physical_memory_rw(addr, (uint8_t*)&tmp, sizeof(tmp), 1)) - return 0; - } + if ((addr + (num * sizeof(uint32_t))) > sizeof(iopMem->Main)) + return 0; + std::memcpy(iopMem->Main + addr, buf, num * sizeof(uint32_t)); return 1; } /* Put an array of dwords in to main memory */ -static inline int put_words(uint32_t addr, uint16_t* buf, int num) +__fi static int put_words(uint32_t addr, uint16_t* buf, uint32_t num) { - int i; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) - { - uint16_t tmp = cpu_to_le16(*buf); - if (cpu_physical_memory_rw(addr, (uint8_t*)&tmp, sizeof(tmp), 1)) - return 0; - } + if ((addr + (num * sizeof(uint16_t))) > sizeof(iopMem->Main)) + return 0; + std::memcpy(iopMem->Main + addr, buf, num * sizeof(uint16_t)); return 1; } @@ -436,31 +433,35 @@ static inline int ohci_put_iso_td(OHCIState* ohci, uint32_t addr, struct ohci_is put_words(addr + 16, td->offset, 8); } -static inline int ohci_put_hcca(OHCIState* ohci, - struct ohci_hcca* hcca) -{ - cpu_physical_memory_write(ohci->hcca + HCCA_WRITEBACK_OFFSET, - (uint8_t*)hcca + HCCA_WRITEBACK_OFFSET, - HCCA_WRITEBACK_SIZE); - return 1; -} - /* Read/Write the contents of a TD from/to main memory. */ static int ohci_copy_td(OHCIState* ohci, struct ohci_td* td, uint8_t* buf, uint32_t len, int write) { uint32_t ptr, n; ptr = td->cbp; - n = 0x1000 - (ptr & 0xfff); - if (n > len) - n = len; - if (cpu_physical_memory_rw(ptr, buf, n, write)) + n = std::min(0x1000 - (ptr & 0xfff), len); + if ((ptr + n) > sizeof(iopMem->Main)) return 1; + + if (write) + std::memcpy(iopMem->Main + ptr, buf, len); + else + std::memcpy(buf, iopMem->Main + ptr, len); + if (n == len) return 0; ptr = td->be & ~0xfffu; buf += n; - cpu_physical_memory_rw(ptr, buf, len - n, write); + len -= n; + + if ((ptr + n) > sizeof(iopMem->Main)) + return 1; + + if (write) + std::memcpy(iopMem->Main + ptr, buf, len); + else + std::memcpy(buf, iopMem->Main + ptr, len); + return 0; } @@ -471,16 +472,30 @@ static int ohci_copy_iso_td(OHCIState* ohci, uint32_t start_addr, uint32_t end_a uint32_t ptr, n; ptr = start_addr; - n = 0x1000 - (ptr & 0xfff); - if (n > len) - n = len; - if (cpu_physical_memory_rw(ptr, buf, n, write)) + n = std::min(0x1000 - (ptr & 0xfff), len); + + if ((ptr + n) > sizeof(iopMem->Main)) return 1; + + if (write) + std::memcpy(iopMem->Main + ptr, buf, len); + else + std::memcpy(buf, iopMem->Main + ptr, len); + if (n == len) return 0; ptr = end_addr & ~0xfffu; buf += n; - cpu_physical_memory_rw(ptr, buf, len - n, write); + len -= n; + + if ((ptr + n) > sizeof(iopMem->Main)) + return 1; + + if (write) + std::memcpy(iopMem->Main + ptr, buf, len); + else + std::memcpy(buf, iopMem->Main + ptr, len); + return 0; } @@ -488,7 +503,7 @@ static void ohci_process_lists(OHCIState* ohci, int completion); static void ohci_async_complete_packet(USBPort* port, USBPacket* packet) { - OHCIState* ohci = CONTAINER_OF(packet, OHCIState, usb_packet); + OHCIState* ohci = USB_CONTAINER_OF(packet, OHCIState, usb_packet); //trace_usb_ohci_async_complete(); ohci->async_complete = true; @@ -733,7 +748,7 @@ static int ohci_service_iso_td(OHCIState* ohci, struct ohci_ed* ed, } else { - if (ret > (ssize_t)len) + if (ret > static_cast(len)) { //trace_usb_ohci_iso_td_data_overrun(ret, len); OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, @@ -1136,8 +1151,8 @@ static int ohci_service_ed_list(OHCIState* ohci, uint32_t head, int completion) /* Generate a SOF event, and set a timer for EOF */ static void ohci_sof(OHCIState* ohci) { - ohci->sof_time = get_clock(); - ohci->eof_timer = usb_frame_time; + ohci->sof_time = usb_get_clock(); + ohci->eof_timer = g_usb_frame_time; ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1170,9 +1185,14 @@ static void ohci_process_lists(OHCIState* ohci, int completion) void ohci_frame_boundary(void* opaque) { OHCIState* ohci = (OHCIState*)opaque; - struct ohci_hcca hcca; - cpu_physical_memory_read(ohci->hcca, (uint8_t*)&hcca, sizeof(hcca)); + if (ohci->hcca + sizeof(ohci_hcca) > sizeof(iopMem->Main)) + { + Console.Error("ohci->hcca pointer is out of range."); + return; + } + + ohci_hcca* hcca = reinterpret_cast(iopMem->Main + ohci->hcca); /* Process all the lists at the end of the frame */ /* if reset bit was set, don't process possibly invalid descriptors */ @@ -1183,7 +1203,7 @@ void ohci_frame_boundary(void* opaque) if (ohci->ctl & OHCI_CTL_PLE) { const int n = ohci->frame_number & 0x1f; - ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0); + ohci_service_ed_list(ohci, hcca->intr[n], 0); } /* Cancel all pending packets if either of the lists has been disabled. */ @@ -1210,7 +1230,7 @@ void ohci_frame_boundary(void* opaque) /* Increment frame number and take care of endianness. */ ohci->frame_number = (ohci->frame_number + 1) & 0xffff; - hcca.frame = cpu_to_le16(ohci->frame_number); + hcca->frame = ohci->frame_number; if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { @@ -1218,7 +1238,7 @@ void ohci_frame_boundary(void* opaque) abort(); if (ohci->intr & ohci->intr_status) ohci->done |= 1; - hcca.done = cpu_to_le32(ohci->done); + hcca->done = ohci->done; ohci->done = 0; ohci->done_count = 7; ohci_set_interrupt(ohci, OHCI_INTR_WD); @@ -1229,9 +1249,6 @@ void ohci_frame_boundary(void* opaque) /* Do SOF stuff here */ ohci_sof(ohci); - - /* Writeback HCCA */ - ohci_put_hcca(ohci, &hcca); } /* Start sending SOF tokens across the USB bus, lists are processed in @@ -1361,13 +1378,13 @@ static uint32_t ohci_get_frame_remaining(OHCIState* ohci) /* Being in USB operational state guarnatees sof_time was * set already. */ - tks = get_clock() - ohci->sof_time; + tks = usb_get_clock() - ohci->sof_time; /* avoid muldiv if possible */ - if (tks >= usb_frame_time) + if (tks >= g_usb_frame_time) return (ohci->frt << 31); - tks = muldiv64(1, tks, usb_bit_time); + tks = muldiv64(1, tks, g_usb_bit_time); fr = (uint16_t)(ohci->fi - tks); return (ohci->frt << 31) | fr; @@ -1717,26 +1734,26 @@ OHCIState* ohci_create(uint32_t base, int ports) return NULL; int i; - const int ticks_per_sec = get_ticks_per_second(); + const int ticks_per_sec = usb_get_ticks_per_second(); memset(ohci, 0, sizeof(OHCIState)); ohci->mem_base = base; - if (usb_frame_time == 0) + if (g_usb_frame_time == 0) { #if OHCI_TIME_WARP - usb_frame_time = ticks_per_sec; - usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ / 1000); + g_usb_frame_time = ticks_per_sec; + g_usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ / 1000); #else - usb_frame_time = muldiv64(1, ticks_per_sec, 1000); + g_usb_frame_time = muldiv64(1, ticks_per_sec, 1000); if (ticks_per_sec >= USB_HZ) { - usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ); + g_usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ); } else { - usb_bit_time = 1; + g_usb_bit_time = 1; } #endif } diff --git a/pcsx2/USB/qemu-usb/vl.cpp b/pcsx2/USB/qemu-usb/vl.cpp deleted file mode 100644 index 01971a233e..0000000000 --- a/pcsx2/USB/qemu-usb/vl.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "vl.h" - -/* compute with 96 bit intermediate result: (a*b)/c */ -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) -{ - union - { - uint64_t ll; - struct - { -#ifdef WORDS_BIGENDIAN - uint32_t high, low; -#else - uint32_t low, high; -#endif - } l; - } u, res; - uint64_t rl, rh; - - u.ll = a; - rl = (uint64_t)u.l.low * (uint64_t)b; - rh = (uint64_t)u.l.high * (uint64_t)b; - rh += (rl >> 32); - res.l.high = rh / c; - res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; - return res.ll; -} diff --git a/pcsx2/USB/qemu-usb/vl.h b/pcsx2/USB/qemu-usb/vl.h deleted file mode 100644 index 1df9cbcd12..0000000000 --- a/pcsx2/USB/qemu-usb/vl.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * QEMU System Emulator header - * - * Copyright (c) 2003 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef VL_H -#define VL_H - -/* we put basic includes here to avoid repeating them in device drivers */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(_MSC_VER) -#define inline __inline -#endif - -#include "qusb.h" - -#ifndef glue -#define xglue(x, y) x##y -#define glue(x, y) xglue(x, y) -#define stringify(s) tostring(s) -#define tostring(s) #s -#endif - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -/* vl.c */ -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); -int cpu_physical_memory_rw(uint32_t addr, uint8_t* buf, - size_t len, int is_write); - -inline int cpu_physical_memory_read(uint32_t addr, - uint8_t* buf, size_t len) -{ - return cpu_physical_memory_rw(addr, buf, len, 0); -} - -inline int cpu_physical_memory_write(uint32_t addr, - const uint8_t* buf, size_t len) -{ - return cpu_physical_memory_rw(addr, (uint8_t*)buf, len, 1); -} - -#endif /* VL_H */ diff --git a/pcsx2/USB/shared/hidapi.cpp b/pcsx2/USB/shared/hidapi.cpp deleted file mode 100644 index 888bc4209b..0000000000 --- a/pcsx2/USB/shared/hidapi.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include -#include -#include "hidapi.h" - -_HidD_GetHidGuid HidD_GetHidGuid = NULL; -_HidD_GetAttributes HidD_GetAttributes = NULL; -_HidD_GetPreparsedData HidD_GetPreparsedData = NULL; -_HidP_GetCaps HidP_GetCaps = NULL; -_HidD_FreePreparsedData HidD_FreePreparsedData = NULL; -_HidD_GetFeature HidD_GetFeature = NULL; -_HidD_SetFeature HidD_SetFeature = NULL; -_HidP_GetSpecificButtonCaps HidP_GetSpecificButtonCaps = NULL; -_HidP_GetButtonCaps HidP_GetButtonCaps = NULL; -_HidP_GetUsages HidP_GetUsages = NULL; -_HidP_GetValueCaps HidP_GetValueCaps = NULL; -_HidP_GetUsageValue HidP_GetUsageValue = NULL; -_HidD_GetProductString HidD_GetProductString = NULL; - -static HMODULE hModHid = 0; - -int InitHid() -{ - if (hModHid) - { - return 1; - } - hModHid = LoadLibraryA("hid.dll"); - if (hModHid) - { - if ((HidD_GetHidGuid = (_HidD_GetHidGuid)GetProcAddress(hModHid, "HidD_GetHidGuid")) && - (HidD_GetAttributes = (_HidD_GetAttributes)GetProcAddress(hModHid, "HidD_GetAttributes")) && - (HidD_GetPreparsedData = (_HidD_GetPreparsedData)GetProcAddress(hModHid, "HidD_GetPreparsedData")) && - (HidP_GetCaps = (_HidP_GetCaps)GetProcAddress(hModHid, "HidP_GetCaps")) && - (HidD_FreePreparsedData = (_HidD_FreePreparsedData)GetProcAddress(hModHid, "HidD_FreePreparsedData")) && - (HidP_GetSpecificButtonCaps = (_HidP_GetSpecificButtonCaps)GetProcAddress(hModHid, "HidP_GetSpecificButtonCaps")) && - (HidP_GetButtonCaps = (_HidP_GetButtonCaps)GetProcAddress(hModHid, "HidP_GetButtonCaps")) && - (HidP_GetUsages = (_HidP_GetUsages)GetProcAddress(hModHid, "HidP_GetUsages")) && - (HidP_GetValueCaps = (_HidP_GetValueCaps)GetProcAddress(hModHid, "HidP_GetValueCaps")) && - (HidP_GetUsageValue = (_HidP_GetUsageValue)GetProcAddress(hModHid, "HidP_GetUsageValue")) && - (HidD_GetProductString = (_HidD_GetProductString)GetProcAddress(hModHid, "HidD_GetProductString")) && - (HidD_GetFeature = (_HidD_GetFeature)GetProcAddress(hModHid, "HidD_GetFeature")) && - (HidD_SetFeature = (_HidD_SetFeature)GetProcAddress(hModHid, "HidD_SetFeature"))) - { - //pHidD_GetHidGuid(&GUID_DEVINTERFACE_HID); - return 1; - } - UninitHid(); - } - return 0; -} - -void UninitHid() -{ - if (hModHid) - { - FreeLibrary(hModHid); - hModHid = 0; - } -} diff --git a/pcsx2/USB/shared/hidapi.h b/pcsx2/USB/shared/hidapi.h deleted file mode 100644 index 2d5c798e32..0000000000 --- a/pcsx2/USB/shared/hidapi.h +++ /dev/null @@ -1,476 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef HIDAPI_H -#define HIDAPI_H - -#include - -#define NTSTATUS int - -typedef USHORT USAGE, *PUSAGE; - -#define HID_USAGE_PAGE_GENERIC ((USAGE)0x01) -#define HID_USAGE_PAGE_SIMULATION ((USAGE)0x02) -#define HID_USAGE_PAGE_VR ((USAGE)0x03) -#define HID_USAGE_PAGE_SPORT ((USAGE)0x04) -#define HID_USAGE_PAGE_GAME ((USAGE)0x05) -#define HID_USAGE_PAGE_KEYBOARD ((USAGE)0x07) -#define HID_USAGE_PAGE_LED ((USAGE)0x08) -#define HID_USAGE_PAGE_BUTTON ((USAGE)0x09) -#define HID_USAGE_PAGE_ORDINAL ((USAGE)0x0A) -#define HID_USAGE_PAGE_TELEPHONY ((USAGE)0x0B) -#define HID_USAGE_PAGE_CONSUMER ((USAGE)0x0C) -#define HID_USAGE_PAGE_DIGITIZER ((USAGE)0x0D) -#define HID_USAGE_PAGE_UNICODE ((USAGE)0x10) -#define HID_USAGE_PAGE_ALPHANUMERIC ((USAGE)0x14) - - -// -// Usages from Generic Desktop Page (0x01) -// - -#define HID_USAGE_GENERIC_POINTER ((USAGE)0x01) -#define HID_USAGE_GENERIC_MOUSE ((USAGE)0x02) -#define HID_USAGE_GENERIC_JOYSTICK ((USAGE)0x04) -#define HID_USAGE_GENERIC_GAMEPAD ((USAGE)0x05) -#define HID_USAGE_GENERIC_KEYBOARD ((USAGE)0x06) -#define HID_USAGE_GENERIC_KEYPAD ((USAGE)0x07) -#define HID_USAGE_GENERIC_SYSTEM_CTL ((USAGE)0x80) - -#define HID_USAGE_GENERIC_X ((USAGE)0x30) -#define HID_USAGE_GENERIC_Y ((USAGE)0x31) -#define HID_USAGE_GENERIC_Z ((USAGE)0x32) -#define HID_USAGE_GENERIC_RX ((USAGE)0x33) -#define HID_USAGE_GENERIC_RY ((USAGE)0x34) -#define HID_USAGE_GENERIC_RZ ((USAGE)0x35) -#define HID_USAGE_GENERIC_SLIDER ((USAGE)0x36) -#define HID_USAGE_GENERIC_DIAL ((USAGE)0x37) -#define HID_USAGE_GENERIC_WHEEL ((USAGE)0x38) -#define HID_USAGE_GENERIC_HATSWITCH ((USAGE)0x39) -#define HID_USAGE_GENERIC_COUNTED_BUFFER ((USAGE)0x3A) -#define HID_USAGE_GENERIC_BYTE_COUNT ((USAGE)0x3B) -#define HID_USAGE_GENERIC_MOTION_WAKEUP ((USAGE)0x3C) -#define HID_USAGE_GENERIC_VX ((USAGE)0x40) -#define HID_USAGE_GENERIC_VY ((USAGE)0x41) -#define HID_USAGE_GENERIC_VZ ((USAGE)0x42) -#define HID_USAGE_GENERIC_VBRX ((USAGE)0x43) -#define HID_USAGE_GENERIC_VBRY ((USAGE)0x44) -#define HID_USAGE_GENERIC_VBRZ ((USAGE)0x45) -#define HID_USAGE_GENERIC_VNO ((USAGE)0x46) -#define HID_USAGE_GENERIC_SYSCTL_POWER ((USAGE)0x81) -#define HID_USAGE_GENERIC_SYSCTL_SLEEP ((USAGE)0x82) -#define HID_USAGE_GENERIC_SYSCTL_WAKE ((USAGE)0x83) -#define HID_USAGE_GENERIC_SYSCTL_CONTEXT_MENU ((USAGE)0x84) -#define HID_USAGE_GENERIC_SYSCTL_MAIN_MENU ((USAGE)0x85) -#define HID_USAGE_GENERIC_SYSCTL_APP_MENU ((USAGE)0x86) -#define HID_USAGE_GENERIC_SYSCTL_HELP_MENU ((USAGE)0x87) -#define HID_USAGE_GENERIC_SYSCTL_MENU_EXIT ((USAGE)0x88) -#define HID_USAGE_GENERIC_SYSCTL_MENU_SELECT ((USAGE)0x89) -#define HID_USAGE_GENERIC_SYSCTL_MENU_RIGHT ((USAGE)0x8A) -#define HID_USAGE_GENERIC_SYSCTL_MENU_LEFT ((USAGE)0x8B) -#define HID_USAGE_GENERIC_SYSCTL_MENU_UP ((USAGE)0x8C) -#define HID_USAGE_GENERIC_SYSCTL_MENU_DOWN ((USAGE)0x8D) - -// -// Usages from Simulation Controls Page (0x02) -// - -#define HID_USAGE_SIMULATION_RUDDER ((USAGE)0xBA) -#define HID_USAGE_SIMULATION_THROTTLE ((USAGE)0xBB) - -// -// Virtual Reality Controls Page (0x03) -// - - -// -// Sport Controls Page (0x04) -// - - -// -// Game Controls Page (0x05) -// - - -// -// Keyboard/Keypad Page (0x07) -// - -// Error "keys" -#define HID_USAGE_KEYBOARD_NOEVENT ((USAGE)0x00) -#define HID_USAGE_KEYBOARD_ROLLOVER ((USAGE)0x01) -#define HID_USAGE_KEYBOARD_POSTFAIL ((USAGE)0x02) -#define HID_USAGE_KEYBOARD_UNDEFINED ((USAGE)0x03) - -// Letters -#define HID_USAGE_KEYBOARD_aA ((USAGE)0x04) -#define HID_USAGE_KEYBOARD_zZ ((USAGE)0x1D) -// Numbers -#define HID_USAGE_KEYBOARD_ONE ((USAGE)0x1E) -#define HID_USAGE_KEYBOARD_ZERO ((USAGE)0x27) -// Modifier Keys -#define HID_USAGE_KEYBOARD_LCTRL ((USAGE)0xE0) -#define HID_USAGE_KEYBOARD_LSHFT ((USAGE)0xE1) -#define HID_USAGE_KEYBOARD_LALT ((USAGE)0xE2) -#define HID_USAGE_KEYBOARD_LGUI ((USAGE)0xE3) -#define HID_USAGE_KEYBOARD_RCTRL ((USAGE)0xE4) -#define HID_USAGE_KEYBOARD_RSHFT ((USAGE)0xE5) -#define HID_USAGE_KEYBOARD_RALT ((USAGE)0xE6) -#define HID_USAGE_KEYBOARD_RGUI ((USAGE)0xE7) -#define HID_USAGE_KEYBOARD_SCROLL_LOCK ((USAGE)0x47) -#define HID_USAGE_KEYBOARD_NUM_LOCK ((USAGE)0x53) -#define HID_USAGE_KEYBOARD_CAPS_LOCK ((USAGE)0x39) -// Funtion keys -#define HID_USAGE_KEYBOARD_F1 ((USAGE)0x3A) -#define HID_USAGE_KEYBOARD_F12 ((USAGE)0x45) - -#define HID_USAGE_KEYBOARD_RETURN ((USAGE)0x28) -#define HID_USAGE_KEYBOARD_ESCAPE ((USAGE)0x29) -#define HID_USAGE_KEYBOARD_DELETE ((USAGE)0x2A) - -#define HID_USAGE_KEYBOARD_PRINT_SCREEN ((USAGE)0x46) - -// and hundreds more... - -// -// LED Page (0x08) -// - -#define HID_USAGE_LED_NUM_LOCK ((USAGE)0x01) -#define HID_USAGE_LED_CAPS_LOCK ((USAGE)0x02) -#define HID_USAGE_LED_SCROLL_LOCK ((USAGE)0x03) -#define HID_USAGE_LED_COMPOSE ((USAGE)0x04) -#define HID_USAGE_LED_KANA ((USAGE)0x05) -#define HID_USAGE_LED_POWER ((USAGE)0x06) -#define HID_USAGE_LED_SHIFT ((USAGE)0x07) -#define HID_USAGE_LED_DO_NOT_DISTURB ((USAGE)0x08) -#define HID_USAGE_LED_MUTE ((USAGE)0x09) -#define HID_USAGE_LED_TONE_ENABLE ((USAGE)0x0A) -#define HID_USAGE_LED_HIGH_CUT_FILTER ((USAGE)0x0B) -#define HID_USAGE_LED_LOW_CUT_FILTER ((USAGE)0x0C) -#define HID_USAGE_LED_EQUALIZER_ENABLE ((USAGE)0x0D) -#define HID_USAGE_LED_SOUND_FIELD_ON ((USAGE)0x0E) -#define HID_USAGE_LED_SURROUND_FIELD_ON ((USAGE)0x0F) -#define HID_USAGE_LED_REPEAT ((USAGE)0x10) -#define HID_USAGE_LED_STEREO ((USAGE)0x11) -#define HID_USAGE_LED_SAMPLING_RATE_DETECT ((USAGE)0x12) -#define HID_USAGE_LED_SPINNING ((USAGE)0x13) -#define HID_USAGE_LED_CAV ((USAGE)0x14) -#define HID_USAGE_LED_CLV ((USAGE)0x15) -#define HID_USAGE_LED_RECORDING_FORMAT_DET ((USAGE)0x16) -#define HID_USAGE_LED_OFF_HOOK ((USAGE)0x17) -#define HID_USAGE_LED_RING ((USAGE)0x18) -#define HID_USAGE_LED_MESSAGE_WAITING ((USAGE)0x19) -#define HID_USAGE_LED_DATA_MODE ((USAGE)0x1A) -#define HID_USAGE_LED_BATTERY_OPERATION ((USAGE)0x1B) -#define HID_USAGE_LED_BATTERY_OK ((USAGE)0x1C) -#define HID_USAGE_LED_BATTERY_LOW ((USAGE)0x1D) -#define HID_USAGE_LED_SPEAKER ((USAGE)0x1E) -#define HID_USAGE_LED_HEAD_SET ((USAGE)0x1F) -#define HID_USAGE_LED_HOLD ((USAGE)0x20) -#define HID_USAGE_LED_MICROPHONE ((USAGE)0x21) -#define HID_USAGE_LED_COVERAGE ((USAGE)0x22) -#define HID_USAGE_LED_NIGHT_MODE ((USAGE)0x23) -#define HID_USAGE_LED_SEND_CALLS ((USAGE)0x24) -#define HID_USAGE_LED_CALL_PICKUP ((USAGE)0x25) -#define HID_USAGE_LED_CONFERENCE ((USAGE)0x26) -#define HID_USAGE_LED_STAND_BY ((USAGE)0x27) -#define HID_USAGE_LED_CAMERA_ON ((USAGE)0x28) -#define HID_USAGE_LED_CAMERA_OFF ((USAGE)0x29) -#define HID_USAGE_LED_ON_LINE ((USAGE)0x2A) -#define HID_USAGE_LED_OFF_LINE ((USAGE)0x2B) -#define HID_USAGE_LED_BUSY ((USAGE)0x2C) -#define HID_USAGE_LED_READY ((USAGE)0x2D) -#define HID_USAGE_LED_PAPER_OUT ((USAGE)0x2E) -#define HID_USAGE_LED_PAPER_JAM ((USAGE)0x2F) -#define HID_USAGE_LED_REMOTE ((USAGE)0x30) -#define HID_USAGE_LED_FORWARD ((USAGE)0x31) -#define HID_USAGE_LED_REVERSE ((USAGE)0x32) -#define HID_USAGE_LED_STOP ((USAGE)0x33) -#define HID_USAGE_LED_REWIND ((USAGE)0x34) -#define HID_USAGE_LED_FAST_FORWARD ((USAGE)0x35) -#define HID_USAGE_LED_PLAY ((USAGE)0x36) -#define HID_USAGE_LED_PAUSE ((USAGE)0x37) -#define HID_USAGE_LED_RECORD ((USAGE)0x38) -#define HID_USAGE_LED_ERROR ((USAGE)0x39) -#define HID_USAGE_LED_SELECTED_INDICATOR ((USAGE)0x3A) -#define HID_USAGE_LED_IN_USE_INDICATOR ((USAGE)0x3B) -#define HID_USAGE_LED_MULTI_MODE_INDICATOR ((USAGE)0x3C) -#define HID_USAGE_LED_INDICATOR_ON ((USAGE)0x3D) -#define HID_USAGE_LED_INDICATOR_FLASH ((USAGE)0x3E) -#define HID_USAGE_LED_INDICATOR_SLOW_BLINK ((USAGE)0x3F) -#define HID_USAGE_LED_INDICATOR_FAST_BLINK ((USAGE)0x40) -#define HID_USAGE_LED_INDICATOR_OFF ((USAGE)0x41) -#define HID_USAGE_LED_FLASH_ON_TIME ((USAGE)0x42) -#define HID_USAGE_LED_SLOW_BLINK_ON_TIME ((USAGE)0x43) -#define HID_USAGE_LED_SLOW_BLINK_OFF_TIME ((USAGE)0x44) -#define HID_USAGE_LED_FAST_BLINK_ON_TIME ((USAGE)0x45) -#define HID_USAGE_LED_FAST_BLINK_OFF_TIME ((USAGE)0x46) -#define HID_USAGE_LED_INDICATOR_COLOR ((USAGE)0x47) -#define HID_USAGE_LED_RED ((USAGE)0x48) -#define HID_USAGE_LED_GREEN ((USAGE)0x49) -#define HID_USAGE_LED_AMBER ((USAGE)0x4A) -#define HID_USAGE_LED_GENERIC_INDICATOR ((USAGE)0x3B) - -// -// Button Page (0x09) -// -// There is no need to label these usages. -// - - -// -// Ordinal Page (0x0A) -// -// There is no need to label these usages. -// - - -// -// Telephony Device Page (0x0B) -// - -#define HID_USAGE_TELEPHONY_PHONE ((USAGE)0x01) -#define HID_USAGE_TELEPHONY_ANSWERING_MACHINE ((USAGE)0x02) -#define HID_USAGE_TELEPHONY_MESSAGE_CONTROLS ((USAGE)0x03) -#define HID_USAGE_TELEPHONY_HANDSET ((USAGE)0x04) -#define HID_USAGE_TELEPHONY_HEADSET ((USAGE)0x05) -#define HID_USAGE_TELEPHONY_KEYPAD ((USAGE)0x06) -#define HID_USAGE_TELEPHONY_PROGRAMMABLE_BUTTON ((USAGE)0x07) - -// BUGBUG defined in ntstatus.h -#ifndef FACILITY_HID_ERROR_CODE -#define FACILITY_HID_ERROR_CODE 0x11 -#endif - -#define HIDP_ERROR_CODES(SEV, CODE) \ - ((NTSTATUS)(((SEV) << 28) | (FACILITY_HID_ERROR_CODE << 16) | (CODE))) - -#define HIDP_STATUS_SUCCESS (HIDP_ERROR_CODES(0x0, 0)) -#define HIDP_STATUS_NULL (HIDP_ERROR_CODES(0x8, 1)) -#define HIDP_STATUS_INVALID_PREPARSED_DATA (HIDP_ERROR_CODES(0xC, 1)) -#define HIDP_STATUS_INVALID_REPORT_TYPE (HIDP_ERROR_CODES(0xC, 2)) -#define HIDP_STATUS_INVALID_REPORT_LENGTH (HIDP_ERROR_CODES(0xC, 3)) -#define HIDP_STATUS_USAGE_NOT_FOUND (HIDP_ERROR_CODES(0xC, 4)) -#define HIDP_STATUS_VALUE_OUT_OF_RANGE (HIDP_ERROR_CODES(0xC, 5)) -#define HIDP_STATUS_BAD_LOG_PHY_VALUES (HIDP_ERROR_CODES(0xC, 6)) -#define HIDP_STATUS_BUFFER_TOO_SMALL (HIDP_ERROR_CODES(0xC, 7)) -#define HIDP_STATUS_INTERNAL_ERROR (HIDP_ERROR_CODES(0xC, 8)) -#define HIDP_STATUS_I8242_TRANS_UNKNOWN (HIDP_ERROR_CODES(0xC, 9)) -#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID (HIDP_ERROR_CODES(0xC, 0xA)) -#define HIDP_STATUS_NOT_VALUE_ARRAY (HIDP_ERROR_CODES(0xC, 0xB)) -#define HIDP_STATUS_IS_VALUE_ARRAY (HIDP_ERROR_CODES(0xC, 0xC)) -#define HIDP_STATUS_DATA_INDEX_NOT_FOUND (HIDP_ERROR_CODES(0xC, 0xD)) -#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE (HIDP_ERROR_CODES(0xC, 0xE)) -#define HIDP_STATUS_BUTTON_NOT_PRESSED (HIDP_ERROR_CODES(0xC, 0xF)) -#define HIDP_STATUS_REPORT_DOES_NOT_EXIST (HIDP_ERROR_CODES(0xC, 0x10)) -#define HIDP_STATUS_NOT_IMPLEMENTED (HIDP_ERROR_CODES(0xC, 0x20)) - -typedef enum _HIDP_REPORT_TYPE -{ - HidP_Input, - HidP_Output, - HidP_Feature -} HIDP_REPORT_TYPE; - -typedef struct _USAGE_AND_PAGE -{ - USAGE Usage; - USAGE UsagePage; -} USAGE_AND_PAGE, *PUSAGE_AND_PAGE; - -typedef struct _HIDP_BUTTON_CAPS -{ - USAGE UsagePage; - UCHAR ReportID; - BOOLEAN IsAlias; - - USHORT BitField; - USHORT LinkCollection; // A unique internal index pointer - - USAGE LinkUsage; - USAGE LinkUsagePage; - - BOOLEAN IsRange; - BOOLEAN IsStringRange; - BOOLEAN IsDesignatorRange; - BOOLEAN IsAbsolute; - - ULONG Reserved[10]; - union - { - struct - { - USAGE UsageMin, UsageMax; - USHORT StringMin, StringMax; - USHORT DesignatorMin, DesignatorMax; - USHORT DataIndexMin, DataIndexMax; - } Range; - struct - { - USAGE Usage, Reserved1; - USHORT StringIndex, Reserved2; - USHORT DesignatorIndex, Reserved3; - USHORT DataIndex, Reserved4; - } NotRange; - }; - -} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS; - - -typedef struct _HIDP_VALUE_CAPS -{ - USAGE UsagePage; - UCHAR ReportID; - BOOLEAN IsAlias; - - USHORT BitField; - USHORT LinkCollection; // A unique internal index pointer - - USAGE LinkUsage; - USAGE LinkUsagePage; - - BOOLEAN IsRange; - BOOLEAN IsStringRange; - BOOLEAN IsDesignatorRange; - BOOLEAN IsAbsolute; - - BOOLEAN HasNull; // Does this channel have a null report union - UCHAR Reserved; - USHORT BitSize; // How many bits are devoted to this value? - - USHORT ReportCount; // See Note below. Usually set to 1. - USHORT Reserved2[5]; - - ULONG UnitsExp; - ULONG Units; - - LONG LogicalMin, LogicalMax; - LONG PhysicalMin, PhysicalMax; - - union - { - struct - { - USAGE UsageMin, UsageMax; - USHORT StringMin, StringMax; - USHORT DesignatorMin, DesignatorMax; - USHORT DataIndexMin, DataIndexMax; - } Range; - - struct - { - USAGE Usage, Reserved1; - USHORT StringIndex, Reserved2; - USHORT DesignatorIndex, Reserved3; - USHORT DataIndex, Reserved4; - } NotRange; - }; -} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; - -typedef struct _HIDD_ATTRIBUTES -{ - ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES) - - // - // Vendor ids of this hid device - // - USHORT VendorID; - USHORT ProductID; - USHORT VersionNumber; - - // - // Additional fields will be added to the end of this structure. - // -} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; - -typedef PUCHAR PHIDP_REPORT_DESCRIPTOR; -typedef struct _HIDP_PREPARSED_DATA* PHIDP_PREPARSED_DATA; - - -typedef struct _HIDP_CAPS -{ - USAGE Usage; - USAGE UsagePage; - USHORT InputReportByteLength; - USHORT OutputReportByteLength; - USHORT FeatureReportByteLength; - USHORT Reserved[17]; - - USHORT NumberLinkCollectionNodes; - - USHORT NumberInputButtonCaps; - USHORT NumberInputValueCaps; - USHORT NumberInputDataIndices; - - USHORT NumberOutputButtonCaps; - USHORT NumberOutputValueCaps; - USHORT NumberOutputDataIndices; - - USHORT NumberFeatureButtonCaps; - USHORT NumberFeatureValueCaps; - USHORT NumberFeatureDataIndices; -} HIDP_CAPS, *PHIDP_CAPS; - -typedef struct _HIDP_DATA -{ - USHORT DataIndex; - USHORT Reserved; - union - { - ULONG RawValue; // for values - BOOLEAN On; // for buttons MUST BE TRUE for buttons. - }; -} HIDP_DATA, *PHIDP_DATA; - -typedef BOOLEAN(__stdcall* _HidD_GetAttributes)(HANDLE HidDeviceObject, HIDD_ATTRIBUTES* Attributes); -typedef void(__stdcall* _HidD_GetHidGuid)(GUID* HidGuid); -typedef BOOLEAN(__stdcall* _HidD_GetPreparsedData)(HANDLE HidDeviceObject, PHIDP_PREPARSED_DATA* PreparsedData); -typedef NTSTATUS(__stdcall* _HidP_GetCaps)(PHIDP_PREPARSED_DATA PreparsedData, HIDP_CAPS* caps); -typedef BOOLEAN(__stdcall* _HidD_FreePreparsedData)(PHIDP_PREPARSED_DATA PreparsedData); -typedef BOOLEAN(__stdcall* _HidD_GetFeature)(HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength); -typedef BOOLEAN(__stdcall* _HidD_SetFeature)(HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength); -typedef NTSTATUS(__stdcall* _HidP_GetSpecificButtonCaps)(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData); -typedef NTSTATUS(__stdcall* _HidP_GetButtonCaps)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData); -typedef NTSTATUS(__stdcall* _HidP_GetUsages)(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE* UsageList, ULONG* UsageLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength); -typedef NTSTATUS(__stdcall* _HidP_GetValueCaps)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData); -typedef NTSTATUS(__stdcall* _HidP_GetUsageValue)(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength); -typedef BOOLEAN(__stdcall* _HidD_GetProductString)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength); - -//#define HidP_GetButtonCaps(_Type_, _Caps_, _Len_, _Data_) \ -// HidP_GetSpecificButtonCaps(_Type_, 0, 0, 0, _Caps_, _Len_, _Data_) - -extern _HidD_GetHidGuid HidD_GetHidGuid; -extern _HidD_GetAttributes HidD_GetAttributes; -extern _HidD_GetPreparsedData HidD_GetPreparsedData; -extern _HidP_GetCaps HidP_GetCaps; -extern _HidD_FreePreparsedData HidD_FreePreparsedData; -extern _HidD_GetFeature HidD_GetFeature; -extern _HidD_SetFeature HidD_SetFeature; -extern _HidP_GetSpecificButtonCaps HidP_GetSpecificButtonCaps; -extern _HidP_GetButtonCaps HidP_GetButtonCaps; -extern _HidP_GetUsages HidP_GetUsages; -extern _HidP_GetValueCaps HidP_GetValueCaps; -extern _HidP_GetUsageValue HidP_GetUsageValue; -extern _HidD_GetProductString HidD_GetProductString; - -void UninitHid(); -int InitHid(); - -#include - -#endif diff --git a/pcsx2/USB/shared/inifile_usb.cpp b/pcsx2/USB/shared/inifile_usb.cpp deleted file mode 100644 index c18dab9f9d..0000000000 --- a/pcsx2/USB/shared/inifile_usb.cpp +++ /dev/null @@ -1,1058 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "inifile_usb.h" -#include -#include -#include - -// Only valid if C++ -#ifndef __cplusplus -#error C++ compiler required. -#endif - -// In the event you want to trace the calls can define _TRACE_CINIFILE -#ifdef _TRACE_CINIFILE -#define _CINIFILE_DEBUG -#endif - -// _CRLF is used in the Save() function -// The class will read the correct data regardless of how the file linefeeds are defined or -// It is best to use the linefeed that is default to the system. This reduces issues if needing to modify -// the file with ie. notepad.exe which doesn't recognize unix linefeeds. -#ifdef _WIN32 // Windows default is \r\n -#ifdef _FORCE_UNIX_LINEFEED -#define _CRLFA "\n" -#define _CRLFW L"\n" -#else -#define _CRLFA "\r\n" -#define _CRLFW L"\r\n" -#endif - -#else // Standard format is \n for unix -#ifdef _FORCE_WINDOWS_LINEFEED -#define _CRLFA "\r\n" -#define _CRLFW L"\r\n" -#else -#define _CRLFA "\n" -#define _CRLFW L"\n" -#endif -#endif - -// Convert wstring to string -std::string wstr_to_str(const std::wstring& arg) -{ - std::string res(arg.length(), '\0'); - wcstombs(const_cast(res.data()), arg.c_str(), arg.length()); - return res; -} - -// Convert string to wstring -std::wstring str_to_wstr(const std::string& arg) -{ - std::wstring res(arg.length(), L'\0'); - mbstowcs(const_cast(res.data()), arg.c_str(), arg.length()); - return res; -} - -// Helper Functions -void RTrim(std::string& str, const std::string& chars = " \t") -{ - str.erase(str.find_last_not_of(chars) + 1); -} - -void LTrim(std::string& str, const std::string& chars = " \t") -{ - str.erase(0, str.find_first_not_of(chars)); -} - -void Trim(std::string& str, const std::string& chars = " \t") -{ - str.erase(str.find_last_not_of(chars) + 1); - str.erase(0, str.find_first_not_of(chars)); -} - -// Stream Helpers - -std::ostream& operator<<(std::ostream& output, CIniFileA& obj) -{ - obj.Save(output); - return output; -} - -std::istream& operator>>(std::istream& input, CIniFileA& obj) -{ - obj.Load(input); - return input; -} - -std::istream& operator>>(std::istream& input, CIniMergeA merger) -{ - return merger(input); -} - -// CIniFileA class methods - -CIniFileA::CIniFileA() -{ -} - -CIniFileA::~CIniFileA() -{ - RemoveAllSections(); -} - -const char* const CIniFileA::LF = _CRLFA; - -void CIniFileA::Save(std::ostream& output) -{ - std::string sSection; - - for (SecIndexA::iterator itr = m_sections.begin(); itr != m_sections.end(); ++itr) - { - sSection = "[" + (*itr)->GetSectionName() + "]"; - - output << sSection << _CRLFA; - - for (KeyIndexA::iterator klitr = (*itr)->m_keys.begin(); klitr != (*itr)->m_keys.end(); ++klitr) - { - std::string sKey = (*klitr)->GetKeyName() + "=" + (*klitr)->GetValue(); - output << sKey << _CRLFA; - } - } -} - -bool CIniFileA::Save(const std::string& fileName) -{ - - std::ofstream output; - - output.open(fileName.c_str(), std::ios::binary); - - if (!output.is_open()) - return false; - - Save(output); - - output.close(); - return true; -} - - -void CIniFileA::Load(std::istream& input, bool bMerge) -{ - if (!bMerge) - RemoveAllSections(); - - CIniSectionA* pSection = NULL; - std::string sRead; - enum - { - KEY, - SECTION, - COMMENT, - OTHER - }; - - while (std::getline(input, sRead)) - { - - // Trim all whitespace on the left - LTrim(sRead); - // Trim any returns - RTrim(sRead, "\n\r"); - - if (!sRead.empty()) - { - auto nType = (sRead.find_first_of("[") == 0 && (sRead[sRead.find_last_not_of(" \t\r\n")] == ']')) ? SECTION : OTHER; - nType = ((nType == OTHER) && (sRead.find_first_of("=") != std::string::npos && sRead.find_first_of("=") > 0)) ? KEY : nType; - nType = ((nType == OTHER) && (sRead.find_first_of("#") == 0)) ? COMMENT : nType; - - switch (nType) - { - case SECTION: - pSection = AddSection(sRead.substr(1, sRead.size() - 2)); - break; - - case KEY: - { - // Check to ensure valid section... or drop the keys listed - if (pSection) - { - size_t iFind = sRead.find_first_of("="); - std::string sKey = sRead.substr(0, iFind); - std::string sValue = sRead.substr(iFind + 1); - CIniKeyA* pKey = pSection->AddKey(sKey); - if (pKey) - { - pKey->SetValue(sValue); - } - } - } - break; - case COMMENT: - break; - case OTHER: - break; - } - } - } -} - -bool CIniFileA::Load(const std::string& fileName, bool bMerge) -{ - std::ifstream input; - - input.open(fileName.c_str(), std::ios::binary); - - if (!input.is_open()) - return false; - - Load(input, bMerge); - - input.close(); - return true; -} - -const SecIndexA& CIniFileA::GetSections() const -{ - return m_sections; -} - - -CIniSectionA* CIniFileA::GetSection(std::string sSection) const -{ - Trim(sSection); - SecIndexA::const_iterator itr = _find_sec(sSection); - if (itr != m_sections.end()) - return *itr; - return NULL; -} - -CIniSectionA* CIniFileA::AddSection(std::string sSection) -{ - - Trim(sSection); - SecIndexA::const_iterator itr = _find_sec(sSection); - if (itr == m_sections.end()) - { - // Note constuctor doesn't trim the string so it is trimmed above - CIniSectionA* pSection = new CIniSectionA(this, sSection); - m_sections.insert(pSection); - return pSection; - } - else - return *itr; -} - - -std::string CIniFileA::GetKeyValue(const std::string& sSection, const std::string& sKey) const -{ - std::string sValue; - CIniSectionA* pSec = GetSection(sSection); - if (pSec) - { - CIniKeyA* pKey = pSec->GetKey(sKey); - if (pKey) - sValue = pKey->GetValue(); - } - return sValue; -} - -void CIniFileA::SetKeyValue(const std::string& sSection, const std::string& sKey, const std::string& sValue) -{ - CIniSectionA* pSec = AddSection(sSection); - if (pSec) - { - CIniKeyA* pKey = pSec->AddKey(sKey); - if (pKey) - pKey->SetValue(sValue); - } -} - - -void CIniFileA::RemoveSection(std::string sSection) -{ - Trim(sSection); - SecIndexA::iterator itr = _find_sec(sSection); - if (itr != m_sections.end()) - { - delete *itr; - m_sections.erase(itr); - } -} - -void CIniFileA::RemoveSection(CIniSectionA* pSection) -{ - // No trim since internal object not from user - SecIndexA::iterator itr = _find_sec(pSection->m_sSectionName); - if (itr != m_sections.end()) - { - delete *itr; - m_sections.erase(itr); - } -} - -void CIniFileA::RemoveAllSections() -{ - for (SecIndexA::iterator itr = m_sections.begin(); itr != m_sections.end(); ++itr) - { - delete *itr; - } - m_sections.clear(); -} - - -bool CIniFileA::RenameSection(const std::string& sSectionName, const std::string& sNewSectionName) -{ - // Note string trims are done in lower calls. - bool bRval = false; - CIniSectionA* pSec = GetSection(sSectionName); - if (pSec) - { - bRval = pSec->SetSectionName(sNewSectionName); - } - return bRval; -} - -bool CIniFileA::RenameKey(const std::string& sSectionName, const std::string& sKeyName, const std::string& sNewKeyName) -{ - // Note string trims are done in lower calls. - bool bRval = false; - CIniSectionA* pSec = GetSection(sSectionName); - if (pSec != NULL) - { - CIniKeyA* pKey = pSec->GetKey(sKeyName); - if (pKey != NULL) - bRval = pKey->SetKeyName(sNewKeyName); - } - return bRval; -} - -// Returns a constant iterator to a section by name, string is not trimmed -SecIndexA::const_iterator CIniFileA::_find_sec(const std::string& sSection) const -{ - CIniSectionA bogus(NULL, sSection); - return m_sections.find(&bogus); -} - -// Returns an iterator to a section by name, string is not trimmed -SecIndexA::iterator CIniFileA::_find_sec(const std::string& sSection) -{ - CIniSectionA bogus(NULL, sSection); - return m_sections.find(&bogus); -} - -// CIniFileA functions end here - -// CIniSectionA functions start here - -CIniSectionA::CIniSectionA(CIniFileA* pIniFile, const std::string& sSectionName) - : m_pIniFile(pIniFile) - , m_sSectionName(sSectionName) -{ -} - - -CIniSectionA::~CIniSectionA() -{ - RemoveAllKeys(); -} - -CIniKeyA* CIniSectionA::GetKey(std::string sKeyName) const -{ - Trim(sKeyName); - KeyIndexA::const_iterator itr = _find_key(sKeyName); - if (itr != m_keys.end()) - return *itr; - return NULL; -} - -void CIniSectionA::RemoveAllKeys() -{ - for (KeyIndexA::iterator itr = m_keys.begin(); itr != m_keys.end(); ++itr) - { - delete *itr; - } - m_keys.clear(); -} - -void CIniSectionA::RemoveKey(std::string sKey) -{ - Trim(sKey); - KeyIndexA::iterator itr = _find_key(sKey); - if (itr != m_keys.end()) - { - delete *itr; - m_keys.erase(itr); - } -} - -void CIniSectionA::RemoveKey(CIniKeyA* pKey) -{ - // No trim is done to improve efficiency since CIniKeyA* should already be trimmed - KeyIndexA::iterator itr = _find_key(pKey->m_sKeyName); - if (itr != m_keys.end()) - { - delete *itr; - m_keys.erase(itr); - } -} - -CIniKeyA* CIniSectionA::AddKey(std::string sKeyName) -{ - Trim(sKeyName); - KeyIndexA::const_iterator itr = _find_key(sKeyName); - if (itr == m_keys.end()) - { - // Note constuctor doesn't trim the string so it is trimmed above - CIniKeyA* pKey = new CIniKeyA(this, sKeyName); - m_keys.insert(pKey); - return pKey; - } - else - return *itr; -} - -bool CIniSectionA::SetSectionName(std::string sSectionName) -{ - Trim(sSectionName); - // Does this already exist. - if (m_pIniFile->_find_sec(sSectionName) == m_pIniFile->m_sections.end()) - { - - // Find the current section if one exists and remove it since we are renaming - SecIndexA::iterator itr = m_pIniFile->_find_sec(m_sSectionName); - - // Just to be safe make sure the old section exists - if (itr != m_pIniFile->m_sections.end()) - m_pIniFile->m_sections.erase(itr); - - // Change name prior to ensure tree balance - m_sSectionName = sSectionName; - - // Set the new map entry we know should not exist - m_pIniFile->m_sections.insert(this); - - return true; - } - else - { - return false; - } -} - -std::string CIniSectionA::GetSectionName() const -{ - return m_sSectionName; -} - -const KeyIndexA& CIniSectionA::GetKeys() const -{ - return m_keys; -} - -std::string CIniSectionA::GetKeyValue(const std::string& sKey) const -{ - std::string sValue; - CIniKeyA* pKey = GetKey(sKey); - if (pKey) - { - sValue = pKey->GetValue(); - } - return sValue; -} - -void CIniSectionA::SetKeyValue(const std::string& sKey, const std::string& sValue) -{ - CIniKeyA* pKey = AddKey(sKey); - if (pKey) - { - pKey->SetValue(sValue); - } -} - -// Returns a constant iterator to a key by name, string is not trimmed -KeyIndexA::const_iterator CIniSectionA::_find_key(const std::string& sKey) const -{ - CIniKeyA bogus(NULL, sKey); - return m_keys.find(&bogus); -} - -// Returns an iterator to a key by name, string is not trimmed -KeyIndexA::iterator CIniSectionA::_find_key(const std::string& sKey) -{ - CIniKeyA bogus(NULL, sKey); - return m_keys.find(&bogus); -} - -// CIniSectionA function end here - -// CIniKeyA Functions Start Here - -CIniKeyA::CIniKeyA(CIniSectionA* pSection, const std::string& sKeyName) - : m_pSection(pSection) - , m_sKeyName(sKeyName) -{ -} - - -CIniKeyA::~CIniKeyA() -{ -} - -void CIniKeyA::SetValue(const std::string& sValue) -{ - m_sValue = sValue; -} - -std::string CIniKeyA::GetValue() const -{ - return m_sValue; -} - -bool CIniKeyA::SetKeyName(std::string sKeyName) -{ - Trim(sKeyName); - - // Check for key name conflict - if (m_pSection->_find_key(sKeyName) == m_pSection->m_keys.end()) - { - KeyIndexA::iterator itr = m_pSection->_find_key(m_sKeyName); - - // Find the old map entry and remove it - if (itr != m_pSection->m_keys.end()) - m_pSection->m_keys.erase(itr); - - // Change name prior to ensure tree balance - m_sKeyName = sKeyName; - - // Make the new map entry - m_pSection->m_keys.insert(this); - return true; - } - else - { - return false; - } -} - -std::string CIniKeyA::GetKeyName() const -{ - return m_sKeyName; -} - -// End of CIniKeyA Functions - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// WIDE FUNCTIONS HERE -// -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Helper Functions -void RTrim(std::wstring& str, const std::wstring& chars = L" \t") -{ - str.erase(str.find_last_not_of(chars) + 1); -} - -void LTrim(std::wstring& str, const std::wstring& chars = L" \t") -{ - str.erase(0, str.find_first_not_of(chars)); -} - -void Trim(std::wstring& str, const std::wstring& chars = L" \t") -{ - str.erase(str.find_last_not_of(chars) + 1); - str.erase(0, str.find_first_not_of(chars)); -} - -// Stream Helpers -std::wostream& operator<<(std::wostream& output, CIniFileW& obj) -{ - obj.Save(output); - return output; -} - -std::wistream& operator>>(std::wistream& input, CIniFileW& obj) -{ - obj.Load(input); - return input; -} - -std::wistream& operator>>(std::wistream& input, CIniMergeW merger) -{ - return merger(input); -} - -CIniFileW::CIniFileW() -{ -} - -CIniFileW::~CIniFileW() -{ - RemoveAllSections(); -} - -const wchar_t* const CIniFileW::LF = _CRLFW; - -void CIniFileW::Save(std::wostream& output) -{ - std::wstring sSection; - - for (SecIndexW::iterator itr = m_sections.begin(); itr != m_sections.end(); ++itr) - { - sSection = L"[" + (*itr)->GetSectionName() + L"]"; - - output << sSection << _CRLFA; - - for (KeyIndexW::iterator klitr = (*itr)->m_keys.begin(); klitr != (*itr)->m_keys.end(); ++klitr) - { - std::wstring sKey = (*klitr)->GetKeyName() + L"=" + (*klitr)->GetValue(); - output << sKey << _CRLFA; - } - } -} - -bool CIniFileW::Save(const std::wstring& fileName) -{ - - std::wofstream output; - -#if defined(_MSC_VER) && (_MSC_VER >= 1300) - output.open(fileName.c_str(), std::ios::binary); -#else - output.open(wstr_to_str(fileName).c_str(), std::ios::binary); -#endif - - if (!output.is_open()) - return false; - - Save(output); - - output.close(); - return true; -} - - -void CIniFileW::Load(std::wistream& input, bool bMerge) -{ - if (!bMerge) - RemoveAllSections(); - - CIniSectionW* pSection = NULL; - std::wstring sRead; - enum - { - KEY, - SECTION, - COMMENT, - OTHER - }; - - while (std::getline(input, sRead)) - { - - // Trim all whitespace on the left - LTrim(sRead); - // Trim any returns - RTrim(sRead, L"\n\r"); - - if (!sRead.empty()) - { - auto nType = (sRead.find_first_of(L"[") == 0 && (sRead[sRead.find_last_not_of(L" \t\r\n")] == L']')) ? SECTION : OTHER; - nType = ((nType == OTHER) && (sRead.find_first_of(L"=") != std::wstring::npos && sRead.find_first_of(L"=") > 0)) ? KEY : nType; - nType = ((nType == OTHER) && (sRead.find_first_of(L"#") == 0)) ? COMMENT : nType; - - switch (nType) - { - case SECTION: - pSection = AddSection(sRead.substr(1, sRead.size() - 2)); - break; - - case KEY: - { - // Check to ensure valid section... or drop the keys listed - if (pSection) - { - size_t iFind = sRead.find_first_of(L"="); - std::wstring sKey = sRead.substr(0, iFind); - std::wstring sValue = sRead.substr(iFind + 1); - CIniKeyW* pKey = pSection->AddKey(sKey); - if (pKey) - { - pKey->SetValue(sValue); - } - } - } - break; - case COMMENT: - break; - case OTHER: - break; - } - } - } -} - -bool CIniFileW::Load(const std::wstring& fileName, bool bMerge) -{ - - std::wifstream input; - -#if defined(_MSC_VER) && (_MSC_VER >= 1300) - input.open(fileName.c_str(), std::ios::binary); -#else - input.open(wstr_to_str(fileName).c_str(), std::ios::binary); -#endif - - if (!input.is_open()) - return false; - - Load(input, bMerge); - - input.close(); - return true; -} - -const SecIndexW& CIniFileW::GetSections() const -{ - return m_sections; -} - - -CIniSectionW* CIniFileW::GetSection(std::wstring sSection) const -{ - Trim(sSection); - SecIndexW::const_iterator itr = _find_sec(sSection); - if (itr != m_sections.end()) - return *itr; - return NULL; -} - -CIniSectionW* CIniFileW::AddSection(std::wstring sSection) -{ - Trim(sSection); - SecIndexW::const_iterator itr = _find_sec(sSection); - if (itr == m_sections.end()) - { - // Note constuctor doesn't trim the string so it is trimmed above - CIniSectionW* pSection = new CIniSectionW(this, sSection); - m_sections.insert(pSection); - return pSection; - } - else - return *itr; -} - - -std::wstring CIniFileW::GetKeyValue(const std::wstring& sSection, const std::wstring& sKey) const -{ - std::wstring sValue; - CIniSectionW* pSec = GetSection(sSection); - if (pSec) - { - CIniKeyW* pKey = pSec->GetKey(sKey); - if (pKey) - sValue = pKey->GetValue(); - } - return sValue; -} - -void CIniFileW::SetKeyValue(const std::wstring& sSection, const std::wstring& sKey, const std::wstring& sValue) -{ - CIniSectionW* pSec = AddSection(sSection); - if (pSec) - { - CIniKeyW* pKey = pSec->AddKey(sKey); - if (pKey) - pKey->SetValue(sValue); - } -} - - -void CIniFileW::RemoveSection(std::wstring sSection) -{ - Trim(sSection); - SecIndexW::iterator itr = _find_sec(sSection); - if (itr != m_sections.end()) - { - delete *itr; - m_sections.erase(itr); - } -} - -void CIniFileW::RemoveSection(CIniSectionW* pSection) -{ - // No trim since internal object not from user - SecIndexW::iterator itr = _find_sec(pSection->m_sSectionName); - if (itr != m_sections.end()) - { - delete *itr; - m_sections.erase(itr); - } -} - -void CIniFileW::RemoveAllSections() -{ - for (SecIndexW::iterator itr = m_sections.begin(); itr != m_sections.end(); ++itr) - { - delete *itr; - } - m_sections.clear(); -} - - -bool CIniFileW::RenameSection(const std::wstring& sSectionName, const std::wstring& sNewSectionName) -{ - // Note string trims are done in lower calls. - bool bRval = false; - CIniSectionW* pSec = GetSection(sSectionName); - if (pSec) - { - bRval = pSec->SetSectionName(sNewSectionName); - } - return bRval; -} - -bool CIniFileW::RenameKey(const std::wstring& sSectionName, const std::wstring& sKeyName, const std::wstring& sNewKeyName) -{ - // Note string trims are done in lower calls. - bool bRval = false; - CIniSectionW* pSec = GetSection(sSectionName); - if (pSec != NULL) - { - CIniKeyW* pKey = pSec->GetKey(sKeyName); - if (pKey != NULL) - bRval = pKey->SetKeyName(sNewKeyName); - } - return bRval; -} - -// Returns a constant iterator to a section by name, string is not trimmed -SecIndexW::const_iterator CIniFileW::_find_sec(const std::wstring& sSection) const -{ - CIniSectionW bogus(NULL, sSection); - return m_sections.find(&bogus); -} - -// Returns an iterator to a section by name, string is not trimmed -SecIndexW::iterator CIniFileW::_find_sec(const std::wstring& sSection) -{ - CIniSectionW bogus(NULL, sSection); - return m_sections.find(&bogus); -} - -// CIniFileW functions end here - -// CIniSectionW functions start here - -CIniSectionW::CIniSectionW(CIniFileW* pIniFile, const std::wstring& sSectionName) - : m_pIniFile(pIniFile) - , m_sSectionName(sSectionName) -{ -} - - -CIniSectionW::~CIniSectionW() -{ - RemoveAllKeys(); -} - -CIniKeyW* CIniSectionW::GetKey(std::wstring sKeyName) const -{ - Trim(sKeyName); - KeyIndexW::const_iterator itr = _find_key(sKeyName); - if (itr != m_keys.end()) - return *itr; - return NULL; -} - -void CIniSectionW::RemoveAllKeys() -{ - for (KeyIndexW::iterator itr = m_keys.begin(); itr != m_keys.end(); ++itr) - { - delete *itr; - } - m_keys.clear(); -} - -void CIniSectionW::RemoveKey(std::wstring sKey) -{ - Trim(sKey); - KeyIndexW::iterator itr = _find_key(sKey); - if (itr != m_keys.end()) - { - delete *itr; - m_keys.erase(itr); - } -} - -void CIniSectionW::RemoveKey(CIniKeyW* pKey) -{ - // No trim is done to improve efficiency since CIniKeyW* should already be trimmed - KeyIndexW::iterator itr = _find_key(pKey->m_sKeyName); - if (itr != m_keys.end()) - { - delete *itr; - m_keys.erase(itr); - } -} - -CIniKeyW* CIniSectionW::AddKey(std::wstring sKeyName) -{ - Trim(sKeyName); - KeyIndexW::const_iterator itr = _find_key(sKeyName); - if (itr == m_keys.end()) - { - // Note constuctor doesn't trim the string so it is trimmed above - CIniKeyW* pKey = new CIniKeyW(this, sKeyName); - m_keys.insert(pKey); - return pKey; - } - else - return *itr; -} - -bool CIniSectionW::SetSectionName(std::wstring sSectionName) -{ - Trim(sSectionName); - // Does this already exist. - if (m_pIniFile->_find_sec(sSectionName) == m_pIniFile->m_sections.end()) - { - // Find the current section if one exists and remove it since we are renaming - SecIndexW::iterator itr = m_pIniFile->_find_sec(m_sSectionName); - - // Just to be safe make sure the old section exists - if (itr != m_pIniFile->m_sections.end()) - m_pIniFile->m_sections.erase(itr); - - // Change name prior to ensure tree balance - m_sSectionName = sSectionName; - - // Set the new map entry we know should not exist - m_pIniFile->m_sections.insert(this); - - return true; - } - else - { - return false; - } -} - -std::wstring CIniSectionW::GetSectionName() const -{ - return m_sSectionName; -} - -const KeyIndexW& CIniSectionW::GetKeys() const -{ - return m_keys; -} - -std::wstring CIniSectionW::GetKeyValue(const std::wstring& sKey) const -{ - std::wstring sValue; - CIniKeyW* pKey = GetKey(sKey); - if (pKey) - { - sValue = pKey->GetValue(); - } - return sValue; -} - -void CIniSectionW::SetKeyValue(const std::wstring& sKey, const std::wstring& sValue) -{ - CIniKeyW* pKey = AddKey(sKey); - if (pKey) - { - pKey->SetValue(sValue); - } -} - -// Returns a constant iterator to a key by name, string is not trimmed -KeyIndexW::const_iterator CIniSectionW::_find_key(const std::wstring& sKey) const -{ - CIniKeyW bogus(NULL, sKey); - return m_keys.find(&bogus); -} - -// Returns an iterator to a key by name, string is not trimmed -KeyIndexW::iterator CIniSectionW::_find_key(const std::wstring& sKey) -{ - CIniKeyW bogus(NULL, sKey); - return m_keys.find(&bogus); -} - -// CIniSectionW function end here - -// CIniKeyW Functions Start Here - -CIniKeyW::CIniKeyW(CIniSectionW* pSection, const std::wstring& sKeyName) - : m_pSection(pSection) - , m_sKeyName(sKeyName) -{ -} - - -CIniKeyW::~CIniKeyW() -{ -} - -void CIniKeyW::SetValue(const std::wstring& sValue) -{ - m_sValue = sValue; -} - -std::wstring CIniKeyW::GetValue() const -{ - return m_sValue; -} - -bool CIniKeyW::SetKeyName(std::wstring sKeyName) -{ - Trim(sKeyName); - - // Check for key name conflict - if (m_pSection->_find_key(sKeyName) == m_pSection->m_keys.end()) - { - KeyIndexW::iterator itr = m_pSection->_find_key(m_sKeyName); - - // Find the old map entry and remove it - if (itr != m_pSection->m_keys.end()) - m_pSection->m_keys.erase(itr); - - // Change name prior to ensure tree balance - m_sKeyName = sKeyName; - - // Make the new map entry - m_pSection->m_keys.insert(this); - return true; - } - else - { - return false; - } -} - -std::wstring CIniKeyW::GetKeyName() const -{ - return m_sKeyName; -} - -// End of CIniKeyW Functions diff --git a/pcsx2/USB/shared/inifile_usb.h b/pcsx2/USB/shared/inifile_usb.h deleted file mode 100644 index a21587e5cf..0000000000 --- a/pcsx2/USB/shared/inifile_usb.h +++ /dev/null @@ -1,568 +0,0 @@ -/******************************************************************************************************************************* - Programmer: Ludvik Jerabek - Date: June 15th, 2009 - - Defined Classed: CIniFileW CIniFileA - - Purpose: C++ Inifile Reader\Writer. Uses std::set and stdext::hash_set to implement an efficient ini object. - - Summary: This is a total re-write of the original CIniFile class written in 2006. Originally the data structures - underlying the CIniFile object were std::list which was extreamly inefficient when dealing with huge ini files. - - - Note: The class currently supports std::wstring and std::string. The typedef CIniFile is based on the whether of no - _UNICODE is defined. If _UNICODE is define in your project CIniFile is a CIniFileW if _UNICODE is not defined - then CIniFile is a CIniFileA object. - - Defines: - - _TRACE_CINIFILE - If defined enables call tracing to standard output - _UNICODE - If defined the CIniFile will be defined as CIniFileW instead of CIniFileA - _FORCE_UNIX_LINEFEED - If defined when _WIN32 is defined (WINDOWS) the default linefeed CRLF is overridden to CR - _FORCE_WINDOWS_LINEFEED - If defined when _WIN32 is not defined (*NIX) the default linefeed CR is overridden to CRLF - - Updates: - - 12\01\2005 - Initial MFC Release. - 01\12\2006 - Ported to Ansi C++ Non-MFC. - 06\16\2009 - Added support for different linefeed types, resolved issues around reading different types of linefeeds. - 06\17\2009 - Added support for wide characters. - 06\21\2009 - Re-written to use std::map. - 07\02\2009 - Removed MFC version. Since Ansi version works in MFC ( Examples provided for download ). - 07\03\2009 - Added support for VS6. - 07\03\2009 - Fixed issue with SecIndexA \ SecIndexW. Were not named specific to the encoding may have caused issues. - 07\03\2009 - Fixed GetKeys and GetSections functions to return const ref v.s. copy of data. - 07\14\2009 - Fixed Load() whitespace preservation on key value. - 09\21\2009 - Fixed removing all the sections and keys, replaced empty() with clear() - 09\22\2009 - Added overloaded Load() and Save() to read\write streams - 09\23\2009 - Added operators for << and >> to be used with streams - 09\24\2009 - Added merge option to Load() - 09\25\2009 - Added CIniMerge for use with << and >> - 09\27\2009 - Moved CIniMerge into CIniFile, fixed issue with VC6 CIniFile::CR - 12\29\2010 - Reduced key storage redundancy by using std::set instead of std::map - 12\29\2010 - Reduced number of pass by value methods to reduce deep copy (std::string to const std::string&) - 05\07\2011 - Fixed MSC_VER to _MSC_VER - 05\07\2011 - Fixed OTHER file parse detection issue - -*******************************************************************************************************************************/ -#ifndef __CINIFILE_H_ -#define __CINIFILE_H_ - -#ifdef _WIN32 -// Prevent compile time warnings for deprecation -#if !defined(_CRT_SECURE_NO_DEPRECATE) -#define _CRT_SECURE_NO_DEPRECATE -#endif -#endif - -#include -#include -#include -#include -#include - -#define INI_TOKEN_A_ANSI "\a" // I.E. Item1;Item2;Item3 - '\a' used in place of ';' -#define INI_TOKEN_B_ANSI "\b" // I.E. Item1,Item1b;Item2,Item2b;Item3,Item3b - '\b' used in place of ',' -#define INI_EMPTY_ANSI "*" // Used to indicate empty value in token string. I.E. *;Item2;*;Item3; - -class CIniFileA -{ -public: - static const char* const LF; - -public: - CIniFileA(); - ~CIniFileA(); - - // Used to save the data back to the file or your choice - bool Save(const std::string& fileName); - - // Save data to an output stream - void Save(std::ostream& output); - - // Loads the Reads the data in the ini file into the IniFile object - bool Load(const std::string& fileName, bool bMerge = false); - - // Load data from an input stream - void Load(std::istream& input, bool bMerge = false); - -public: - class CIniMergeA - { - public: - explicit CIniMergeA(CIniFileA& ini) - : _ini(ini) - { - } - std::istream& operator()(std::istream& input) const - { - _ini.Load(input, true); - return input; - } - - private: - CIniFileA& _ini; - }; - -public: -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - struct ci_less_a; -#endif -#endif - class CIniSectionA - { - friend class CIniFileA; // Allow CIniFileA to create sections -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - friend struct ci_less_a; - -#endif -#endif - public: -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - struct ci_less_a; -#endif -#endif - class CIniKeyA - { - friend class CIniSectionA; // Allow CIniSectionA to create keys -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - friend struct ci_less_a; -#endif -#endif - private: // CIniFileA acts as a class factory for CIniSectionA Objects - CIniKeyA(CIniSectionA* pSection, const std::string& sKeyName); - CIniKeyA(const CIniKeyA&); // No Copy - CIniKeyA& operator=(const CIniKeyA&); // No Copy - ~CIniKeyA(); - - public: - // Sets the value of the key - void SetValue(const std::string& sValue); - // Returns the value of the key - std::string GetValue() const; - // Sets the key name, returns true on success, fails if the section - // name sKeyName already exists - bool SetKeyName(std::string sKeyName); - // Returns the name of the Key - std::string GetKeyName() const; - - private: - // Pointer to the parent CIniSectionA - CIniSectionA* m_pSection; - // Name of the Key - std::string m_sKeyName; - // Value associated - std::string m_sValue; - }; // End of CIniKeyA - // Typedef of set of CIniKeyA pointers - struct ci_less_a - { - bool operator()(const CIniKeyA* s1, const CIniKeyA* s2) const - { -#ifndef _WIN32 - return strcasecmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0; -#else - return _stricmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0; -#endif - } - }; - - typedef std::set KeyIndexA; - -#ifdef _WIN32 - // Added for VC6 Support -#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300) - friend class CIniKeyA; -#endif -#endif - private: // CIniSectionA acts as a class factory for CIniKeyA Objects - CIniSectionA(CIniFileA* pIniFile, const std::string& sSectionName); - CIniSectionA(const CIniSectionA&); // No Copy - CIniSectionA& operator=(const CIniSectionA&); // No Copy - ~CIniSectionA(); - - public: - // Adds a key to the CIniSectionA object, returns a CIniKeyA pointer to the new or existing object - CIniKeyA* AddKey(std::string sKeyName); - // Removes a single key by pointer - void RemoveKey(CIniKeyA* pKey); - // Removes a single key by string - void RemoveKey(std::string sKey); - // Removes all the keys in the section - void RemoveAllKeys(); - // Returns a CIniKeyA pointer to the key by name, NULL if it was not found - CIniKeyA* GetKey(std::string sKeyName) const; - // Returns all keys in the section by KeyIndex only to be used for enumeration - const KeyIndexA& GetKeys() const; - // Returns a KeyValue at a certain section - std::string GetKeyValue(const std::string& sKey) const; - // Sets a KeyValuePair at a certain section - void SetKeyValue(const std::string& sKey, const std::string& sValue); - // Sets the section name, returns true on success, fails if the section - // name sSectionName already exists - bool SetSectionName(std::string sSectionName); - // Returns the section name - std::string GetSectionName() const; - - private: - KeyIndexA::const_iterator _find_key(const std::string& sKeyName) const; - KeyIndexA::iterator _find_key(const std::string& sKeyName); - - private: - // CIniFileA pointer back to the object that instanciated the section - CIniFileA* m_pIniFile; - // Name of the section - std::string m_sSectionName; - // List of CIniKeyA pointers ( Keys in the section ) - KeyIndexA m_keys; - }; // End of CIniSectionA - // Typedef of a List of CIniSectionA pointers - struct ci_less_a - { - bool operator()(const CIniSectionA* s1, const CIniSectionA* s2) const - { -#ifndef _WIN32 - return strcasecmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0; -#else - return _stricmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0; -#endif - } - }; - - typedef std::set SecIndexA; - -#ifdef _WIN32 - // Added for VC6 Support -#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300) - friend class CIniSectionA; -#endif -#endif -public: - // Adds a section to the CIniFileA object, returns a CIniFileA pointer to the new or existing object - CIniSectionA* AddSection(std::string sSection); - // Removes section by pointer - void RemoveSection(CIniSectionA* pSection); - // Removes a section by its name sSection - void RemoveSection(std::string sSection); - // Removes all existing sections - void RemoveAllSections(); - // Returns a CIniSectionA* to the section by name, NULL if it was not found - CIniSectionA* GetSection(std::string sSection) const; - // Returns all sections in the inifile by SecIndex, only to be used for enumeration (DO NOT KEEP THE REF OR TRY TO DELETE STUFF!) - const SecIndexA& GetSections() const; - // Returns a KeyValue at a certain section - std::string GetKeyValue(const std::string& sSection, const std::string& sKey) const; - // Sets a KeyValuePair at a certain section - void SetKeyValue(const std::string& sSection, const std::string& sKey, const std::string& sValue); - // Renames an existing section returns true on success, false if the section didn't exist or there was another section with the same sNewSectionName - bool RenameSection(const std::string& sSectionName, const std::string& sNewSectionName); - // Renames an existing key returns true on success, false if the key didn't exist or there was another section with the same sNewSectionName - bool RenameKey(const std::string& sSectionName, const std::string& sKeyName, const std::string& sNewKeyName); - -private: - SecIndexA::const_iterator _find_sec(const std::string& sSection) const; - SecIndexA::iterator _find_sec(const std::string& sSection); - -private: - CIniFileA(const CIniFileA&); // No Copy - CIniFileA& operator=(const CIniFileA&); // No Copy - // List of CIniSectionA pointers ( List of sections in the class ) - SecIndexA m_sections; -}; // End of CIniFileA - -// Basic typedefs for ease of use -typedef CIniFileA::CIniMergeA CIniMergeA; -typedef CIniFileA::CIniSectionA CIniSectionA; -typedef CIniSectionA::CIniKeyA CIniKeyA; - -// Pointers -typedef CIniFileA* PCINIA; -typedef CIniKeyA* PCINIKEYA; -typedef CIniSectionA* PCINISECA; - -// Map Types -typedef CIniSectionA::KeyIndexA KeyIndexA; -typedef CIniFileA::SecIndexA SecIndexA; - -std::ostream& operator<<(std::ostream& output, CIniFileA& obj); -std::istream& operator>>(std::istream& input, CIniFileA& obj); -std::istream& operator>>(std::istream& input, CIniMergeA merger); - -// Unicode Class Definition - -#define INI_TOKEN_A_UNICODE L"\a" // I.E. Item1;Item2;Item3 - '\a' used in place of ';' -#define INI_TOKEN_B_UNICODE L"\b" // I.E. Item1,Item1b;Item2,Item2b;Item3,Item3b - '\b' used in place of ',' -#define INI_EMPTY_UNICODE L"*" // Used to indicate empty value in token string. I.E. *;Item2;*;Item3; - -class CIniFileW -{ -public: - static const wchar_t* const LF; - -public: - CIniFileW(); - ~CIniFileW(); - - // Used to save the data back to the file or your choice - bool Save(const std::wstring& fileName); - - // Save data to an output stream - void Save(std::wostream& output); - - // Loads the Reads the data in the ini file into the IniFile object - bool Load(const std::wstring& fileName, bool bMerge = false); - - // Load data from an input stream - void Load(std::wistream& input, bool bMerge = false); - -public: - class CIniMergeW - { - public: - explicit CIniMergeW(CIniFileW& ini) - : _ini(ini) - { - } - std::wistream& operator()(std::wistream& input) const - { - _ini.Load(input, true); - return input; - } - - private: - CIniFileW& _ini; - }; - -public: -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - struct ci_less_w; -#endif -#endif - class CIniSectionW - { - friend class CIniFileW; // Allow CIniFileW to create sections -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - friend struct ci_less_w; -#endif -#endif - public: -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - struct ci_less_w; -#endif -#endif - class CIniKeyW - { - friend class CIniSectionW; // Allow CIniSectionW to create keys -#ifdef _WIN32 - // Added for versions earlier than VS2008 -#if defined(_MSC_VER) && (_MSC_VER <= 1400) - friend struct ci_less_w; -#endif -#endif - private: // CIniFileW acts as a class factory for CIniSectionW Objects - CIniKeyW(CIniSectionW* pSection, const std::wstring& sKeyName); - CIniKeyW(const CIniKeyW&); // No Copy - CIniKeyW& operator=(const CIniKeyW&); // No Copy - ~CIniKeyW(); - - public: - // Sets the value of the key - void SetValue(const std::wstring& sValue); - // Returns the value of the key - std::wstring GetValue() const; - // Sets the key name, returns true on success, fails if the section - // name sKeyName already exists - bool SetKeyName(std::wstring sKeyName); - // Returns the name of the Key - std::wstring GetKeyName() const; - - private: - // Pointer to the parent CIniSectionW - CIniSectionW* m_pSection; - // Name of the Key - std::wstring m_sKeyName; - // Value associated - std::wstring m_sValue; - }; // End of CIniKeyW - // Typedef of set of CIniKeyW pointers - struct ci_less_w - { - bool operator()(const CIniKeyW* s1, const CIniKeyW* s2) const - { -#ifndef _WIN32 - return wcscasecmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0; -#else - return _wcsicmp(s1->m_sKeyName.c_str(), s2->m_sKeyName.c_str()) < 0; -#endif - } - }; - - typedef std::set KeyIndexW; - -#ifdef _WIN32 - // Added for VC6 Support -#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300) - friend class CIniKeyW; -#endif -#endif - private: // CIniSectionW acts as a class factory for CIniKeyW Objects - CIniSectionW(CIniFileW* pIniFile, const std::wstring& sSectionName); - CIniSectionW(const CIniSectionW&); // No Copy - CIniSectionW& operator=(const CIniSectionW&); // No Copy - ~CIniSectionW(); - - public: - // Adds a key to the CIniSectionW object, returns a CIniKeyW pointer to the new or existing object - CIniKeyW* AddKey(std::wstring sKeyName); - // Removes a single key by pointer - void RemoveKey(CIniKeyW* pKey); - // Removes a single key by string - void RemoveKey(std::wstring sKey); - // Removes all the keys in the section - void RemoveAllKeys(); - // Returns a CIniKeyW pointer to the key by name, NULL if it was not found - CIniKeyW* GetKey(std::wstring sKeyName) const; - // Returns all keys in the section by KeyIndex only to be used for enumeration - const KeyIndexW& GetKeys() const; - // Returns a KeyValue at a certain section - std::wstring GetKeyValue(const std::wstring& sKey) const; - // Sets a KeyValuePair at a certain section - void SetKeyValue(const std::wstring& sKey, const std::wstring& sValue); - // Sets the section name, returns true on success, fails if the section - // name sSectionName already exists - bool SetSectionName(std::wstring sSectionName); - // Returns the section name - std::wstring GetSectionName() const; - - private: - KeyIndexW::const_iterator _find_key(const std::wstring& sKeyName) const; - KeyIndexW::iterator _find_key(const std::wstring& sKeyName); - - private: - // CIniFileW pointer back to the object that instanciated the section - CIniFileW* m_pIniFile; - // Name of the section - std::wstring m_sSectionName; - // List of CIniKeyW pointers ( Keys in the section ) - KeyIndexW m_keys; - }; // End of CIniSectionW - // Typedef of a List of CIniSectionW pointers - struct ci_less_w - { - bool operator()(const CIniSectionW* s1, const CIniSectionW* s2) const - { -#ifndef _WIN32 - return wcscasecmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0; -#else - return _wcsicmp(s1->m_sSectionName.c_str(), s2->m_sSectionName.c_str()) < 0; -#endif - } - }; - - typedef std::set SecIndexW; - -#ifdef _WIN32 - // Added for VC6 Support -#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300) - friend class CIniSectionW; -#endif -#endif -public: - // Adds a section to the CIniFileW object, returns a CIniFileW pointer to the new or existing object - CIniSectionW* AddSection(std::wstring sSection); - // Removes section by pointer - void RemoveSection(CIniSectionW* pSection); - // Removes a section by its name sSection - void RemoveSection(std::wstring sSection); - // Removes all existing sections - void RemoveAllSections(); - // Returns a CIniSectionW* to the section by name, NULL if it was not found - CIniSectionW* GetSection(std::wstring sSection) const; - // Returns all sections in the inifile by SecIndex, only to be used for enumeration (DO NOT KEEP THE REF OR TRY TO DELETE STUFF!) - const SecIndexW& GetSections() const; - // Returns a KeyValue at a certain section - std::wstring GetKeyValue(const std::wstring& sSection, const std::wstring& sKey) const; - // Sets a KeyValuePair at a certain section - void SetKeyValue(const std::wstring& sSection, const std::wstring& sKey, const std::wstring& sValue); - // Renames an existing section returns true on success, false if the section didn't exist or there was another section with the same sNewSectionName - bool RenameSection(const std::wstring& sSectionName, const std::wstring& sNewSectionName); - // Renames an existing key returns true on success, false if the key didn't exist or there was another section with the same sNewSectionName - bool RenameKey(const std::wstring& sSectionName, const std::wstring& sKeyName, const std::wstring& sNewKeyName); - -private: - SecIndexW::const_iterator _find_sec(const std::wstring& sSection) const; - SecIndexW::iterator _find_sec(const std::wstring& sSection); - -private: - CIniFileW(const CIniFileW&); // No Copy - CIniFileW& operator=(const CIniFileW&); // No Copy - // List of CIniSectionW pointers ( List of sections in the class ) - SecIndexW m_sections; -}; // End of CIniFileW - -// Basic typedefs for ease of use -typedef CIniFileW::CIniMergeW CIniMergeW; -typedef CIniFileW::CIniSectionW CIniSectionW; -typedef CIniSectionW::CIniKeyW CIniKeyW; - -// Pointers -typedef CIniFileW* PCINIW; -typedef CIniKeyW* PCINIKEYW; -typedef CIniSectionW* PCINISECW; - -// Map Types -typedef CIniSectionW::KeyIndexW KeyIndexW; -typedef CIniFileW::SecIndexW SecIndexW; - -std::wostream& operator<<(std::wostream& output, CIniFileW& obj); -std::wistream& operator>>(std::wistream& input, CIniFileW& obj); -std::wistream& operator>>(std::wistream& input, CIniMergeW merger); - -// Additional defines -#ifdef _UNICODE -#define INI_TOKEN_A INI_TOKEN_UNICODE -#define INI_TOKEN_B INI_TOKEN_UNICODE -#define INI_EMPTY INI_EMPTY_UNICODE -typedef CIniMergeW CIniMerge; -typedef CIniFileW CIniFile; -typedef CIniSectionW CIniSection; -typedef CIniKeyW CIniKey; -typedef PCINIW PCINI; -typedef PCINIKEYW PCINIKEY; -typedef PCINISECW PCINISEC; -typedef KeyIndexW KeyIndex; -typedef SecIndexW SecIndex; -#else -#define INI_TOKEN_A INI_TOKEN_ANSI -#define INI_TOKEN_B INI_TOKEN_ANSI -#define INI_EMPTY INI_EMPTY_ANSI -typedef CIniMergeA CIniMerge; -typedef CIniFileA CIniFile; -typedef CIniSectionA CIniSection; -typedef CIniKeyA CIniKey; -typedef PCINIA PCINI; -typedef PCINIKEYA PCINIKEY; -typedef PCINISECA PCINISEC; -typedef KeyIndexA KeyIndex; -typedef SecIndexA SecIndex; -#endif - - -std::wstring str_to_wstr(const std::string& arg); -std::string wstr_to_str(const std::wstring& arg); - -#endif diff --git a/pcsx2/USB/shared/rawinput_usb.cpp b/pcsx2/USB/shared/rawinput_usb.cpp deleted file mode 100644 index cd589c8eb6..0000000000 --- a/pcsx2/USB/shared/rawinput_usb.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "rawinput_usb.h" -#include -#include -#include -#include -#include "USB/platcompat.h" -#include "PAD/Windows/WndProcEater.h" - -extern HINSTANCE hInst; - -namespace shared -{ - namespace rawinput - { - - static std::vector callbacks; - - bool inited = false; - bool skipInput = false; - std::mutex cb_mutex; - - void RegisterCallback(ParseRawInputCB* cb) - { - std::scoped_lock lk(cb_mutex); - if (cb && std::find(callbacks.begin(), callbacks.end(), cb) == callbacks.end()) - callbacks.push_back(cb); - } - - void UnregisterCallback(ParseRawInputCB* cb) - { - std::scoped_lock lk(cb_mutex); - auto it = std::find(callbacks.begin(), callbacks.end(), cb); - if (it != callbacks.end()) - callbacks.erase(it); - } - - static POINT origCursorPos; - static POINT center; - static bool cursorCaptured = false; - - static void WindowResized(HWND hWnd) - { - RECT r; - GetWindowRect(hWnd, &r); - ClipCursor(&r); - center.x = (r.left + r.right) / 2; - center.y = (r.top + r.bottom) / 2; - SetCursorPos(center.x, center.y); - } - - static void CursorCapture(HWND hWnd) - { - Console.WriteLn("Capture cursor\n"); - SetCapture(hWnd); - ShowCursor(0); - - GetCursorPos(&origCursorPos); - - RECT r; - GetWindowRect(hWnd, &r); - ClipCursor(&r); - center.x = (r.left + r.right) / 2; - center.y = (r.top + r.bottom) / 2; - SetCursorPos(center.x, center.y); - cursorCaptured = true; - } - - static void CursorRelease() - { - Console.WriteLn("Release cursor\n"); - if (cursorCaptured) - { - ClipCursor(0); - ReleaseCapture(); - ShowCursor(1); - SetCursorPos(origCursorPos.x, origCursorPos.y); - cursorCaptured = false; - } - } - - static void ToggleCursor(HWND hWnd, RAWKEYBOARD& k) - { - static bool shiftDown = false; - - if (k.VKey == VK_SHIFT || k.VKey == VK_LSHIFT || k.VKey == VK_RSHIFT) - shiftDown = !(k.Flags & RI_KEY_BREAK); - - if (shiftDown && k.VKey == VK_F11 && !k.Flags) - { - if (!cursorCaptured) - CursorCapture(hWnd); - else - CursorRelease(); - } - } - - static int RegisterRaw(HWND hWnd) - { - RAWINPUTDEVICE Rid[4]; - Rid[0].usUsagePage = 0x01; - Rid[0].usUsage = HID_USAGE_GENERIC_GAMEPAD; - Rid[0].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE; // adds game pad - Rid[0].hwndTarget = hWnd; - - Rid[1].usUsagePage = 0x01; - Rid[1].usUsage = HID_USAGE_GENERIC_JOYSTICK; - Rid[1].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE; // adds joystick - Rid[1].hwndTarget = hWnd; - - Rid[2].usUsagePage = 0x01; - Rid[2].usUsage = HID_USAGE_GENERIC_KEYBOARD; - Rid[2].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE; // | RIDEV_NOLEGACY; // adds HID keyboard //and also !ignores legacy keyboard messages - Rid[2].hwndTarget = hWnd; - - Rid[3].usUsagePage = 0x01; - Rid[3].usUsage = HID_USAGE_GENERIC_MOUSE; - Rid[3].dwFlags = hWnd ? RIDEV_INPUTSINK : RIDEV_REMOVE; - Rid[3].hwndTarget = hWnd; - - if (RegisterRawInputDevices(Rid, countof(Rid), sizeof(Rid[0])) == FALSE) - { - //registration failed. Call GetLastError for the cause of the error. - Console.Warning("Could not (de)register raw input devices.\n"); - return 0; - } - return 1; - } - - static ExtraWndProcResult RawInputProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* out) - { - PRAWINPUT pRawInput = nullptr; - UINT bufferSize = 0; - - switch (uMsg) - { - case WM_INPUT: - { - if (skipInput) - break; - - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER)); - pRawInput = (PRAWINPUT)malloc(bufferSize); - - if (!pRawInput) - break; - - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, pRawInput, &bufferSize, sizeof(RAWINPUTHEADER)) > 0) - { - - if (pRawInput->header.dwType == RIM_TYPEKEYBOARD) - ToggleCursor(hWnd, pRawInput->data.keyboard); - - std::lock_guard lk(cb_mutex); - for (auto cb : callbacks) - cb->ParseRawInput(pRawInput); - } - - free(pRawInput); - break; - } - case WM_ENABLE: - skipInput = !wParam; - break; - case WM_ACTIVATE: - skipInput = LOWORD(wParam) == WA_INACTIVE; - if (LOWORD(wParam) == WA_INACTIVE) - CursorRelease(); - break; - case WM_SETFOCUS: - skipInput = false; - break; - case WM_KILLFOCUS: - skipInput = true; - break; - case WM_SIZE: - if (cursorCaptured) - WindowResized(hWnd); - break; - case WM_DESTROY: - Uninitialize(); - break; - } - - return CONTINUE_BLISSFULLY; - } - - int Initialize(void* ptr) - { - skipInput = false; - // Reinitialized without USBclose, like when disc swapping - if (inited) - return 1; - - HWND hWnd = static_cast(ptr); - if (!InitHid()) - return 0; - - RegisterRaw(hWnd); - hWndGSProc.SetWndHandle(hWnd); - hWndGSProc.Eat(RawInputProc, 0); - inited = true; - return 1; - } - - void Uninitialize() - { - if (!inited) - return; - RegisterRaw(nullptr); - hWndGSProc.ReleaseExtraProc(RawInputProc); - inited = false; - } - - } // namespace rawinput -} // namespace shared diff --git a/pcsx2/USB/shared/rawinput_usb.h b/pcsx2/USB/shared/rawinput_usb.h deleted file mode 100644 index 01d0eef660..0000000000 --- a/pcsx2/USB/shared/rawinput_usb.h +++ /dev/null @@ -1,38 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -#include -#include -#include "hidapi.h" - -namespace shared -{ - namespace rawinput - { - - class ParseRawInputCB - { - public: - virtual void ParseRawInput(PRAWINPUT pRawInput) = 0; - }; - - int Initialize(void* hWnd); - void Uninitialize(); - - void RegisterCallback(ParseRawInputCB* cb); - void UnregisterCallback(ParseRawInputCB* cb); - } // namespace rawinput -} // namespace shared diff --git a/pcsx2/USB/shared/ringbuffer.cpp b/pcsx2/USB/shared/ringbuffer.cpp index c3aa67058c..588857b621 100644 --- a/pcsx2/USB/shared/ringbuffer.cpp +++ b/pcsx2/USB/shared/ringbuffer.cpp @@ -170,8 +170,6 @@ void RingBuffer::write(size_t bytes) } else m_end = (m_end + bytes) % m_capacity; - - mLastWrite = hrc::now(); } void RingBuffer::read(size_t bytes) diff --git a/pcsx2/USB/shared/ringbuffer.h b/pcsx2/USB/shared/ringbuffer.h index 0ccb6666da..9928a8d9fc 100644 --- a/pcsx2/USB/shared/ringbuffer.h +++ b/pcsx2/USB/shared/ringbuffer.h @@ -13,17 +13,10 @@ * If not, see . */ -#ifndef RINGBUFFER_H -#define RINGBUFFER_H +#pragma once + #include // for std::min #include -#include - -using hrc = std::chrono::high_resolution_clock; -using ms = std::chrono::milliseconds; -using us = std::chrono::microseconds; -using ns = std::chrono::nanoseconds; -using sec = std::chrono::seconds; class RingBuffer { @@ -98,18 +91,11 @@ public: return (T*)(m_data + m_end); } - long long MilliSecsSinceLastWrite() - { - return std::chrono::duration_cast(hrc::now() - mLastWrite).count(); - } - private: size_t m_begin; size_t m_end; size_t m_capacity; char* m_data; bool m_overrun; - hrc::time_point mLastWrite = hrc::time_point(ns(0)); }; -#endif diff --git a/pcsx2/USB/shared/shared_usb.cpp b/pcsx2/USB/shared/shared_usb.cpp deleted file mode 100644 index dda6e1d032..0000000000 --- a/pcsx2/USB/shared/shared_usb.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "shared_usb.h" -#include - -#ifdef _WIN32 -#include "rawinput_usb.h" -#endif - -namespace shared -{ - - void Initialize(void* ptr) - { -#ifdef _WIN32 - if (!shared::rawinput::Initialize(ptr)) - throw std::runtime_error("Failed to initialize raw input!"); -#endif - } - - void Uninitialize(/*void *ptr*/) - { -#ifdef _WIN32 - shared::rawinput::Uninitialize(/*ptr*/); -#endif - } - -}; // namespace shared diff --git a/pcsx2/USB/shared/shared_usb.h b/pcsx2/USB/shared/shared_usb.h deleted file mode 100644 index 4558e91c2f..0000000000 --- a/pcsx2/USB/shared/shared_usb.h +++ /dev/null @@ -1,21 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -namespace shared -{ - void Initialize(void* ptr); - void Uninitialize(/*void *ptr*/); -}; // namespace shared diff --git a/pcsx2/USB/usb-eyetoy/api_init_linux.cpp b/pcsx2/USB/usb-eyetoy/api_init_linux.cpp deleted file mode 100644 index fbac059bd3..0000000000 --- a/pcsx2/USB/usb-eyetoy/api_init_linux.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "videodeviceproxy.h" -#include "cam-linux.h" - -void usb_eyetoy::RegisterVideoDevice::Register() -{ - auto& inst = RegisterVideoDevice::instance(); - inst.Add(linux_api::APINAME, new VideoDeviceProxy()); -} diff --git a/pcsx2/USB/usb-eyetoy/api_init_win32_eyetoy.cpp b/pcsx2/USB/usb-eyetoy/api_init_win32_eyetoy.cpp deleted file mode 100644 index 6f41feb1d4..0000000000 --- a/pcsx2/USB/usb-eyetoy/api_init_win32_eyetoy.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "videodeviceproxy.h" -#include "cam-windows.h" - -void usb_eyetoy::RegisterVideoDevice::Register() -{ - auto& inst = RegisterVideoDevice::instance(); - inst.Add(windows_api::APINAME, new VideoDeviceProxy()); -} diff --git a/pcsx2/USB/usb-eyetoy/cam-linux.cpp b/pcsx2/USB/usb-eyetoy/cam-linux.cpp index a61282975c..df60a48104 100644 --- a/pcsx2/USB/usb-eyetoy/cam-linux.cpp +++ b/pcsx2/USB/usb-eyetoy/cam-linux.cpp @@ -18,7 +18,6 @@ #include "jpgd.h" #include "jpge.h" #include "jo_mpeg.h" -#include "USB/gtk.h" #include "common/Console.h" #include @@ -242,9 +241,9 @@ namespace usb_eyetoy return 1; } - std::vector getDevList() + std::vector> getDevList() { - std::vector devList; + std::vector> devList; char dev_name[64]; int fd; struct v4l2_capability cap; @@ -260,7 +259,7 @@ namespace usb_eyetoy if (ioctl(fd, VIDIOC_QUERYCAP, &cap) >= 0) { - devList.push_back((char*)cap.card); + devList.emplace_back((char*)cap.card, (char*)cap.card); } close(fd); @@ -603,9 +602,8 @@ namespace usb_eyetoy free(comprBuf); } - V4L2::V4L2(int port) + V4L2::V4L2() { - mPort = port; mpeg_buffer.start = calloc(1, 640 * 480 * 2); } @@ -636,9 +634,7 @@ namespace usb_eyetoy pthread_join(eyetoy_thread, NULL); v4l_close(); } - std::string selectedDevice; - LoadSetting(EyeToyWebCamDevice::TypeName(), mPort, APINAME, N_DEVICE, selectedDevice); - if (v4l_open(selectedDevice) != 0) + if (v4l_open(mHostDevice.c_str()) != 0) return -1; pthread_create(&eyetoy_thread, NULL, &v4l_thread, NULL); eyetoy_running = 1; @@ -674,76 +670,15 @@ namespace usb_eyetoy { mirroring_enabled = state; } - - static void deviceChanged(GtkComboBox* widget, gpointer data) - { - *(int*)data = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - } - - int GtkConfigure(int port, const char* dev_type, void* data) - { - std::string selectedDevice; - LoadSetting(dev_type, port, APINAME, N_DEVICE, selectedDevice); - - GtkWidget* dlg = gtk_dialog_new_with_buttons( - "V4L2 Settings", GTK_WINDOW(data), GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE); - gtk_window_set_default_size(GTK_WINDOW(dlg), 320, 75); - - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - GtkWidget* main_hbox = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(dlg_area_box), main_hbox); - GtkWidget* right_vbox = gtk_vbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 5); - - GtkWidget* rs_cb = new_combobox("Device:", right_vbox); - - std::vector devList = getDevList(); - int sel_idx = 0; - for (uint32_t idx = 0; idx < (uint32_t)devList.size(); idx++) - { - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), devList.at(idx).c_str()); - if (!selectedDevice.empty() && selectedDevice == devList.at(idx)) - { - gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), idx); - sel_idx = idx; - } - } - - int sel_new = 0; - g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(deviceChanged), (gpointer)&sel_new); - - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - - int ret = RESULT_OK; - if (result == GTK_RESPONSE_OK) - { - if (devList.size() && sel_new != sel_idx) - { - if (!SaveSetting(dev_type, port, APINAME, N_DEVICE, devList.at(sel_new))) - { - ret = RESULT_FAILED; - } - } - } - else - { - ret = RESULT_CANCELED; - } - - gtk_widget_destroy(dlg); - return ret; - } - - int V4L2::Configure(int port, const char* dev_type, void* data) - { - return GtkConfigure(port, dev_type, data); - }; - } // namespace linux_api + + std::unique_ptr VideoDevice::CreateInstance() + { + return std::make_unique(); + } + + std::vector> VideoDevice::GetDeviceList() + { + return linux_api::getDevList(); + } } // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-eyetoy/cam-linux.h b/pcsx2/USB/usb-eyetoy/cam-linux.h index e44c595d7b..c05de06242 100644 --- a/pcsx2/USB/usb-eyetoy/cam-linux.h +++ b/pcsx2/USB/usb-eyetoy/cam-linux.h @@ -19,6 +19,7 @@ namespace usb_eyetoy { namespace linux_api { + std::vector> getDevList(); typedef struct _buffer_t { @@ -26,31 +27,16 @@ namespace usb_eyetoy size_t length; } buffer_t; - static const char* APINAME = "V4L2"; - class V4L2 : public VideoDevice { public: - V4L2(int port); + V4L2(); ~V4L2(); int Open(int width, int height, FrameFormat format, int mirror); int Close(); int GetImage(uint8_t* buf, size_t len); void SetMirroring(bool state); int Reset() { return 0; }; - - static const TCHAR* Name() - { - return TEXT("V4L2"); - } - static int Configure(int port, const char* dev_type, void* data); - - int Port() { return mPort; } - void Port(int port) { mPort = port; } - - private: - int mPort; }; - } // namespace linux_api } // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-eyetoy/cam-noop.cpp b/pcsx2/USB/usb-eyetoy/cam-noop.cpp new file mode 100644 index 0000000000..607c2ec8bc --- /dev/null +++ b/pcsx2/USB/usb-eyetoy/cam-noop.cpp @@ -0,0 +1,67 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2020 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include "common/StringUtil.h" + +#include "videodev.h" +#include "usb-eyetoy-webcam.h" +#include "jo_mpeg.h" +#include "USB/USB.h" + +namespace usb_eyetoy +{ + namespace noop_api + { + class NoopVideoDevice final : public VideoDevice + { + public: + int Open(int width, int height, FrameFormat format, int mirror) override + { + return -1; + } + + int Close() override + { + return 0; + } + + int GetImage(uint8_t* buf, size_t len) override + { + return 0; + } + + void SetMirroring(bool state) override + { + } + + int Reset() override + { + return 0; + } + }; + } // namespace noop_api + + std::unique_ptr VideoDevice::CreateInstance() + { + return std::make_unique(); + } + + std::vector> VideoDevice::GetDeviceList() + { + return {}; + } +} // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-eyetoy/cam-windows.cpp b/pcsx2/USB/usb-eyetoy/cam-windows.cpp index 09236ee181..c52d5316f6 100644 --- a/pcsx2/USB/usb-eyetoy/cam-windows.cpp +++ b/pcsx2/USB/usb-eyetoy/cam-windows.cpp @@ -15,14 +15,14 @@ #include "PrecompiledHeader.h" +#include "common/StringUtil.h" + #include #include "videodev.h" #include "cam-windows.h" #include "usb-eyetoy-webcam.h" #include "jo_mpeg.h" - -#include "USB/Win32/Config_usb.h" -#include "USB/Win32/resource_usb.h" +#include "USB/USB.h" namespace usb_eyetoy { @@ -59,9 +59,9 @@ namespace usb_eyetoy return E_NOINTERFACE; } - std::vector getDevList() + std::vector> getDevList() { - std::vector devList; + std::vector> devList; ICreateDevEnum* pCreateDevEnum = nullptr; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum)); @@ -100,7 +100,8 @@ namespace usb_eyetoy } if (SUCCEEDED(hr)) { - devList.push_back(var.bstrVal); + std::string u8name(StringUtil::WideStringToUTF8String(var.bstrVal)); + devList.emplace_back(u8name, u8name); VariantClear(&var); } @@ -203,7 +204,8 @@ namespace usb_eyetoy goto freeVar; } - hr = pGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, IID_IAMStreamConfig, (void**)&pSourceConfig); + hr = pGraphBuilder->FindInterface( + &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, IID_IAMStreamConfig, (void**)&pSourceConfig); if (SUCCEEDED(hr)) { int iCount = 0, iSize = 0; @@ -218,17 +220,13 @@ namespace usb_eyetoy VIDEO_STREAM_CONFIG_CAPS scc; AM_MEDIA_TYPE* pmtConfig; hr = pSourceConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc); - Console.Warning("Camera: GetStreamCaps min=%dx%d max=%dx%d, fmt=%x", - scc.MinOutputSize.cx, scc.MinOutputSize.cy, - scc.MaxOutputSize.cx, scc.MaxOutputSize.cy, - pmtConfig->subtype); + Console.Warning("Camera: GetStreamCaps min=%dx%d max=%dx%d, fmt=%x", scc.MinOutputSize.cx, scc.MinOutputSize.cy, + scc.MaxOutputSize.cx, scc.MaxOutputSize.cy, pmtConfig->subtype); if (SUCCEEDED(hr)) { - if ((pmtConfig->majortype == MEDIATYPE_Video) && - (pmtConfig->formattype == FORMAT_VideoInfo) && - (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) && - (pmtConfig->pbFormat != nullptr)) + if ((pmtConfig->majortype == MEDIATYPE_Video) && (pmtConfig->formattype == FORMAT_VideoInfo) && + (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) && (pmtConfig->pbFormat != nullptr)) { VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat; pVih->bmiHeader.biWidth = frame_width; @@ -383,7 +381,8 @@ namespace usb_eyetoy int comprLen = 0; if (frame_format == format_mpeg) { - comprLen = jo_write_mpeg(comprBuf, data, frame_width, frame_height, JO_BGR24, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_FLIP_Y); + comprLen = jo_write_mpeg( + comprBuf, data, frame_width, frame_height, JO_BGR24, mirroring_enabled ? JO_FLIP_X : JO_NONE, JO_FLIP_Y); } else if (frame_format == format_jpeg) { @@ -419,8 +418,8 @@ namespace usb_eyetoy for (int y = 0; y < 8; y++) for (int x = 0; x < 8; x++) { - int srcx = 4* (8*mx + x); - int srcy = frame_height - 4* (8*my + y) - 1; + int srcx = 4 * (8 * mx + x); + int srcy = frame_height - 4 * (8 * my + y) - 1; unsigned char* src = data + (srcy * frame_width + srcx) * bytesPerPixel; if (srcy < 0) { @@ -501,9 +500,8 @@ namespace usb_eyetoy free(comprBuf); } - DirectShow::DirectShow(int port) + DirectShow::DirectShow() { - mPort = port; pGraphBuilder = nullptr; pGraph = nullptr; pControl = nullptr; @@ -539,13 +537,10 @@ namespace usb_eyetoy create_dummy_frame_eyetoy(); } - std::wstring selectedDevice; - LoadSetting(EyeToyWebCamDevice::TypeName(), Port(), APINAME, N_DEVICE, selectedDevice); - - int ret = InitializeDevice(selectedDevice); + int ret = InitializeDevice(StringUtil::UTF8StringToWideString(mHostDevice)); if (ret < 0) { - Console.Warning("Camera: cannot find '%ls'", selectedDevice.c_str()); + Console.Warning("Camera: cannot find '%s'", mHostDevice.c_str()); return -1; } @@ -590,75 +585,16 @@ namespace usb_eyetoy return len2; }; - void DirectShow::SetMirroring(bool state) - { - mirroring_enabled = state; - } - - BOOL CALLBACK DirectShowDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - int port; - - switch (uMsg) - { - case WM_CREATE: - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); - break; - case WM_INITDIALOG: - { - port = (int)lParam; - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); - - std::wstring selectedDevice; - LoadSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice); - SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_RESETCONTENT, 0, 0); - - std::vector devList = getDevList(); - for (auto i = 0; i != devList.size(); i++) - { - SendDlgItemMessageW(hW, IDC_COMBO1_USB, CB_ADDSTRING, 0, (LPARAM)devList[i].c_str()); - if (selectedDevice == devList.at(i)) - { - SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_SETCURSEL, i, i); - } - } - return TRUE; - } - case WM_COMMAND: - if (HIWORD(wParam) == BN_CLICKED) - { - switch (LOWORD(wParam)) - { - case IDOK: - { - INT_PTR res = RESULT_OK; - static wchar_t selectedDevice[500] = {0}; - GetWindowTextW(GetDlgItem(hW, IDC_COMBO1_USB), selectedDevice, countof(selectedDevice)); - port = (int)GetWindowLongPtr(hW, GWLP_USERDATA); - if (!SaveSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice)) - { - res = RESULT_FAILED; - } - EndDialog(hW, res); - return TRUE; - } - case IDCANCEL: - EndDialog(hW, RESULT_CANCELED); - return TRUE; - } - } - } - return FALSE; - } - - int DirectShow::Configure(int port, const char* dev_type, void* data) - { - Win32Handles handles = *(Win32Handles*)data; - return DialogBoxParam(handles.hInst, - MAKEINTRESOURCE(IDD_DLG_EYETOY_USB), - handles.hWnd, - (DLGPROC)DirectShowDlgProc, port); - }; - + void DirectShow::SetMirroring(bool state) { mirroring_enabled = state; } } // namespace windows_api + + std::unique_ptr VideoDevice::CreateInstance() + { + return std::make_unique(); + } + + std::vector> VideoDevice::GetDeviceList() + { + return windows_api::getDevList(); + } } // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-eyetoy/cam-windows.h b/pcsx2/USB/usb-eyetoy/cam-windows.h index 3e9c5104e2..2ac49c4725 100644 --- a/pcsx2/USB/usb-eyetoy/cam-windows.h +++ b/pcsx2/USB/usb-eyetoy/cam-windows.h @@ -30,15 +30,13 @@ extern GUID CLSID_NullRenderer; } #pragma region qedit.h -struct __declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85")) - ISampleGrabberCB : IUnknown +struct __declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85")) ISampleGrabberCB : IUnknown { virtual HRESULT __stdcall SampleCB(double SampleTime, struct IMediaSample* pSample) = 0; virtual HRESULT __stdcall BufferCB(double SampleTime, unsigned char* pBuffer, long BufferLen) = 0; }; -struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f")) - ISampleGrabber : IUnknown +struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f")) ISampleGrabber : IUnknown { virtual HRESULT __stdcall SetOneShot(long OneShot) = 0; virtual HRESULT __stdcall SetMediaType(struct _AMMediaType* pType) = 0; @@ -49,8 +47,7 @@ struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f")) virtual HRESULT __stdcall SetCallback(struct ISampleGrabberCB* pCallback, long WhichMethodToCallback) = 0; }; -struct __declspec(uuid("c1f400a0-3f08-11d3-9f0b-006008039e37")) - SampleGrabber; +struct __declspec(uuid("c1f400a0-3f08-11d3-9f0b-006008039e37")) SampleGrabber; #pragma endregion @@ -71,6 +68,7 @@ namespace usb_eyetoy { namespace windows_api { + std::vector> getDevList(); typedef void (*DShowVideoCaptureCallback)(unsigned char* data, int len, int bitsperpixel); @@ -80,12 +78,10 @@ namespace usb_eyetoy size_t length = 0; }; - static const char* APINAME = "DirectShow"; - class DirectShow : public VideoDevice { public: - DirectShow(int port); + DirectShow(); ~DirectShow(); int Open(int width, int height, FrameFormat format, int mirror); int Close(); @@ -93,15 +89,6 @@ namespace usb_eyetoy void SetMirroring(bool state); int Reset() { return 0; }; - static const TCHAR* Name() - { - return TEXT("DirectShow"); - } - static int Configure(int port, const char* dev_type, void* data); - - int Port() { return mPort; } - void Port(int port) { mPort = port; } - protected: void SetCallback(DShowVideoCaptureCallback cb) { callbackhandler->SetCallback(cb); } void Start(); @@ -109,8 +96,6 @@ namespace usb_eyetoy int InitializeDevice(std::wstring selectedDevice); private: - int mPort; - wil::unique_couninitialize_call dshowCoInitialize; ICaptureGraphBuilder2* pGraphBuilder; IFilterGraph2* pGraph; diff --git a/pcsx2/USB/usb-eyetoy/jo_mpeg.cpp b/pcsx2/USB/usb-eyetoy/jo_mpeg.cpp index b14d0c73df..62673759a0 100644 --- a/pcsx2/USB/usb-eyetoy/jo_mpeg.cpp +++ b/pcsx2/USB/usb-eyetoy/jo_mpeg.cpp @@ -30,7 +30,6 @@ #include "PrecompiledHeader.h" #include #include -#include #include "jo_mpeg.h" diff --git a/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp b/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp index 8d943c0591..2eb719edd4 100644 --- a/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp +++ b/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp @@ -14,11 +14,12 @@ */ #include "PrecompiledHeader.h" -#include "videodeviceproxy.h" +#include "videodev.h" #include "usb-eyetoy-webcam.h" #include "ov519.h" #include "USB/qemu-usb/desc.h" -#include "USB/shared/inifile_usb.h" +#include "USB/USB.h" +#include "StateWrapper.h" namespace usb_eyetoy { @@ -28,21 +29,22 @@ namespace usb_eyetoy USBDesc desc; USBDescDevice desc_dev; - VideoDevice* videodev; + u32 port; + u32 subtype; + + std::unique_ptr videodev; // struct freeze { - uint8_t regs[0xFF]; //OV519 + uint8_t regs[0xFF]; //OV519 uint8_t i2c_regs[0xFF]; //OV764x int hw_camera_running; int frame_step; - unsigned char* mpeg_frame_data; + std::unique_ptr mpeg_frame_data; unsigned int mpeg_frame_size; unsigned int mpeg_frame_offset; // } f; } EYETOYState; - static EYETOYState* static_state; - static const USBDescStrings desc_strings = { "", "Sony corporation", @@ -51,11 +53,11 @@ namespace usb_eyetoy static void reset_controller(EYETOYState* s) { - if (s->videodev->Type() == TYPE_EYETOY) + if (s->subtype == TYPE_EYETOY) { memcpy(s->regs, ov519_defaults, sizeof(s->regs)); } - else if (s->videodev->Type() == TYPE_OV511P) + else if (s->subtype == TYPE_OV511P) { memcpy(s->regs, ov511p_defaults, sizeof(s->regs)); } @@ -63,26 +65,61 @@ namespace usb_eyetoy static void reset_sensor(EYETOYState* s) { - if (s->videodev->Type() == TYPE_EYETOY) + if (s->subtype == TYPE_EYETOY) { memcpy(s->i2c_regs, ov7648_defaults, sizeof(s->regs)); } - else if (s->videodev->Type() == TYPE_OV511P) + else if (s->subtype == TYPE_OV511P) { memcpy(s->i2c_regs, ov7620_defaults, sizeof(s->regs)); } } - static void eyetoy_handle_reset(USBDevice* dev) + static void open_camera(EYETOYState* s) { - reset_controller((EYETOYState*)dev); - reset_sensor((EYETOYState*)dev); + if (s->hw_camera_running && s->subtype == TYPE_EYETOY) + { + const int width = s->regs[OV519_R10_H_SIZE] << 4; + const int height = s->regs[OV519_R11_V_SIZE] << 3; + const FrameFormat format = s->regs[OV519_RA0_FORMAT] == OV519_RA0_FORMAT_JPEG ? format_jpeg : format_mpeg; + const int mirror = !!(s->i2c_regs[OV7610_REG_COM_A] & OV7610_REG_COM_A_MASK_MIRROR); + Console.WriteLn( + "EyeToy : eyetoy_open(); hw=%d, w=%d, h=%d, fmt=%d, mirr=%d", s->hw_camera_running, width, height, format, mirror); + if (s->videodev->Open(width, height, format, mirror) != 0) + Console.Error("(Eyetoy) Failed to open video device"); + } + else if (s->hw_camera_running && s->subtype == TYPE_OV511P) + { + const int width = 320; + const int height = 240; + const FrameFormat format = format_yuv400; + const int mirror = 0; + Console.WriteLn( + "EyeToy : eyetoy_open(); hw=%d, w=%d, h=%d, fmt=%d, mirr=%d", s->hw_camera_running, width, height, format, mirror); + if (s->videodev->Open(width, height, format, mirror) != 0) + Console.Error("(Eyetoy) Failed to open video device"); + } } - static void webcam_handle_control_eyetoy(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + static void close_camera(EYETOYState* s) { - EYETOYState* s = (EYETOYState*)dev; + Console.WriteLn("EyeToy : eyetoy_close(); hw=%d", s->hw_camera_running); + if (s->hw_camera_running) + { + s->hw_camera_running = 0; + s->videodev->Close(); + } + } + + static void eyetoy_handle_reset(USBDevice* dev) + { + reset_controller(USB_CONTAINER_OF(dev, EYETOYState, dev)); + reset_sensor(USB_CONTAINER_OF(dev, EYETOYState, dev)); + } + + static void webcam_handle_control_eyetoy(USBDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) + { + EYETOYState* s = USB_CONTAINER_OF(dev, EYETOYState, dev); int ret = 0; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -118,8 +155,8 @@ namespace usb_eyetoy if (s->hw_camera_running && s->regs[OV519_RA0_FORMAT] != data[0]) { Console.WriteLn("EyeToy : reinitialize the camera"); - s->dev.klass.close(dev); - s->dev.klass.open(dev); + close_camera(s); + open_camera(s); } break; case OV519_R10_H_SIZE: @@ -129,15 +166,15 @@ namespace usb_eyetoy Console.WriteLn("EyeToy : Image height : %d", data[0] << 3); break; case OV519_GPIO_DATA_OUT0: + { + static char led_state = -1; + if (led_state != data[0]) { - static char led_state = -1; - if (led_state != data[0]) - { - led_state = data[0]; - Console.WriteLn("EyeToy : LED : %d", !!led_state); - } + led_state = data[0]; + Console.WriteLn("EyeToy : LED : %d", !!led_state); } - break; + } + break; case R518_I2C_CTL: if (data[0] == 1) // Commit I2C write { @@ -187,10 +224,9 @@ namespace usb_eyetoy } } - static void webcam_handle_control_ov511p(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + static void webcam_handle_control_ov511p(USBDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) { - EYETOYState* s = (EYETOYState*)dev; + EYETOYState* s = USB_CONTAINER_OF(dev, EYETOYState, dev); int ret = 0; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -246,7 +282,7 @@ namespace usb_eyetoy static void webcam_handle_data_eyetoy(USBDevice* dev, USBPacket* p) { - EYETOYState* s = (EYETOYState*)dev; + EYETOYState* s = USB_CONTAINER_OF(dev, EYETOYState, dev); static const int max_ep_size = 896; uint8_t devep = p->ep->nr; @@ -254,7 +290,7 @@ namespace usb_eyetoy { Console.WriteLn("EyeToy : initialization done; start the camera"); s->hw_camera_running = 1; - s->dev.klass.open(dev); + open_camera(s); } switch (p->pid) @@ -267,20 +303,19 @@ namespace usb_eyetoy if (s->frame_step == 0) { - s->mpeg_frame_size = s->videodev->GetImage(s->mpeg_frame_data, 640 * 480 * 3); + s->mpeg_frame_size = s->videodev->GetImage(s->mpeg_frame_data.get(), 640 * 480 * 3); if (s->mpeg_frame_size == 0) { p->status = USB_RET_NAK; break; } - uint8_t header[] = { - 0xFF, 0xFF, 0xFF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t header[] = {0xFF, 0xFF, 0xFF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; header[0x0A] = s->regs[OV519_RA0_FORMAT] == OV519_RA0_FORMAT_JPEG ? 0x03 : 0x01; memcpy(data, header, sizeof(header)); int data_pk = max_ep_size - sizeof(header); - memcpy(data + sizeof(header), s->mpeg_frame_data, data_pk); + memcpy(data + sizeof(header), s->mpeg_frame_data.get(), data_pk); s->mpeg_frame_offset = data_pk; s->frame_step++; } @@ -289,14 +324,13 @@ namespace usb_eyetoy int data_pk = s->mpeg_frame_size - s->mpeg_frame_offset; if (data_pk > max_ep_size) data_pk = max_ep_size; - memcpy(data, s->mpeg_frame_data + s->mpeg_frame_offset, data_pk); + memcpy(data, s->mpeg_frame_data.get() + s->mpeg_frame_offset, data_pk); s->mpeg_frame_offset += data_pk; s->frame_step++; } else { - uint8_t footer[] = { - 0xFF, 0xFF, 0xFF, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t footer[] = {0xFF, 0xFF, 0xFF, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; footer[0x0A] = s->regs[OV519_RA0_FORMAT] == OV519_RA0_FORMAT_JPEG ? 0x03 : 0x01; memcpy(data, footer, sizeof(footer)); s->frame_step = 0; @@ -321,7 +355,7 @@ namespace usb_eyetoy static void webcam_handle_data_ov511p(USBDevice* dev, USBPacket* p) { - EYETOYState* s = (EYETOYState*)dev; + EYETOYState* s = USB_CONTAINER_OF(dev, EYETOYState, dev); static const int max_ep_size = 960; // 961 uint8_t devep = p->ep->nr; @@ -329,7 +363,7 @@ namespace usb_eyetoy { Console.WriteLn("EyeToy : initialization done; start the camera"); s->hw_camera_running = 1; - s->dev.klass.open(dev); + open_camera(s); } switch (p->pid) @@ -342,7 +376,7 @@ namespace usb_eyetoy if (s->frame_step == 0) { - s->mpeg_frame_size = s->videodev->GetImage(s->mpeg_frame_data, 640 * 480 * 3); + s->mpeg_frame_size = s->videodev->GetImage(s->mpeg_frame_data.get(), 640 * 480 * 3); if (s->mpeg_frame_size == 0) { p->status = USB_RET_NAK; @@ -353,7 +387,7 @@ namespace usb_eyetoy memcpy(data, header, sizeof(header)); int data_pk = max_ep_size - sizeof(header); - memcpy(data + sizeof(header), s->mpeg_frame_data, data_pk); + memcpy(data + sizeof(header), s->mpeg_frame_data.get(), data_pk); s->mpeg_frame_offset = data_pk; s->frame_step++; } @@ -362,7 +396,7 @@ namespace usb_eyetoy int data_pk = s->mpeg_frame_size - s->mpeg_frame_offset; if (data_pk > max_ep_size) data_pk = max_ep_size; - memcpy(data, s->mpeg_frame_data + s->mpeg_frame_offset, data_pk); + memcpy(data, s->mpeg_frame_data.get() + s->mpeg_frame_offset, data_pk); s->mpeg_frame_offset += data_pk; s->frame_step++; } @@ -385,80 +419,28 @@ namespace usb_eyetoy static void eyetoy_handle_destroy(USBDevice* dev) { - EYETOYState* s = (EYETOYState*)dev; + EYETOYState* s = USB_CONTAINER_OF(dev, EYETOYState, dev); + close_camera(s); delete s; } - int eyetoy_open(USBDevice* dev) + USBDevice* EyeToyWebCamDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { - EYETOYState* s = (EYETOYState*)dev; - if (s->hw_camera_running && s->videodev->Type() == TYPE_EYETOY) - { - const int width = s->regs[OV519_R10_H_SIZE] << 4; - const int height = s->regs[OV519_R11_V_SIZE] << 3; - const FrameFormat format = s->regs[OV519_RA0_FORMAT] == OV519_RA0_FORMAT_JPEG ? format_jpeg : format_mpeg; - const int mirror = !!(s->i2c_regs[OV7610_REG_COM_A] & OV7610_REG_COM_A_MASK_MIRROR); - Console.Error("EyeToy : eyetoy_open(); hw=%d, w=%d, h=%d, fmt=%d, mirr=%d", s->hw_camera_running, - width, height, format, mirror); - s->videodev->Open(width, height, format, mirror); - } - else if (s->hw_camera_running && s->videodev->Type() == TYPE_OV511P) - { - const int width = 320; - const int height = 240; - const FrameFormat format = format_yuv400; - const int mirror = 0; - Console.Error("EyeToy : eyetoy_open(); hw=%d, w=%d, h=%d, fmt=%d, mirr=%d", s->hw_camera_running, - width, height, format, mirror); - s->videodev->Open(width, height, format, mirror); - } - return 1; - } - - void eyetoy_close(USBDevice* dev) - { - EYETOYState* s = (EYETOYState*)dev; - Console.Error("EyeToy : eyetoy_close(); hw=%d", s->hw_camera_running); - if (s->hw_camera_running) - { - s->hw_camera_running = 0; - s->videodev->Close(); - } - } - - USBDevice* EyeToyWebCamDevice::CreateDevice(int port) - { - VideoDevice* videodev = nullptr; - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - VideoDeviceProxyBase* proxy = RegisterVideoDevice::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("Invalid video device API: %" SFMTs "\n", varApi.c_str()); - return NULL; - } - - videodev = proxy->CreateObject(port); + std::unique_ptr videodev(VideoDevice::CreateInstance()); if (!videodev) - return NULL; + { + Console.Error("Failed to create video device."); + return nullptr; + } - videodev->Type(GetSelectedSubtype(std::make_pair(port, TypeName()))); - - EYETOYState* s; - s = new EYETOYState(); - if (!s) - return NULL; + videodev->HostDevice(USB::GetConfigString(si, port, TypeName(), "device_name")); + EYETOYState* s = new EYETOYState(); + s->subtype = subtype; s->desc.full = &s->desc_dev; s->desc.str = desc_strings; - if (videodev->Type() == TYPE_EYETOY) + if (subtype == TYPE_EYETOY) { if (usb_desc_parse_dev(eyetoy_dev_descriptor, sizeof(eyetoy_dev_descriptor), s->desc, s->desc_dev) < 0) goto fail; @@ -467,7 +449,7 @@ namespace usb_eyetoy s->dev.klass.handle_control = webcam_handle_control_eyetoy; s->dev.klass.handle_data = webcam_handle_data_eyetoy; } - else if (videodev->Type() == TYPE_OV511P) + else if (subtype == TYPE_OV511P) { if (usb_desc_parse_dev(ov511p_dev_descriptor, sizeof(ov511p_dev_descriptor), s->desc, s->desc_dev) < 0) goto fail; @@ -477,59 +459,71 @@ namespace usb_eyetoy s->dev.klass.handle_data = webcam_handle_data_ov511p; } - s->videodev = videodev; + s->videodev = std::move(videodev); s->dev.speed = USB_SPEED_FULL; s->dev.klass.handle_attach = usb_desc_attach; s->dev.klass.handle_reset = eyetoy_handle_reset; s->dev.klass.unrealize = eyetoy_handle_destroy; - s->dev.klass.open = eyetoy_open; - s->dev.klass.close = eyetoy_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = s->desc.str[2]; usb_desc_init(&s->dev); usb_ep_init(&s->dev); - eyetoy_handle_reset((USBDevice*)s); + eyetoy_handle_reset(&s->dev); s->hw_camera_running = 0; s->frame_step = 0; - s->mpeg_frame_data = (unsigned char*)calloc(1, 640 * 480 * 3); + s->mpeg_frame_data = std::make_unique(640 * 480 * 3); + std::memset(s->mpeg_frame_data.get(), 0, 640 * 480 * 3); s->mpeg_frame_offset = 0; - static_state = s; - return (USBDevice*)s; + return &s->dev; fail: - eyetoy_handle_destroy((USBDevice*)s); + eyetoy_handle_destroy(&s->dev); return nullptr; } - int EyeToyWebCamDevice::Configure(int port, const std::string& api, void* data) + const char* EyeToyWebCamDevice::Name() const { - auto proxy = RegisterVideoDevice::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; + return "Webcam (EyeToy)"; } - int EyeToyWebCamDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* EyeToyWebCamDevice::TypeName() const { - /*switch (mode) - { - case FREEZE_LOAD: - if (!s) return -1; - s->f = *(PADState::freeze *)data; - s->pad->Type((PS2WheelTypes)s->f.wheel_type); - return sizeof(PADState::freeze); - case FREEZE_SAVE: - if (!s) return -1; - *(PADState::freeze *)data = s->f; - return sizeof(PADState::freeze); - case FREEZE_SIZE: - return sizeof(PADState::freeze); - default: - break; - }*/ - return 0; + return "webcam"; } + bool EyeToyWebCamDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + EYETOYState* s = USB_CONTAINER_OF(dev, EYETOYState, dev); + if (!sw.DoMarker("EYETOYState")) + return false; + + sw.DoBytes(s->regs, sizeof(s->regs)); + sw.DoBytes(s->i2c_regs, sizeof(s->i2c_regs)); + sw.Do(&s->frame_step); + sw.DoBytes(s->mpeg_frame_data.get(), 640 * 480 * 3); + sw.Do(&s->mpeg_frame_size); + sw.Do(&s->mpeg_frame_offset); + return !sw.HasError(); + } + + void EyeToyWebCamDevice::UpdateSettings(USBDevice* dev, SettingsInterface& si) const + { + // TODO: Update device name + } + + std::vector EyeToyWebCamDevice::SubTypes() const + { + return {"Sony EyeToy", "Konami Capture Eye"}; + } + + gsl::span EyeToyWebCamDevice::Settings(u32 subtype) const + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::StringList, "device_name", "Device Name", "Selects the device to capture images from.", "", nullptr, + nullptr, nullptr, nullptr, nullptr, &VideoDevice::GetDeviceList}, + }; + return info; + } } // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.h b/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.h index af67ddb2c5..51001372e0 100644 --- a/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.h +++ b/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.h @@ -13,13 +13,11 @@ * If not, see . */ -#ifndef USBEYETOYWEBCAM_H -#define USBEYETOYWEBCAM_H +#pragma once -#include "USB/qemu-usb/vl.h" -#include "USB/configuration.h" +#include "USB/qemu-usb/qusb.h" #include "USB/deviceproxy.h" -#include "videodeviceproxy.h" +#include "USB/usb-eyetoy/videodev.h" #include @@ -469,37 +467,16 @@ namespace usb_eyetoy 0x75, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - class EyeToyWebCamDevice + class EyeToyWebCamDevice final : public DeviceProxy { public: - virtual ~EyeToyWebCamDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Webcam (EyeToy)"); - } - static const char* TypeName() - { - return "webcam"; - } - static std::list ListAPIs() - { - return RegisterVideoDevice::instance().Names(); - } - static const TCHAR* LongAPIName(const std::string& name) - { - auto proxy = RegisterVideoDevice::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {"Sony EyeToy", "Konami Capture Eye"}; - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* Name() const override; + const char* TypeName() const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override; + std::vector SubTypes() const override; + gsl::span Settings(u32 subtype) const override; }; } // namespace usb_eyetoy -#endif diff --git a/pcsx2/USB/usb-eyetoy/videodev.h b/pcsx2/USB/usb-eyetoy/videodev.h index 694e92f21f..d1723508fb 100644 --- a/pcsx2/USB/usb-eyetoy/videodev.h +++ b/pcsx2/USB/usb-eyetoy/videodev.h @@ -13,10 +13,11 @@ * If not, see . */ -#ifndef VIDEODEV_H -#define VIDEODEV_H -#include "USB/qemu-usb/vl.h" -#include "USB/configuration.h" +#pragma once +#include "USB/qemu-usb/qusb.h" +#include +#include +#include namespace usb_eyetoy { @@ -43,15 +44,13 @@ namespace usb_eyetoy virtual void SetMirroring(bool state) = 0; virtual int Reset() = 0; - virtual int Port() { return mPort; } - virtual void Port(int port) { mPort = port; } - virtual int Type() { return mType; } - virtual void Type(int type) { mType = type; } + virtual const std::string& HostDevice() const { return mHostDevice; } + virtual void HostDevice(std::string dev) { mHostDevice = std::move(dev); } + + static std::unique_ptr CreateInstance(); + static std::vector> GetDeviceList(); protected: - int mPort; - int mType; + std::string mHostDevice; }; - } // namespace usb_eyetoy -#endif diff --git a/pcsx2/USB/usb-eyetoy/videodeviceproxy.h b/pcsx2/USB/usb-eyetoy/videodeviceproxy.h deleted file mode 100644 index 379b2e6828..0000000000 --- a/pcsx2/USB/usb-eyetoy/videodeviceproxy.h +++ /dev/null @@ -1,90 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef VIDEODEVICEPROXY_H -#define VIDEODEVICEPROXY_H -#include -#include -#include -#include -#include -#include "videodev.h" -#include "USB/helpers.h" -#include "USB/deviceproxy.h" - -namespace usb_eyetoy -{ - - class VideoDeviceError : public std::runtime_error - { - public: - VideoDeviceError(const char* msg) - : std::runtime_error(msg) - { - } - virtual ~VideoDeviceError() throw() {} - }; - - class VideoDeviceProxyBase : public ProxyBase - { - VideoDeviceProxyBase(const VideoDeviceProxyBase&) = delete; - - public: - VideoDeviceProxyBase() {} - VideoDeviceProxyBase(const std::string& name); - virtual VideoDevice* CreateObject(int port) const = 0; - }; - - template - class VideoDeviceProxy : public VideoDeviceProxyBase - { - VideoDeviceProxy(const VideoDeviceProxy&) = delete; - - public: - VideoDeviceProxy() {} - VideoDeviceProxy(const std::string& name) - : VideoDeviceProxyBase(name) - { - } - VideoDevice* CreateObject(int port) const - { - try - { - return new T(port); - } - catch (VideoDeviceError& err) - { - (void)err; - return nullptr; - } - } - virtual const TCHAR* Name() const - { - return T::Name(); - } - virtual int Configure(int port, const char* dev_type, void* data) - { - return T::Configure(port, dev_type, data); - } - }; - - class RegisterVideoDevice : public RegisterProxy - { - public: - static void Register(); - }; - -} // namespace usb_eyetoy -#endif diff --git a/pcsx2/USB/usb-hid/api_init_linux.cpp b/pcsx2/USB/usb-hid/api_init_linux.cpp deleted file mode 100644 index 36fbaaab0c..0000000000 --- a/pcsx2/USB/usb-hid/api_init_linux.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "hidproxy.h" -#include "evdev/evdev.h" -#include "noop.h" - -void usb_hid::RegisterUsbHID::Register() -{ - auto& inst = RegisterUsbHID::instance(); - inst.Add(usb_hid::evdev::APINAME, new UsbHIDProxy()); - inst.Add(usb_hid::noop::APINAME, new UsbHIDProxy()); -} diff --git a/pcsx2/USB/usb-hid/api_init_win32_hid.cpp b/pcsx2/USB/usb-hid/api_init_win32_hid.cpp deleted file mode 100644 index 3894519d8f..0000000000 --- a/pcsx2/USB/usb-hid/api_init_win32_hid.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "hidproxy.h" -#include "raw/rawinput.h" -#include "noop.h" - -void usb_hid::RegisterUsbHID::Register() -{ - auto& inst = RegisterUsbHID::instance(); - inst.Add(usb_hid::raw::APINAME, new UsbHIDProxy()); - inst.Add(usb_hid::noop::APINAME, new UsbHIDProxy()); -} diff --git a/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp b/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp deleted file mode 100644 index 6aa906676f..0000000000 --- a/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "USB/usb-hid/usb-hid.h" -#include "evdev.h" -#include -#include "USB/gtk.h" -#include -#include - -namespace usb_hid -{ - namespace evdev - { - -#define EVDEV_DIR "/dev/input/by-path/" - - typedef std::vector> devs_t; - struct ConfigData - { - int port; - devs_t devs; - devs_t::const_iterator iter; - }; - - static void PopulateHIDs(ConfigData& cfg, HIDType hid_type) - { - std::stringstream str; - struct dirent* dp; - const char* devstr[] = {"event-kbd", "event-mouse"}; - - cfg.devs.clear(); - cfg.devs.push_back(std::make_pair("None", "")); - - DIR* dirp = opendir(EVDEV_DIR); - if (dirp == NULL) - { - Console.Warning("Error opening " EVDEV_DIR ": %s\n", strerror(errno)); - return; - } - - // Loop over dir entries using readdir - int len = strlen(devstr[hid_type]); - while ((dp = readdir(dirp)) != NULL) - { - // Only select names that end in 'event-joystick' - int devlen = strlen(dp->d_name); - if (devlen >= len) - { - const char* const start = dp->d_name + devlen - len; - if (strncmp(start, devstr[hid_type], len) == 0) - { - - str.clear(); - str.str(""); - str << EVDEV_DIR << dp->d_name; - - char name[1024]; - std::string dev_path = str.str(); - if (!GetEvdevName(dev_path, name)) - { - //XXX though it also could mean that controller is unusable - cfg.devs.push_back(std::make_pair(dp->d_name, dev_path)); - } - else - { - cfg.devs.push_back(std::make_pair(std::string(name), dev_path)); - } - } - } - } - closedir(dirp); - } - - static void combo_changed(GtkComboBox* widget, gpointer data) - { - gint idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - ConfigData* cfg = reinterpret_cast(data); - - if (!cfg) - return; - - //std::string& name = (cfg->devs.begin() + idx)->first; - cfg->iter = (cfg->devs.begin() + idx); - - if (idx > 0) - { - } - } - - int GtkHidConfigure(int port, const char* dev_type, HIDType hid_type, GtkWindow* parent) - { - GtkWidget *main_hbox, *right_vbox, *rs_cb; - - assert((int)HIDTYPE_MOUSE == 1); //make sure there is atleast two types so we won't go beyond array length - - ConfigData cfg; - cfg.port = port; - - PopulateHIDs(cfg, hid_type); - cfg.iter = cfg.devs.end(); - - std::string path; - LoadSetting(dev_type, port, APINAME, N_DEVICE, path); - - // --------------------------- - GtkWidget* dlg = gtk_dialog_new_with_buttons( - "HID Evdev Settings", parent, GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE); - gtk_window_set_default_size(GTK_WINDOW(dlg), 320, 240); - - // --------------------------- - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - - main_hbox = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(dlg_area_box), main_hbox); - - // left_vbox = gtk_vbox_new (FALSE, 5); - // gtk_box_pack_start (GTK_BOX (main_hbox), left_vbox, TRUE, TRUE, 5); - right_vbox = gtk_vbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 5); - - // --------------------------- - rs_cb = new_combobox("Device:", right_vbox); - - const int evdev_dir_len = strlen(EVDEV_DIR); - int idx = 0, sel_idx = 0; - for (auto& it : cfg.devs) - { - std::stringstream str; - str << it.first; - if (!it.second.empty()) - str << " [" << it.second.substr(evdev_dir_len) << "]"; - - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), str.str().c_str()); - if (!path.empty() && it.second == path) - { - sel_idx = idx; - } - idx++; - } - - //g_object_set_data (G_OBJECT (rs_cb), CFG, &cfg); - g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(combo_changed), reinterpret_cast(&cfg)); - gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), sel_idx); - - // --------------------------- - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - - int ret = RESULT_OK; - if (result == GTK_RESPONSE_OK) - { - if (cfg.iter != cfg.devs.end()) - { - if (!SaveSetting(dev_type, port, APINAME, N_DEVICE, cfg.iter->second)) - ret = RESULT_FAILED; - } - } - else - ret = RESULT_CANCELED; - - gtk_widget_destroy(dlg); - return ret; - } - - int EvDev::Configure(int port, const char* dev_type, HIDType hid_type, void* data) - { - return GtkHidConfigure(port, dev_type, hid_type, GTK_WINDOW(data)); - } - -#undef EVDEV_DIR - } // namespace evdev -} // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/evdev/evdev.cpp b/pcsx2/USB/usb-hid/evdev/evdev.cpp deleted file mode 100644 index b151eade8a..0000000000 --- a/pcsx2/USB/usb-hid/evdev/evdev.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "evdev.h" -#include -#include -#include "USB/usb-hid/hidproxy.h" -#include "USB/qemu-usb/input-keymap-linux-to-qcode.h" - -#ifdef USING_X11 -#include -#include -extern Display* g_GSdsp; -extern Window g_GSwin; -#endif - -namespace usb_hid -{ - namespace evdev - { - -#define test_bit(nr, addr) \ - (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0) -#define NBITS(x) ((((x)-1) / (sizeof(long) * 8)) + 1) - - bool FindHid(const std::string& evphys, std::string& hid_dev) - { - int fd; - char buf[256]; - - std::stringstream str; - struct dirent* dp; - - DIR* dirp = opendir("/dev/input/"); - if (dirp == NULL) - { - Console.Warning("Error opening /dev/input/"); - return false; - } - - while ((dp = readdir(dirp)) != NULL) - { - if (strncmp(dp->d_name, "hidraw", 6) == 0) - { - - str.clear(); - str.str(""); - str << "/dev/input/" << dp->d_name; - fd = open(str.str().c_str(), O_RDWR | O_NONBLOCK); - - if (fd < 0) - { - Console.Warning("Evdev: Unable to open device: %s", str.str().c_str()); - continue; - } - - memset(buf, 0x0, sizeof(buf)); - //res = ioctl(fd, HIDIOCGRAWNAME(256), buf); - - /* res = ioctl(fd, HIDIOCGRAWPHYS(256), buf); - if (res < 0) - Console.Warning("HIDIOCGRAWPHYS"); - else - */ - close(fd); - if (evphys == buf) - { - closedir(dirp); - hid_dev = str.str(); - return true; - } - } - } - //quit: - closedir(dirp); - return false; - } - - int EvDev::TokenOut(const uint8_t* data, int len) - { - return len; - } - - int EvDev::Open() - { - // Make sure there is atleast two types so we won't go beyond array length - assert((int)HIDTYPE_MOUSE == 1); - std::stringstream name; - - mHandle = -1; - - std::string path; - if (!LoadSetting(mDevType, mPort, APINAME, N_DEVICE, path)) - { - return 1; - } - - if (path.empty() || !file_exists(path)) - goto quit; - - if ((mHandle = open(path.c_str(), O_RDWR | O_NONBLOCK)) < 0) - { - goto quit; - } - - if (!mReaderThreadIsRunning) - { - if (mReaderThread.joinable()) - mReaderThread.join(); - mReaderThread = std::thread(&EvDev::ReaderThread, this); - } - return 0; - - quit: - Close(); - return 1; - } - - int EvDev::Close() - { - if (mHandle != -1) - close(mHandle); - - mHandle = -1; - return 0; - } - - void EvDev::ReaderThread() - { - ssize_t len; - input_event events[32]; - - mReaderThreadIsRunning = true; - - while (mHandle != -1) - { - //Non-blocking read sets len to -1 and errno to EAGAIN if no new data - while ((len = read(mHandle, &events, sizeof(events))) > -1) - { - InputEvent ev{}; - len /= sizeof(events[0]); - for (int i = 0; i < len; i++) - { - input_event& event = events[i]; - switch (event.type) - { - case EV_ABS: - { - - if (mHIDState->kind == HID_MOUSE || mHIDState->kind == HID_KEYBOARD) // usually mouse position is expected to be relative - continue; - - if (!mHIDState->ptr.eh_entry) - continue; - - ev.type = INPUT_EVENT_KIND_ABS; - if (event.code == ABS_X) - { - ev.u.abs.axis = INPUT_AXIS_X; - ev.u.abs.value = event.value; - mHIDState->ptr.eh_entry(mHIDState, &ev); - } - else if (event.code == ABS_Y) - { - ev.u.abs.axis = INPUT_AXIS_Y; - ev.u.abs.value = event.value; - mHIDState->ptr.eh_entry(mHIDState, &ev); - } - } - break; - case EV_REL: - { - if (mHIDState->kind == HID_KEYBOARD || !mHIDState->ptr.eh_entry) - continue; - - ev.type = INPUT_EVENT_KIND_REL; - ev.u.rel.value = event.value; - if (event.code == ABS_X) - { - ev.u.rel.axis = INPUT_AXIS_X; - mHIDState->ptr.eh_entry(mHIDState, &ev); - } - else if (event.code == ABS_Y) - { - ev.u.rel.axis = INPUT_AXIS_Y; - mHIDState->ptr.eh_entry(mHIDState, &ev); - } - } - break; - case EV_KEY: - { - -#ifdef USING_X11 //FIXME not thread-safe - if (event.code == KEY_LEFTSHIFT || event.code == KEY_RIGHTSHIFT) - shift = (event.value > 0); - - if (event.code == KEY_F12 && (event.value == 1) && shift && g_GSdsp) - { - if (!grabbed) - { - grabbed = true; - XGrabPointer(g_GSdsp, g_GSwin, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, g_GSwin, None, CurrentTime); - XGrabKeyboard(g_GSdsp, g_GSwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); - // Hides globally :( - XFixesHideCursor(g_GSdsp, g_GSwin); - } - else - { - grabbed = false; - XUngrabPointer(g_GSdsp, CurrentTime); - XUngrabKeyboard(g_GSdsp, CurrentTime); - XFixesShowCursor(g_GSdsp, g_GSwin); - } - } -#endif - - if (mHIDState->kind == HID_KEYBOARD && mHIDState->kbd.eh_entry) - { - - QKeyCode qcode = Q_KEY_CODE_UNMAPPED; - if (event.code < (uint16_t)qemu_input_map_linux_to_qcode_len) - qcode = qemu_input_map_linux_to_qcode[event.code]; - - if (event.value < 2) - { - ev.type = INPUT_EVENT_KIND_KEY; - ev.u.key.down = (event.value == 1); // 2 if repeat - ev.u.key.key.type = KEY_VALUE_KIND_QCODE; - ev.u.key.key.u.qcode = qcode; - - mHIDState->kbd.eh_entry(mHIDState, &ev); - } - } - - if (mHIDState->kind != HID_KEYBOARD && mHIDState->ptr.eh_entry) - { - ev.type = INPUT_EVENT_KIND_BTN; - switch (event.code) - { - case BTN_LEFT: - ev.u.btn.button = INPUT_BUTTON_LEFT; - ev.u.btn.down = (event.value == 1); - mHIDState->ptr.eh_entry(mHIDState, &ev); - break; - case BTN_RIGHT: - ev.u.btn.button = INPUT_BUTTON_RIGHT; - ev.u.btn.down = (event.value == 1); - mHIDState->ptr.eh_entry(mHIDState, &ev); - break; - case BTN_MIDDLE: - ev.u.btn.button = INPUT_BUTTON_MIDDLE; - ev.u.btn.down = (event.value == 1); - mHIDState->ptr.eh_entry(mHIDState, &ev); - break; - default: - break; - } - } - } - break; - case EV_SYN: //TODO useful? - { - switch (event.code) - { - case SYN_REPORT: - if (mHIDState->ptr.eh_sync) //TODO sync here? - mHIDState->ptr.eh_sync(mHIDState); - break; - case SYN_DROPPED: - //restore last good state - break; - } - } - break; - default: - break; - } - } - - if (len < (ssize_t)sizeof(input_event) && errno != EAGAIN) - { - break; - } - } - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - mReaderThreadIsRunning = false; - } - - } // namespace evdev -} // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/evdev/evdev.h b/pcsx2/USB/usb-hid/evdev/evdev.h deleted file mode 100644 index aa022d06fc..0000000000 --- a/pcsx2/USB/usb-hid/evdev/evdev.h +++ /dev/null @@ -1,89 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -#include "USB/usb-hid/usb-hid.h" -#include "USB/linux/util.h" -#include "common/Console.h" -#include -#include -#include -#include -#include - -namespace usb_hid -{ - namespace evdev - { - - static const char* APINAME = "evdev"; - - class EvDev : public UsbHID - { - public: - EvDev(int port, const char* dev_type) - : UsbHID(port, dev_type) - , mHandle(-1) - , mReaderThreadIsRunning(false) - { - } - - ~EvDev() { Close(); } - int Open(); - int Close(); - int TokenIn(uint8_t* buf, int len); - int TokenOut(const uint8_t* data, int len); - int Reset() { return 0; } - - static const TCHAR* Name() - { - return TEXT("Evdev"); - } - - static int Configure(int port, const char* dev_type, HIDType hid_type, void* data); - - protected: - void ReaderThread(); - - int mHandle; - - std::thread mReaderThread; - std::atomic mReaderThreadIsRunning; - }; - - template - bool GetEvdevName(const std::string& path, char (&name)[_Size]) - { - int fd = 0; - if ((fd = open(path.c_str(), O_RDONLY)) < 0) - { - Console.Warning("Cannot open %s\n", path.c_str()); - } - else - { - if (ioctl(fd, EVIOCGNAME(_Size), name) < -1) - { - Console.Warning("Cannot get controller's name\n"); - close(fd); - return false; - } - close(fd); - return true; - } - return false; - } - - } // namespace evdev -} // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/hidproxy.h b/pcsx2/USB/usb-hid/hidproxy.h deleted file mode 100644 index 08ae7673b5..0000000000 --- a/pcsx2/USB/usb-hid/hidproxy.h +++ /dev/null @@ -1,93 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef USBHIDPROXY_H -#define USBHIDPROXY_H -#include -#include -#include -#include -#include -#include "usb-hid.h" -#include "USB/helpers.h" -#include "USB/deviceproxy.h" - -namespace usb_hid -{ - - class UsbHIDError : public std::runtime_error - { - public: - UsbHIDError(const char* msg) - : std::runtime_error(msg) - { - } - virtual ~UsbHIDError() throw() {} - }; - - class UsbHIDProxyBase : public ProxyBase - { - UsbHIDProxyBase(const UsbHIDProxyBase&) = delete; - - public: - UsbHIDProxyBase() {} - UsbHIDProxyBase(const std::string& name); - virtual UsbHID* CreateObject(int port, const char* dev_type) const = 0; - // ProxyBase::Configure is ignored - virtual int Configure(int port, const char* dev_type, void* data) { return RESULT_CANCELED; } - virtual int Configure(int port, const char* dev_type, HIDType hid_type, void* data) = 0; - }; - - template - class UsbHIDProxy : public UsbHIDProxyBase - { - UsbHIDProxy(const UsbHIDProxy&) = delete; - - public: - UsbHIDProxy() {} - UsbHIDProxy(const std::string& name) - : UsbHIDProxyBase(name) - { - } - UsbHID* CreateObject(int port, const char* dev_type) const - { - try - { - return new T(port, dev_type); - } - catch (UsbHIDError& err) - { - (void)err; - return nullptr; - } - } - virtual const TCHAR* Name() const - { - return T::Name(); - } - virtual int Configure(int port, const char* dev_type, HIDType hid_type, void* data) - { - return T::Configure(port, dev_type, hid_type, data); - } - }; - - class RegisterUsbHID : public RegisterProxy - { - public: - static void Register(); - }; - -} // namespace usb_hid -#endif diff --git a/pcsx2/USB/usb-hid/noop.h b/pcsx2/USB/usb-hid/noop.h deleted file mode 100644 index ee25a86f96..0000000000 --- a/pcsx2/USB/usb-hid/noop.h +++ /dev/null @@ -1,52 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "usb-hid.h" -#include "hidproxy.h" - -namespace usb_hid -{ - namespace noop - { - - static const char* APINAME = "noop"; - - class NOOP : public UsbHID - { - public: - NOOP(int port, const char* dev_type) - : UsbHID(port, dev_type) - { - } - ~NOOP() {} - int Open() { return 0; } - int Close() { return 0; } - int TokenIn(uint8_t* buf, int len) { return len; } - int TokenOut(const uint8_t* data, int len) { return len; } - int Reset() { return 0; } - - static const TCHAR* Name() - { - return TEXT("NOOP"); - } - - static int Configure(int port, const char* dev_type, HIDType type, void* data) - { - return RESULT_CANCELED; - } - }; - - } // namespace noop -} // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/raw/rawinput.cpp b/pcsx2/USB/usb-hid/raw/rawinput.cpp deleted file mode 100644 index 26c7d398cc..0000000000 --- a/pcsx2/USB/usb-hid/raw/rawinput.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "rawinput.h" -#include "USB/Win32/Config_usb.h" -#include "USB/qemu-usb/input-keymap.h" -#include "USB/qemu-usb/input-keymap-win32-to-qcode.h" - -namespace usb_hid -{ - namespace raw - { - -#define CHECK(exp) \ - do \ - { \ - if (!(exp)) \ - goto Error; \ - } while (0) -#define SAFE_FREE(p) \ - do \ - { \ - if (p) \ - { \ - free(p); \ - (p) = NULL; \ - } \ - } while (0) - - // VKEY from https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes - // and convert to HID usage id from "10 Keyboard/Keypad Page (0x07)" - // https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf - - int RawInput::TokenOut(const uint8_t* data, int len) - { - // std::array report{ 0 }; - // memcpy(report.data() + 1, data, report.size() - 1); - - return len; - } - - static void ParseRawInput(PRAWINPUT pRawInput, HIDState* hs) - { - PHIDP_PREPARSED_DATA pPreparsedData = NULL; - HIDP_CAPS Caps; - PHIDP_BUTTON_CAPS pButtonCaps = NULL; - PHIDP_VALUE_CAPS pValueCaps = NULL; - UINT bufferSize = 0; - ULONG usageLength, value; - TCHAR name[1024] = {0}; - UINT nameSize = 1024; - RID_DEVICE_INFO devInfo = {0}; - std::wstring devName; - USHORT capsLength = 0; - USAGE usage[32] = {0}; - int numberOfButtons; - - GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, name, &nameSize); - - devName = name; - std::transform(devName.begin(), devName.end(), devName.begin(), ::toupper); - - CHECK(GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0); - CHECK(pPreparsedData = (PHIDP_PREPARSED_DATA)malloc(bufferSize)); - CHECK((int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0); - CHECK(HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS); - - //Get pressed buttons - CHECK(pButtonCaps = (PHIDP_BUTTON_CAPS)malloc(sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps)); - //If fails, maybe wheel only has axes - capsLength = Caps.NumberInputButtonCaps; - HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData); - - numberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1; - usageLength = countof(usage); //numberOfButtons; - - NTSTATUS stat; - if ((stat = HidP_GetUsages( - HidP_Input, pButtonCaps->UsagePage, 0, usage, &usageLength, pPreparsedData, - (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid)) == HIDP_STATUS_SUCCESS) - { - for (uint32_t i = 0; i < usageLength; i++) - { - uint16_t btn = usage[i] - pButtonCaps->Range.UsageMin; - } - } - - /// Get axes' values - CHECK(pValueCaps = (PHIDP_VALUE_CAPS)malloc(sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps)); - capsLength = Caps.NumberInputValueCaps; - if (HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS) - { - for (USHORT i = 0; i < capsLength; i++) - { - if (HidP_GetUsageValue( - HidP_Input, pValueCaps[i].UsagePage, 0, - pValueCaps[i].Range.UsageMin, &value, pPreparsedData, - (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS) - { - continue; // if here then maybe something is up with HIDP_CAPS.NumberInputValueCaps - } - - switch (pValueCaps[i].Range.UsageMin) - { - case HID_USAGE_GENERIC_X: //0x30 - case HID_USAGE_GENERIC_Y: - case HID_USAGE_GENERIC_Z: - case HID_USAGE_GENERIC_RX: - case HID_USAGE_GENERIC_RY: - case HID_USAGE_GENERIC_RZ: //0x35 - //int axis = (value * 0x3FFF) / pValueCaps[i].LogicalMax; - break; - case HID_USAGE_GENERIC_HATSWITCH: - //Console.Warning("Hat: %02X\n", value); - break; - } - } - } - - Error: - SAFE_FREE(pPreparsedData); - SAFE_FREE(pButtonCaps); - SAFE_FREE(pValueCaps); - } - - static void ParseRawInputKB(RAWKEYBOARD& k, HIDState* hs) - { - if (hs->kind != HID_KEYBOARD || !hs->kbd.eh_entry) - return; - - if (KEYBOARD_OVERRUN_MAKE_CODE == k.MakeCode) - return; - - QKeyCode qcode = Q_KEY_CODE_UNMAPPED; - if (k.VKey < (int)qemu_input_map_win32_to_qcode.size()) - qcode = qemu_input_map_win32_to_qcode[k.VKey]; - - //TODO - if (k.Flags & RI_KEY_E0) - { - if (Q_KEY_CODE_SHIFT == qcode) - qcode = Q_KEY_CODE_SHIFT_R; - else if (Q_KEY_CODE_CTRL == qcode) - qcode = Q_KEY_CODE_CTRL_R; - else if (Q_KEY_CODE_ALT == qcode) - qcode = Q_KEY_CODE_ALT_R; - } - - InputEvent ev{}; - ev.type = INPUT_EVENT_KIND_KEY; - ev.u.key.down = !(k.Flags & RI_KEY_BREAK); - ev.u.key.key.type = KEY_VALUE_KIND_QCODE; - ev.u.key.key.u.qcode = qcode; - - hs->kbd.eh_entry(hs, &ev); - } - - static void SendPointerEvent(InputEvent& ev, HIDState* hs) - { - hs->ptr.eh_entry(hs, &ev); - } - - static void ParseRawInputMS(RAWMOUSE& m, HIDState* hs) - { - if (!hs->ptr.eh_entry || (hs->kind != HID_MOUSE && hs->kind != HID_TABLET)) - return; - - int z = 0; - InputEvent ev{}; - - if (m.usButtonFlags & RI_MOUSE_WHEEL) - z = (short)m.usButtonData / WHEEL_DELTA; - - ev.type = INPUT_EVENT_KIND_BTN; - - if (m.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) - { - ev.u.btn.button = INPUT_BUTTON_LEFT; - ev.u.btn.down = true; - SendPointerEvent(ev, hs); - } - if (m.ulButtons & RI_MOUSE_LEFT_BUTTON_UP) - { - ev.u.btn.button = INPUT_BUTTON_LEFT; - ev.u.btn.down = false; - SendPointerEvent(ev, hs); - } - - if (m.ulButtons & RI_MOUSE_RIGHT_BUTTON_DOWN) - { - ev.u.btn.button = INPUT_BUTTON_RIGHT; - ev.u.btn.down = true; - SendPointerEvent(ev, hs); - } - if (m.ulButtons & RI_MOUSE_RIGHT_BUTTON_UP) - { - ev.u.btn.button = INPUT_BUTTON_RIGHT; - ev.u.btn.down = false; - SendPointerEvent(ev, hs); - } - - if (m.ulButtons & RI_MOUSE_MIDDLE_BUTTON_DOWN) - { - ev.u.btn.button = INPUT_BUTTON_MIDDLE; - ev.u.btn.down = true; - SendPointerEvent(ev, hs); - } - if (m.ulButtons & RI_MOUSE_MIDDLE_BUTTON_UP) - { - ev.u.btn.button = INPUT_BUTTON_MIDDLE; - ev.u.btn.down = false; - SendPointerEvent(ev, hs); - } - - if (z != 0) - { - ev.u.btn.button = (z < 0) ? INPUT_BUTTON_WHEEL_DOWN : INPUT_BUTTON_WHEEL_UP; - for (int i = 0; i < z; i++) - { - ev.u.btn.down = true; - SendPointerEvent(ev, hs); - ev.u.btn.down = false; // TODO needs an UP event? - SendPointerEvent(ev, hs); - } - } - - if (m.usFlags & MOUSE_MOVE_ABSOLUTE) - { - /*ev.type = INPUT_EVENT_KIND_ABS; - ev.u.abs.axis = INPUT_AXIS_X; - ev.u.abs.value = m.lLastX; - SendPointerEvent(ev, hs); - - ev.u.abs.axis = INPUT_AXIS_Y; - ev.u.abs.value = m.lLastY; - SendPointerEvent(ev, hs);*/ - } - else - { - ev.type = INPUT_EVENT_KIND_REL; - ev.u.rel.axis = INPUT_AXIS_X; - ev.u.rel.value = m.lLastX; - SendPointerEvent(ev, hs); - - ev.u.rel.axis = INPUT_AXIS_Y; - ev.u.rel.value = m.lLastY; - SendPointerEvent(ev, hs); - } - - - if (hs->ptr.eh_sync) - hs->ptr.eh_sync(hs); - } - - void RawInput::ParseRawInput(PRAWINPUT pRawInput) - { - if (pRawInput->header.dwType == RIM_TYPEKEYBOARD) - ParseRawInputKB(pRawInput->data.keyboard, mHIDState); - else if (pRawInput->header.dwType == RIM_TYPEMOUSE) - ParseRawInputMS(pRawInput->data.mouse, mHIDState); - // else - // ParseRawInput(pRawInput, mHIDState); - } - - int RawInput::Open() - { - Close(); - shared::rawinput::RegisterCallback(this); - return 0; - } - - int RawInput::Close() - { - shared::rawinput::UnregisterCallback(this); - Reset(); - return 0; - } - - int RawInput::Configure(int port, const char* dev_type, HIDType type, void* data) - { - Win32Handles* h = (Win32Handles*)data; - INT_PTR res = RESULT_CANCELED; - return res; - } - - } // namespace raw -} // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/raw/rawinput.h b/pcsx2/USB/usb-hid/raw/rawinput.h deleted file mode 100644 index 5d3199d9a0..0000000000 --- a/pcsx2/USB/usb-hid/raw/rawinput.h +++ /dev/null @@ -1,54 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "USB/shared/rawinput_usb.h" -#include "USB/usb-hid/hidproxy.h" -#include "USB/usb-hid/usb-hid.h" - -namespace usb_hid -{ - namespace raw - { - - static const char* APINAME = "rawinput"; - - class RawInput : public UsbHID, shared::rawinput::ParseRawInputCB - { - public: - RawInput(int port, const char* dev_type) - : UsbHID(port, dev_type) - { - } - ~RawInput() - { - Close(); - } - int Open(); - int Close(); - // int TokenIn(uint8_t *buf, int len); - int TokenOut(const uint8_t* data, int len); - int Reset() { return 0; } - void ParseRawInput(PRAWINPUT pRawInput); - - static const TCHAR* Name() - { - return TEXT("Raw Input"); - } - - static int Configure(int port, const char* dev_type, HIDType, void* data); - }; - - } // namespace raw -} // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/usb-buzzer.cpp b/pcsx2/USB/usb-hid/usb-buzzer.cpp deleted file mode 100644 index 6edfb42146..0000000000 --- a/pcsx2/USB/usb-hid/usb-buzzer.cpp +++ /dev/null @@ -1,936 +0,0 @@ -/* - * QEMU USB HID devices - * - * Copyright (c) 2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "vl.h" -#include -#include -extern "C" { -#include "USB/ddk/hidsdi.h" -} - -#pragma comment(lib, "setupapi.lib") -#pragma comment(lib, "ddk/hid.lib") - -#if 0 -/* HID interface requests */ -#define GET_REPORT 0xa101 -#define GET_IDLE 0xa102 -#define GET_PROTOCOL 0xa103 -#define SET_IDLE 0x210a -#define SET_PROTOCOL 0x210b - -#define USB_MOUSE 1 -#define USB_TABLET 2 - -typedef struct USBKeyboardState { - USBDevice dev; - int keyboard_grabbed; -} USBKeyboardState; - -extern HWND gsWnd; - -#define VK_BASED - -#ifdef VK_BASED -static const uint8_t vk_to_key_code[] = { -0x00, //FAIL: 0x00 -0x00, //FAIL: LMOUSE -0x00, //FAIL: RMOUSE -0x00, //FAIL: Break -0x00, //FAIL: MMOUSE -0x00, //FAIL: X1MOUSE -0x00, //FAIL: X2MOUSE -0x00, //FAIL: 0x00 -0x2A, //OK: Backspace -0x2B, //OK: Tab -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x9C, //OK: Clear -0x28, //FAIL: ENTER -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: SHIFT -0x00, //FAIL: CTRL -0x00, //FAIL: ALT -0x48, //OK: Pause -0x39, //OK: Caps Lock -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -//0x00, //FAIL: 0x00 -//0x00, //FAIL: 0x00 -//0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x29, //FAIL: ESC -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x2C, //OK: Spacebar -#ifdef ENABLE_KEYPAD_Fx -0x4B, //FAIL: PAGE UP -0x4E, //FAIL: PAGE DOWN -0x4D, //OK: End -0x4A, //OK: Home -0x50, //FAIL: LEFT ARROW -0x52, //FAIL: UP ARROW -0x4F, //FAIL: RIGHT ARROW -0x51, //FAIL: DOWN ARROW -0x77, //OK: Select -0x00, //FAIL: PRINT -0x74, //OK: Execute -0x46, //FAIL: PRINT SCREEN -0x49, //FAIL: INS -0x4C, //FAIL: DEL -0x75, //OK: Help VK_HOME -#else -0x00, //FAIL: PAGE UP -0x00, //FAIL: PAGE DOWN -0x00, //OK: End -0x00, //OK: Home -0x00, //FAIL: LEFT ARROW -0x00, //FAIL: UP ARROW -0x00, //FAIL: RIGHT ARROW -0x00, //FAIL: DOWN ARROW -0x00, //OK: Select -0x00, //FAIL: PRINT -0x00, //OK: Execute -0x00, //FAIL: PRINT SCREEN -0x00, //FAIL: INS -0x00, //FAIL: DEL -0x00, //OK: Help VK_HOME -#endif -0x27, //OK: 0 -0x1E, //OK: 1 -0x1F, //OK: 2 -0x20, //OK: 3 -0x21, //OK: 4 -0x22, //OK: 5 -0x23, //OK: 6 -0x24, //OK: 7 -0x25, //OK: 8 -0x26, //OK: 9 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: not found -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x04, //OK: A -0x05, //OK: B -0x06, //OK: C -0x07, //OK: D -0x08, //OK: E -0x09, //OK: F -0x0A, //OK: G -0x0B, //OK: H -0x0C, //OK: I -0x0D, //OK: J -0x0E, //OK: K -0x0F, //OK: L -0x10, //OK: M -0x11, //OK: N -0x12, //OK: O -0x13, //OK: P -0x14, //OK: Q -0x15, //OK: R -0x16, //OK: S -0x17, //OK: T -0x18, //OK: U -0x19, //OK: V -0x1A, //OK: W -0x1B, //OK: X -0x1C, //OK: Y -0x1D, //OK: Z -#ifdef ENABLE_KEYPAD_Fx -0xE3, //OK: LGUI -0xE7, //OK: RGUI -0x65, //OK: Application -0x00, //FAIL: 0x00 -0x00, //FAIL: SLEEP -0x62, //OK: Keypad 0 -0x59, //OK: Keypad 1 -0x5A, //OK: Keypad 2 -0x5B, //OK: Keypad 3 -0x5C, //OK: Keypad 4 -0x5D, //OK: Keypad 5 -0x5E, //OK: Keypad 6 -0x5F, //OK: Keypad 7 -0x60, //OK: Keypad 8 -0x61, //OK: Keypad 9 -0x55, //OK: Keypad * -0x57, //OK: Keypad + -0x9F, //OK: Separator -0x56, //OK: Keypad - -0x63, //OK: Keypad . -0x54, //OK: Keypad / -0x3A, //OK: F1 -0x3B, //OK: F2 -0x3C, //OK: F3 -0x3D, //OK: F4 -0x3E, //OK: F5 -0x3F, //OK: F6 -0x40, //OK: F7 -0x41, //OK: F8 -0x42, //OK: F9 -0x43, //OK: F10 -0x44, //OK: F11 -0x45, //OK: F12 -0x68, //OK: F13 -0x69, //OK: F14 -0x6A, //OK: F15 -0x6B, //OK: F16 -0x6C, //OK: F17 -0x6D, //OK: F18 -0x6E, //OK: F19 -0x6F, //OK: F20 -0x70, //OK: F21 -0x71, //OK: F22 -0x72, //OK: F23 -0x73, //OK: F24 -#else -0x00, //OK: LGUI -0x00, //OK: RGUI -0x00, //OK: Application -0x00, //FAIL: 0x00 -0x00, //FAIL: SLEEP -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00, -#endif -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x83, //OK: NUM LOCK -0x47, //OK: Scroll Lock -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0xE1, //OK: LSHIFT -0xE5, //OK: RSHIFT -0xE0, //OK: LCONTROL -0xE4, //OK: RCONTROL -0xE3, //OK: LGUI -0xE7, //OK: RGUI -0x00, //FAIL: Windows 2000/XP: Browser Back -0x00, //FAIL: Windows 2000/XP: Browser Forward -0x00, //FAIL: Windows 2000/XP: Browser Refresh -0x00, //FAIL: Windows 2000/XP: Browser Stop -0x00, //FAIL: Windows 2000/XP: Browser Search -0x00, //FAIL: Windows 2000/XP: Browser Favorites -0x00, //FAIL: Windows 2000/XP: Browser Start and Home -0x00, //FAIL: Windows 2000/XP: Volume Mute -0x00, //FAIL: Windows 2000/XP: Volume Down -0x00, //FAIL: Windows 2000/XP: Volume Up -0x00, //FAIL: Windows 2000/XP: Next Track -0x00, //FAIL: Windows 2000/XP: Previous Track -0x00, //FAIL: Windows 2000/XP: Stop Media -0x00, //FAIL: Windows 2000/XP: Play/Pause Media -0x00, //FAIL: Windows 2000/XP: Start Mail -0x00, //FAIL: Windows 2000/XP: Select Media -0x00, //FAIL: Windows 2000/XP: Start Application 1 -0x00, //FAIL: Windows 2000/XP: Start Application 2 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x33, //FAIL: Windows 2000/XP: For the US standard keyboard, the ';:' key -0x2E, //FAIL: Windows 2000/XP: For any country/region, the '+' -0x36, //FAIL: Windows 2000/XP: For any country/region, the ',' -0x2D, //FAIL: Windows 2000/XP: For any country/region, the '-' -0x37, //FAIL: Windows 2000/XP: For any country/region, the '.' -0x38, //FAIL: Windows 2000/XP: For the US standard keyboard, the '/?' key -0x35, //FAIL: Windows 2000/XP: For the US standard keyboard, the '`~' key -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: not found -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x2F, //FAIL: Windows 2000/XP: For the US standard keyboard, the '[{' key -0x31, //FAIL: Windows 2000/XP: For the US standard keyboard, the '\|' key -0x30, //FAIL: Windows 2000/XP: For the US standard keyboard, the ']}' key -0x34, //FAIL: Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key -0x00, //FAIL: Used for miscellaneous characters; it can vary byboard. -0x00, //FAIL: Reserved -0x00, //FAIL: OEM specific -0x32, //FAIL: Windows 2000/XP: Either the angle bracket or the backslash key on the RT 102-key keyboard -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCEE6 -0x00, //FAIL: OEM specific -0x00, //FAIL: Windows 2000/XP: Used to pass Unicode characters as if they E8 -0x00, //FAIL: Unassigned -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: 0x00 -0x00, //FAIL: Attn -0xA3, //OK: CrSel -0xA4, //OK: ExSel -0x00, //FAIL: Erase EOF -0x00, //FAIL: Play -0x00, //FAIL: Zoom -0x00, //FAIL: Reserved -0x00, //FAIL: PA1 -0x9C, //OK: Clear -0x00, //FAIL: 0x00 -}; -#else -#ifdef ENABLE_KEYPAD_Fx -static const unsigned char scan_to_usb[] = { -0x00,0x29,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x2f,0x30,0x2a,0x2b, -0x14,0x1a,0x08,0x15,0x17,0x1c,0x18,0x0c,0x12,0x13,0x33,0x2e,0x28,0xe0,0x04,0x16, -0x07,0x09,0x0a,0x0b,0x0d,0x0e,0x0f,0x35,0x34,0x31,0xe1,0x38,0x1d,0x1b,0x06,0x19, -0x05,0x11,0x10,0x36,0x37,0x2d,0xe5,0x55,0xe3,0x2c,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, -0x3f,0x40,0x41,0x42,0x43,0x83,0x47,0x5f,0x60,0x61,0x56,0x5c,0x5d,0x5e,0x57,0x59, -0x5a,0x5b,0x62,0x63,0x46,0x00,0x32,0x44,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x75,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe4,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x54,0x00,0x00,0xe7,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe7,0x65,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#else -static const unsigned char scan_to_usb[] = { -0x00,0x29,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x2f,0x30,0x2a,0x2b, -0x14,0x1a,0x08,0x15,0x17,0x1c,0x18,0x0c,0x12,0x13,0x33,0x2e,0x28,0xe0,0x04,0x16, -0x07,0x09,0x0a,0x0b,0x0d,0x0e,0x0f,0x35,0x34,0x31,0xe1,0x38,0x1d,0x1b,0x06,0x19, -0x05,0x11,0x10,0x36,0x37,0x2d,0xe5,0x00,0xe3,0x2c,0x39,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x83,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x46,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe4,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3,0xe7,0x65,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; -#endif -#endif - -/* mostly the same values as the Bochs USB Keyboard device */ -static const uint8_t qemu_keyboard_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x00, /* u16 bcdUSB; v1.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - -// 0x27, 0x06, /* u16 idVendor; */ - 0x4C, 0x05, - // 0x01, 0x00, /* u16 idProduct; */ - 0x00, 0x10, - 0x00, 0x00, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -static const uint8_t qemu_keyboard_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 50, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; */ - 0x01, /* u8 if_bInterfaceSubClass; */ - 0x01, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x05, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 50, 0, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - -static const uint8_t qemu_tablet_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 50, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; */ - 0x01, /* u8 if_bInterfaceSubClass; */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x05, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x03, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 74, 0, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - -static const uint8_t qemu_keyboard_hid_report_descriptor[] = { - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xa1, 0x01, // COLLECTION (Application) - 0x05, 0x07, // USAGE_PAGE (Keyboard) - 0x19, 0xe0, // USAGE_MINIMUM (Keyboard Left Control) - 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x08, // REPORT_SIZE (8) - 0x81, 0x03, // INPUT (Cnst,Var,Abs) - 0x95, 0x05, // REPORT_COUNT (5) - 0x75, 0x01, // REPORT_SIZE (1) - 0x05, 0x08, // USAGE_PAGE (LEDs) - 0x19, 0x01, // USAGE_MINIMUM (Num Lock) - 0x29, 0x05, // USAGE_MAXIMUM (Kana) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x03, // REPORT_SIZE (3) - 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) - 0x95, 0x06, // REPORT_COUNT (6) - 0x75, 0x08, // REPORT_SIZE (8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x65, // LOGICAL_MAXIMUM (101) - 0x05, 0x07, // USAGE_PAGE (Keyboard) - 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) - 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) - 0x81, 0x00, // INPUT (Data,Ary,Abs) - 0xc0 // END_COLLECTION -}; - -#define BUZZER_VID 0x054C -#define BUZZER_PID 0x1000 -#define BUZZER_PID2 0x0002 - -unsigned char comp_data[6]; -HANDLE usb_buzzer=(HANDLE)-1; -HANDLE readData=(HANDLE)-1; -OVERLAPPED ovl; - -static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len) -{ - unsigned char data[6]; - - ReadFile(usb_buzzer, data, 6, 0, &ovl); - - if(!memcmp(data, comp_data, 6) || (data[0]!=0x00 && data[1]!=0x7F && data[2]!=0x7F)){ - //No event from buzzers - // CancelIo(usb_buzzer); - return USB_RET_STALL; - }else{ - //We got an event !!! - memset(buf, 0, 6); - memcpy(comp_data, data, 6); - - buf[0]=0x7F; - buf[1]=0x7F; - buf[2]=data[3]; - buf[3]=data[4]; - buf[4]=data[5]|0xF0; - - return 16; - } - - return 16; -} - -static void usb_keyboard_handle_reset(USBDevice *dev) -{ - USBKeyboardState *s = (USBKeyboardState *)dev; -} - -static int usb_keyboard_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) -{ - USBKeyboardState *s = (USBKeyboardState *)dev; - int ret = 0; - unsigned char buf[8]; - - switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_keyboard_dev_descriptor, - sizeof(qemu_keyboard_dev_descriptor)); - ret = sizeof(qemu_keyboard_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_keyboard_config_descriptor, - sizeof(qemu_keyboard_config_descriptor)); - ret = sizeof(qemu_keyboard_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "Generic USB Keyboard"); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "PCSX2/QEMU"); - break; - case 4: - ret = set_usb_string(data, "HID Keyboard"); - break; - case 5: - ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_INTERFACE: - data[0] = 0; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_INTERFACE: - ret = 0; - break; - /* hid specific requests */ - case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case 0x22: - memcpy(data, qemu_keyboard_hid_report_descriptor, - sizeof(qemu_keyboard_hid_report_descriptor)); - ret = sizeof(qemu_keyboard_hid_report_descriptor); - break; - default: - goto fail; - } - break; - case GET_REPORT: - ret = usb_keyboard_poll(s, data, length); - break; - case 0x2109: - ret=0; - memset(buf, 0, 8); - buf[2]=data[1]; - buf[3]=data[2]; - buf[4]=data[3]; - buf[5]=data[4]; - CancelIo(usb_buzzer); - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - break; - case SET_IDLE: - ret = 0; - break; - default: - fail: - ret = USB_RET_STALL; - break; - } - return ret; -} - -static int usb_keyboard_handle_data(USBDevice *dev, int pid, - uint8_t devep, uint8_t *data, int len) -{ - USBKeyboardState *s = (USBKeyboardState *)dev; - int ret = 0; - unsigned char buf[8]; - - switch(pid) { - case USB_TOKEN_IN: - if (devep == 1) { - ret = usb_keyboard_poll(s, data, len); - } else { - goto fail; - } - break; - case USB_TOKEN_OUT: - ret=0; - memset(buf, 0, 8); - buf[2]=data[1]; - buf[3]=data[2]; - buf[4]=data[3]; - buf[5]=data[4]; - CancelIo(usb_buzzer); - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - break; - default: - fail: - ret = USB_RET_STALL; - break; - } - return ret; -} - -static void usb_keyboard_handle_destroy(USBDevice *dev) -{ - USBKeyboardState *s = (USBKeyboardState *)dev; - - //qemu_add_keyboard_event_handler(NULL, NULL, 0); - free(s); - CloseHandle(usb_buzzer); -} - -USBDevice *usb_keyboard_init(void) -{ - USBKeyboardState *s; - - s = (USBKeyboardState *)malloc(sizeof(USBKeyboardState)); - if (!s) - return NULL; - memset(s,0,sizeof(USBKeyboardState)); - s->dev.speed = USB_SPEED_FULL; - s->dev.handle_packet = usb_generic_handle_packet; - - s->dev.handle_reset = usb_keyboard_handle_reset; - s->dev.handle_control = usb_keyboard_handle_control; - s->dev.handle_data = usb_keyboard_handle_data; - s->dev.handle_destroy = usb_keyboard_handle_destroy; - - strncpy(s->dev.devname, "Generic USB Keyboard", sizeof(s->dev.devname)); - - int i=0; - DWORD needed=0; - unsigned char buf[8]; - HDEVINFO devInfo; - GUID guid; - SP_DEVICE_INTERFACE_DATA diData; - PSP_DEVICE_INTERFACE_DETAIL_DATA didData; - HIDD_ATTRIBUTES attr; - - readData=CreateEvent(0, 0, 0, 0); - memset(&ovl, 0, sizeof(OVERLAPPED)); - ovl.hEvent=readData; - ovl.Offset=0; - ovl.OffsetHigh=0; - - HidD_GetHidGuid(&guid); - - devInfo=SetupDiGetClassDevs(&guid, 0, 0, DIGCF_DEVICEINTERFACE); - if(!devInfo)return 0; - - diData.cbSize=sizeof(diData); - - while(SetupDiEnumDeviceInterfaces(devInfo, 0, &guid, i, &diData)){ - if(usb_buzzer!=INVALID_HANDLE_VALUE)CloseHandle(usb_buzzer); - - SetupDiGetDeviceInterfaceDetail(devInfo, &diData, 0, 0, &needed, 0); - - didData=(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(needed); - didData->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - if(!SetupDiGetDeviceInterfaceDetail(devInfo, &diData, didData, needed, 0, 0)){ - free(didData); - break; - } - - usb_buzzer=CreateFile(didData->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - if(usb_buzzer==INVALID_HANDLE_VALUE){ - free(didData); - i++; - continue; - } - - HidD_GetAttributes(usb_buzzer, &attr); - - - if((attr.VendorID==BUZZER_VID) && (attr.ProductID==BUZZER_PID || attr.ProductID==BUZZER_PID2)){ - //We've found our buzzers !!! - free(didData); - - memset(buf, 0, 8); - buf[2]=0xFF; - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - Sleep(100); - - memset(buf, 0, 8); - buf[3]=0xFF; - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - Sleep(100); - - memset(buf, 0, 8); - buf[4]=0xFF; - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - Sleep(100); - - memset(buf, 0, 8); - buf[5]=0xFF; - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - Sleep(100); - - memset(buf, 0, 8); - WriteFile(usb_buzzer, buf, 8, 0, &ovl); - break; - } - i++; - } - - return (USBDevice *)s; -} -#endif diff --git a/pcsx2/USB/usb-hid/usb-hid.cpp b/pcsx2/USB/usb-hid/usb-hid.cpp index ed5895dc7c..3af198e5fb 100644 --- a/pcsx2/USB/usb-hid/usb-hid.cpp +++ b/pcsx2/USB/usb-hid/usb-hid.cpp @@ -25,71 +25,37 @@ #include "PrecompiledHeader.h" #include "USB/deviceproxy.h" -#include "hidproxy.h" #include "USB/qemu-usb/desc.h" +#include "USB/qemu-usb/USBinternal.h" #include "usb-hid.h" -#include "USB/shared/inifile_usb.h" +#include "USB/USB.h" +#include "StateWrapper.h" -#define CONTAINER_OF(p, type, field) ((type*)((char*)p - ((ptrdiff_t) & ((type*)0)->field))) +#include "Frontend/InputManager.h" namespace usb_hid { - - typedef struct UsbHIDState + struct UsbHIDState { - USBDevice dev; - USBDesc desc; - USBDescDevice desc_dev; + explicit UsbHIDState(u32 port_); - UsbHID* usbhid; + USBDevice dev{}; + USBDesc desc{}; + USBDescDevice desc_dev{}; - USBEndpoint* intr; - uint8_t port; - struct freeze - { - HIDState hid; - int ep; - } f; + USBEndpoint* intr = nullptr; + HIDState hid{}; - } UsbHIDState; + u32 port = 0; - std::list HIDKbdDevice::ListAPIs() - { - return RegisterUsbHID::instance().Names(); - } + std::map keycode_mapping; - const TCHAR* HIDKbdDevice::LongAPIName(const std::string& name) - { - auto proxy = RegisterUsbHID::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } + void SetKeycodeMapping(); - std::list BeatManiaDevice::ListAPIs() - { - return RegisterUsbHID::instance().Names(); - } - - const TCHAR* BeatManiaDevice::LongAPIName(const std::string& name) - { - auto proxy = RegisterUsbHID::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } - std::list HIDMouseDevice::ListAPIs() - { - return RegisterUsbHID::instance().Names(); - } - - const TCHAR* HIDMouseDevice::LongAPIName(const std::string& name) - { - auto proxy = RegisterUsbHID::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } + void QueueKeyboardState(KeyValue keycode, bool pressed); + void QueueMouseAxisState(InputPointerAxis axis, float delta); + void QueueMouseButtonState(InputButton button, bool pressed); + }; enum { @@ -124,8 +90,8 @@ namespace usb_hid /* mostly the same values as the Bochs USB Keyboard device */ static const uint8_t kbd_dev_desc[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ 0x10, 0x00, /* u16 bcdUSB; v1.0 */ 0x00, /* u8 bDeviceClass; */ @@ -139,26 +105,26 @@ namespace usb_hid 0x00, 0x10, 0x00, 0x00, /* u16 bcdDevice */ - STR_MANUFACTURER, /* u8 iManufacturer; */ + STR_MANUFACTURER, /* u8 iManufacturer; */ STR_PRODUCT_KEYBOARD, /* u8 iProduct; */ - STR_SERIALNUMBER, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ + STR_SERIALNUMBER, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ }; static const uint8_t kbd_config_desc[] = { /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; Bit 7: must be set, 6: Self-powered, 5: Remote wakeup, 4..0: resvd */ - 50, /* u8 MaxPower; */ + 50, /* u8 MaxPower; */ /* USB 1.1: * USB 2.0, single TT organization (mandatory): @@ -183,27 +149,27 @@ namespace usb_hid 0x05, /* u8 if_iInterface; */ /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 63, 0, /* u16 len */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 63, 0, /* u16 len */ /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ }; /* mostly the same values as the Bochs USB Mouse device */ static const uint8_t qemu_mouse_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ 0x10, 0x00, /* u16 bcdUSB; v1.0 */ 0x00, /* u8 bDeviceClass; */ @@ -215,26 +181,26 @@ namespace usb_hid 0x01, 0x00, /* u16 idProduct; */ 0x00, 0x00, /* u16 bcdDevice */ - STR_MANUFACTURER, /* u8 iManufacturer; */ + STR_MANUFACTURER, /* u8 iManufacturer; */ STR_PRODUCT_MOUSE, /* u8 iProduct; */ - STR_SERIALNUMBER, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ + STR_SERIALNUMBER, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ }; static const uint8_t qemu_mouse_config_descriptor[] = { /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; Bit 7: must be set, 6: Self-powered, 5: Remote wakeup, 4..0: resvd */ - 50, /* u8 MaxPower; */ + 50, /* u8 MaxPower; */ /* USB 1.1: * USB 2.0, single TT organization (mandatory): @@ -259,37 +225,37 @@ namespace usb_hid 0x05, /* u8 if_iInterface; */ /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 52, 0, /* u16 len */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 52, 0, /* u16 len */ /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ 0x04, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ }; - [[maybe_unused]]static const uint8_t qemu_tablet_config_descriptor[] = { + [[maybe_unused]] static const uint8_t qemu_tablet_config_descriptor[] = { /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; Bit 7: must be set, 6: Self-powered, 5: Remote wakeup, 4..0: resvd */ - 50, /* u8 MaxPower; */ + 50, /* u8 MaxPower; */ /* USB 1.1: * USB 2.0, single TT organization (mandatory): @@ -314,21 +280,21 @@ namespace usb_hid 0x05, /* u8 if_iInterface; */ /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 74, 0, /* u16 len */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 74, 0, /* u16 len */ /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ }; static const uint8_t qemu_mouse_hid_report_descriptor[] = { @@ -357,48 +323,48 @@ namespace usb_hid 0x75, 0x08, /* Report Size (8) */ 0x95, 0x03, /* Report Count (3) */ 0x81, 0x06, /* Input (Data, Variable, Relative) */ - 0xc0, /* End Collection */ - 0xc0, /* End Collection */ + 0xc0, /* End Collection */ + 0xc0, /* End Collection */ }; static const uint8_t qemu_tablet_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x02, /* Usage (Mouse) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x00, /* Collection (Physical) */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ - 0x75, 0x01, /* Report Size (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ - 0x81, 0x01, /* Input (Constant) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x30, /* Usage (X) */ - 0x09, 0x31, /* Usage (Y) */ - 0x15, 0x00, /* Logical Minimum (0) */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x02, /* Usage (Mouse) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x09, 0x01, /* Usage (Pointer) */ + 0xa1, 0x00, /* Collection (Physical) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (1) */ + 0x29, 0x03, /* Usage Maximum (3) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x95, 0x03, /* Report Count (3) */ + 0x75, 0x01, /* Report Size (1) */ + 0x81, 0x02, /* Input (Data, Variable, Absolute) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x05, /* Report Size (5) */ + 0x81, 0x01, /* Input (Constant) */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x31, /* Usage (Y) */ + 0x15, 0x00, /* Logical Minimum (0) */ 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */ - 0x35, 0x00, /* Physical Minimum (0) */ + 0x35, 0x00, /* Physical Minimum (0) */ 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */ - 0x75, 0x10, /* Report Size (16) */ - 0x95, 0x02, /* Report Count (2) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x38, /* Usage (Wheel) */ - 0x15, 0x81, /* Logical Minimum (-0x7f) */ - 0x25, 0x7f, /* Logical Maximum (0x7f) */ - 0x35, 0x00, /* Physical Minimum (same as logical) */ - 0x45, 0x00, /* Physical Maximum (same as logical) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x01, /* Report Count (1) */ - 0x81, 0x06, /* Input (Data, Variable, Relative) */ - 0xc0, /* End Collection */ - 0xc0, /* End Collection */ + 0x75, 0x10, /* Report Size (16) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x02, /* Input (Data, Variable, Absolute) */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x38, /* Usage (Wheel) */ + 0x15, 0x81, /* Logical Minimum (-0x7f) */ + 0x25, 0x7f, /* Logical Maximum (0x7f) */ + 0x35, 0x00, /* Physical Minimum (same as logical) */ + 0x45, 0x00, /* Physical Maximum (same as logical) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x01, /* Report Count (1) */ + 0x81, 0x06, /* Input (Data, Variable, Relative) */ + 0xc0, /* End Collection */ + 0xc0, /* End Collection */ }; static const uint8_t qemu_keyboard_hid_report_descriptor[] = { @@ -433,12 +399,12 @@ namespace usb_hid 0x19, 0x00, /* Usage Minimum (0) */ 0x29, 0xff, /* Usage Maximum (255) */ 0x81, 0x00, /* Input (Data, Array) */ - 0xc0, /* End Collection */ + 0xc0, /* End Collection */ }; static const uint8_t beatmania_dev_desc[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ WBVAL(0x110), /* u16 bcdUSB; v1.10 */ 0x00, /* u8 bDeviceClass; */ @@ -452,21 +418,21 @@ namespace usb_hid WBVAL(0x0002), WBVAL(0x0020), /* u16 bcdDevice */ - 1, /* u8 iManufacturer; */ - 2, /* u8 iProduct; */ - 0, /* u8 iSerialNumber; */ + 1, /* u8 iManufacturer; */ + 2, /* u8 iProduct; */ + 0, /* u8 iSerialNumber; */ 0x01 /* u8 bNumConfigurations; */ }; static const uint8_t beatmania_config_desc[] = { - 0x09, // bLength - 0x02, // bDescriptorType (Configuration) + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) 0x22, 0x00, // wTotalLength 34 - 0x01, // bNumInterfaces 1 - 0x01, // bConfigurationValue - 0x02, // iConfiguration (String Index) - 0xA0, // bmAttributes Remote Wakeup - 0x14, // bMaxPower 40mA + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x02, // iConfiguration (String Index) + 0xA0, // bmAttributes Remote Wakeup + 0x14, // bMaxPower 40mA 0x09, // bLength 0x04, // bDescriptorType (Interface) @@ -478,82 +444,232 @@ namespace usb_hid 0x01, // bInterfaceProtocol 0x00, // iInterface (String Index) - 0x09, // bLength - 0x21, // bDescriptorType (HID) + 0x09, // bLength + 0x21, // bDescriptorType (HID) 0x10, 0x01, // bcdHID 1.10 - 0x0F, // bCountryCode - 0x01, // bNumDescriptors - 0x22, // bDescriptorType[0] (HID) + 0x0F, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) 0x44, 0x00, // wDescriptorLength[0] 68 - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x81, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x81, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) 0x08, 0x00, // wMaxPacketSize 8 - 0x0A, // bInterval 10 (unit depends on device speed) + 0x0A, // bInterval 10 (unit depends on device speed) // 34 bytes }; static const uint8_t beatmania_dadada_hid_report_descriptor[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x06, // Usage (Keyboard) - 0xA1, 0x01, // Collection (Application) - 0x05, 0x07, // Usage Page (Kbrd/Keypad) - 0x19, 0xE0, // Usage Minimum (0xE0) - 0x29, 0xE7, // Usage Maximum (0xE7) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x08, // Report Count (8) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x08, // Report Size (8) - 0x95, 0x01, // Report Count (1) - 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x07, // Usage Page (Kbrd/Keypad) - 0x19, 0x00, // Usage Minimum (0x00) - 0x29, 0xFF, // Usage Maximum (0xFF) - 0x15, 0x00, // Logical Minimum (0) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0xE0, // Usage Minimum (0xE0) + 0x29, 0xE7, // Usage Maximum (0xE7) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0x00, // Usage Minimum (0x00) + 0x29, 0xFF, // Usage Maximum (0xFF) + 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x06, // Report Count (6) - 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x08, // Usage Page (LEDs) - 0x19, 0x01, // Usage Minimum (Num Lock) - 0x29, 0x05, // Usage Maximum (Kana) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x05, // Report Count (5) - 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x75, 0x03, // Report Size (3) - 0x95, 0x01, // Report Count (1) - 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0xC0, // End Collection + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (Num Lock) + 0x29, 0x05, // Usage Maximum (Kana) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x05, // Report Count (5) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x75, 0x03, // Report Size (3) + 0x95, 0x01, // Report Count (1) + 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection // 68 bytes }; + static constexpr const std::pair s_qkeycode_names[] = { + {Q_KEY_CODE_0, "0"}, + {Q_KEY_CODE_1, "1"}, + {Q_KEY_CODE_2, "2"}, + {Q_KEY_CODE_3, "3"}, + {Q_KEY_CODE_4, "4"}, + {Q_KEY_CODE_5, "5"}, + {Q_KEY_CODE_6, "6"}, + {Q_KEY_CODE_7, "7"}, + {Q_KEY_CODE_8, "8"}, + {Q_KEY_CODE_9, "9"}, + {Q_KEY_CODE_A, "A"}, + {Q_KEY_CODE_AC_BACK, "ACBack"}, + {Q_KEY_CODE_AC_BOOKMARKS, "ACBookmarks"}, + {Q_KEY_CODE_AC_FORWARD, "ACForward"}, + {Q_KEY_CODE_AC_HOME, "ACHome"}, + {Q_KEY_CODE_AC_REFRESH, "ACRefresh"}, + {Q_KEY_CODE_AGAIN, "Again"}, + {Q_KEY_CODE_ALT, "Alt"}, + {Q_KEY_CODE_ALT_R, "Alt_r"}, + {Q_KEY_CODE_APOSTROPHE, "Apostrophe"}, + {Q_KEY_CODE_ASTERISK, "Asterisk"}, + {Q_KEY_CODE_AUDIOMUTE, "AudioMute"}, + {Q_KEY_CODE_AUDIONEXT, "AudioNext"}, + {Q_KEY_CODE_AUDIOPLAY, "AudioPlay"}, + {Q_KEY_CODE_AUDIOPREV, "AudioPrev"}, + {Q_KEY_CODE_AUDIOSTOP, "AudioStop"}, + {Q_KEY_CODE_B, "B"}, + {Q_KEY_CODE_BACKSLASH, "Backslash"}, + {Q_KEY_CODE_BACKSPACE, "Backspace"}, + {Q_KEY_CODE_BRACKET_LEFT, "BracketLeft"}, + {Q_KEY_CODE_BRACKET_RIGHT, "BracketRight"}, + {Q_KEY_CODE_C, "C"}, + {Q_KEY_CODE_CALCULATOR, "Calculator"}, + {Q_KEY_CODE_CAPS_LOCK, "Caps_lock"}, + {Q_KEY_CODE_COMMA, "Comma"}, + {Q_KEY_CODE_COMPOSE, "Compose"}, + {Q_KEY_CODE_COMPUTER, "Computer"}, + {Q_KEY_CODE_COPY, "Copy"}, + {Q_KEY_CODE_CTRL, "Ctrl"}, + {Q_KEY_CODE_CTRL_R, "Ctrl_r"}, + {Q_KEY_CODE_CUT, "Cut"}, + {Q_KEY_CODE_D, "D"}, + {Q_KEY_CODE_DELETE, "Delete"}, + {Q_KEY_CODE_DOT, "Dot"}, + {Q_KEY_CODE_DOWN, "Down"}, + {Q_KEY_CODE_E, "E"}, + {Q_KEY_CODE_END, "End"}, + {Q_KEY_CODE_EQUAL, "Equal"}, + {Q_KEY_CODE_ESC, "Esc"}, + {Q_KEY_CODE_F, "F"}, + {Q_KEY_CODE_F1, "F1"}, + {Q_KEY_CODE_F10, "F10"}, + {Q_KEY_CODE_F11, "F11"}, + {Q_KEY_CODE_F12, "F12"}, + {Q_KEY_CODE_F2, "F2"}, + {Q_KEY_CODE_F3, "F3"}, + {Q_KEY_CODE_F4, "F4"}, + {Q_KEY_CODE_F5, "F5"}, + {Q_KEY_CODE_F6, "F6"}, + {Q_KEY_CODE_F7, "F7"}, + {Q_KEY_CODE_F8, "F8"}, + {Q_KEY_CODE_F9, "F9"}, + {Q_KEY_CODE_FIND, "Find"}, + {Q_KEY_CODE_FRONT, "Front"}, + {Q_KEY_CODE_G, "G"}, + {Q_KEY_CODE_GRAVE_ACCENT, "Agrave"}, + {Q_KEY_CODE_H, "H"}, + {Q_KEY_CODE_HELP, "Help"}, + {Q_KEY_CODE_HENKAN, "Henkan"}, + {Q_KEY_CODE_HIRAGANA, "Hiragana"}, + {Q_KEY_CODE_HOME, "Home"}, + {Q_KEY_CODE_I, "I"}, + {Q_KEY_CODE_INSERT, "Insert"}, + {Q_KEY_CODE_J, "J"}, + {Q_KEY_CODE_K, "K"}, + {Q_KEY_CODE_KATAKANAHIRAGANA, "KatakanaHiragana"}, + {Q_KEY_CODE_KP_0, "Numpad0"}, + {Q_KEY_CODE_KP_1, "Numpad1"}, + {Q_KEY_CODE_KP_2, "Numpad2"}, + {Q_KEY_CODE_KP_3, "Numpad3"}, + {Q_KEY_CODE_KP_4, "Numpad4"}, + {Q_KEY_CODE_KP_5, "Numpad5"}, + {Q_KEY_CODE_KP_6, "Numpad6"}, + {Q_KEY_CODE_KP_7, "Numpad7"}, + {Q_KEY_CODE_KP_8, "Numpad8"}, + {Q_KEY_CODE_KP_9, "Numpad9"}, + {Q_KEY_CODE_KP_ADD, "NumpadPlus"}, + {Q_KEY_CODE_KP_COMMA, "NumpadComma"}, + {Q_KEY_CODE_KP_DECIMAL, "NumpadPeriod"}, + {Q_KEY_CODE_KP_DIVIDE, "NumpadSlash"}, + {Q_KEY_CODE_KP_ENTER, "NumpadReturn"}, + {Q_KEY_CODE_KP_EQUALS, "NumpadEqual"}, + {Q_KEY_CODE_KP_MULTIPLY, "NumpadAsterisk"}, + {Q_KEY_CODE_KP_SUBTRACT, "NumpadMinus"}, + {Q_KEY_CODE_L, "L"}, + {Q_KEY_CODE_LEFT, "Left"}, + {Q_KEY_CODE_LESS, "Less"}, + {Q_KEY_CODE_LF, "Lf"}, + {Q_KEY_CODE_M, "M"}, + {Q_KEY_CODE_MAIL, "Mail"}, + {Q_KEY_CODE_MEDIASELECT, "MediaSelect"}, + {Q_KEY_CODE_MENU, "Menu"}, + {Q_KEY_CODE_META_L, "Meta"}, + {Q_KEY_CODE_META_R, "Meta"}, + {Q_KEY_CODE_MINUS, "Minus"}, + {Q_KEY_CODE_MUHENKAN, "Muhenkan"}, + {Q_KEY_CODE_N, "N"}, + {Q_KEY_CODE_NUM_LOCK, "Num_lock"}, + {Q_KEY_CODE_O, "O"}, + {Q_KEY_CODE_OPEN, "Open"}, + {Q_KEY_CODE_P, "P"}, + {Q_KEY_CODE_PASTE, "Paste"}, + {Q_KEY_CODE_PAUSE, "Pause"}, + {Q_KEY_CODE_PGDN, "PageDown"}, + {Q_KEY_CODE_PGUP, "PageUp"}, + {Q_KEY_CODE_POWER, "Power"}, + {Q_KEY_CODE_PRINT, "Print"}, + {Q_KEY_CODE_PROPS, "Props"}, + {Q_KEY_CODE_Q, "Q"}, + {Q_KEY_CODE_R, "R"}, + {Q_KEY_CODE_RET, "Return"}, + {Q_KEY_CODE_RIGHT, "Right"}, + {Q_KEY_CODE_RO, "Ro"}, + {Q_KEY_CODE_S, "S"}, + {Q_KEY_CODE_SCROLL_LOCK, "Scroll_lock"}, + {Q_KEY_CODE_SEMICOLON, "Semicolon"}, + {Q_KEY_CODE_SHIFT, "Shift"}, + {Q_KEY_CODE_SHIFT_R, "Shift_r"}, + {Q_KEY_CODE_SLASH, "Slash"}, + {Q_KEY_CODE_SLEEP, "Sleep"}, + {Q_KEY_CODE_SPC, "Spc"}, + {Q_KEY_CODE_STOP, "Stop"}, + {Q_KEY_CODE_SYSRQ, "Sysrq"}, + {Q_KEY_CODE_T, "T"}, + {Q_KEY_CODE_TAB, "Tab"}, + {Q_KEY_CODE_U, "U"}, + {Q_KEY_CODE_UNDO, "Undo"}, + {Q_KEY_CODE_UP, "Up"}, + {Q_KEY_CODE_V, "V"}, + {Q_KEY_CODE_VOLUMEDOWN, "VolumeDown"}, + {Q_KEY_CODE_VOLUMEUP, "VolumeUp"}, + {Q_KEY_CODE_W, "W"}, + {Q_KEY_CODE_WAKE, "Wake"}, + {Q_KEY_CODE_X, "X"}, + {Q_KEY_CODE_Y, "Y"}, + {Q_KEY_CODE_YEN, "Yen"}, + {Q_KEY_CODE_Z, "Z"}, + }; + static void usb_hid_changed(HIDState* hs) { - UsbHIDState* us = CONTAINER_OF(hs, UsbHIDState, f.hid); + UsbHIDState* us = USB_CONTAINER_OF(hs, UsbHIDState, hid); usb_wakeup(us->intr, 0); } static void usb_hid_handle_reset(USBDevice* dev) { - UsbHIDState* us = reinterpret_cast(dev); + UsbHIDState* us = USB_CONTAINER_OF(dev, UsbHIDState, dev); - hid_reset(&us->f.hid); + hid_reset(&us->hid); } static void usb_hid_handle_control(USBDevice* dev, USBPacket* p, - int request, int value, int index, int length, uint8_t* data) + int request, int value, int index, int length, uint8_t* data) { - UsbHIDState* us = reinterpret_cast(dev); - HIDState* hs = &us->f.hid; + UsbHIDState* us = USB_CONTAINER_OF(dev, UsbHIDState, dev); + HIDState* hs = &us->hid; int ret; DevCon.WriteLn("usb-hid: req %04X val: %04X idx: %04X len: %d\n", request, value, index, length); @@ -573,13 +689,13 @@ namespace usb_hid if (hs->kind == HID_MOUSE) { memcpy(data, qemu_mouse_hid_report_descriptor, - sizeof(qemu_mouse_hid_report_descriptor)); + sizeof(qemu_mouse_hid_report_descriptor)); p->actual_length = sizeof(qemu_mouse_hid_report_descriptor); } else if (hs->kind == HID_TABLET) { memcpy(data, qemu_tablet_hid_report_descriptor, - sizeof(qemu_tablet_hid_report_descriptor)); + sizeof(qemu_tablet_hid_report_descriptor)); p->actual_length = sizeof(qemu_tablet_hid_report_descriptor); } else if (hs->kind == HID_KEYBOARD) @@ -657,8 +773,8 @@ namespace usb_hid static void usb_hid_handle_data(USBDevice* dev, USBPacket* p) { - UsbHIDState* us = reinterpret_cast(dev); - HIDState* hs = &us->f.hid; + UsbHIDState* us = USB_CONTAINER_OF(dev, UsbHIDState, dev); + HIDState* hs = &us->hid; std::vector buf(p->iov.size); size_t len = 0; @@ -702,54 +818,79 @@ namespace usb_hid static void usb_hid_unrealize(USBDevice* dev) { - UsbHIDState* us = reinterpret_cast(dev); - - hid_free(&us->f.hid); - + UsbHIDState* us = USB_CONTAINER_OF(dev, UsbHIDState, dev); + hid_free(&us->hid); delete us; } - int usb_hid_open(USBDevice* dev) + UsbHIDState::UsbHIDState(u32 port_) + : port(port_) { - UsbHIDState* s = (UsbHIDState*)dev; - if (s) - return s->usbhid->Open(); - return 0; } - void usb_hid_close(USBDevice* dev) + // NOTE: This is really cruddy, reusing qemu's stuff here, when we could just do + // it ourselves. But this code isn't used often enough to make it worthwhile. + + void UsbHIDState::QueueMouseButtonState(InputButton button, bool pressed) { - UsbHIDState* s = (UsbHIDState*)dev; - if (s) - s->usbhid->Close(); + InputEvent evt; + evt.type = INPUT_EVENT_KIND_BTN; + evt.u.btn.button = button; + evt.u.btn.down = pressed; + hid.ptr.eh_entry(&hid, &evt); + hid.ptr.eh_sync(&hid); } - USBDevice* HIDKbdDevice::CreateDevice(int port) + void UsbHIDState::QueueMouseAxisState(InputPointerAxis axis, float delta) { - UsbHIDState* s; - - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - UsbHIDProxyBase* proxy = RegisterUsbHID::instance().Proxy(varApi); - if (!proxy) + if (axis < InputPointerAxis::WheelX) { - Console.WriteLn("Invalid HID API: %s \n", varApi.c_str()); - return nullptr; + // x/y + InputEvent evt; + evt.type = INPUT_EVENT_KIND_REL; + evt.u.rel.axis = static_cast(axis); + evt.u.rel.value = static_cast(delta); + hid.ptr.eh_entry(&hid, &evt); + hid.ptr.eh_sync(&hid); } + else if (axis == InputPointerAxis::WheelY) + { + InputEvent evt; + evt.type = INPUT_EVENT_KIND_BTN; + evt.u.btn.button = (delta > 0.0f) ? INPUT_BUTTON_WHEEL_DOWN : INPUT_BUTTON_WHEEL_UP; + evt.u.btn.down = true; + hid.ptr.eh_entry(&hid, &evt); + hid.ptr.eh_sync(&hid); + } + } - UsbHID* usbhid = proxy->CreateObject(port, TypeName()); + void UsbHIDState::SetKeycodeMapping() + { + for (const auto& [keycode, name] : s_qkeycode_names) + { + std::optional hkeycode(InputManager::ConvertHostKeyboardStringToCode(name)); + if (!hkeycode.has_value()) + { + DevCon.WriteLn("(UsbHIDState): Missing host mapping for QKey '%s'", name); + continue; + } - if (!usbhid) - return nullptr; + keycode_mapping.emplace(hkeycode.value(), keycode); + } + } - s = new UsbHIDState(); + void UsbHIDState::QueueKeyboardState(KeyValue keycode, bool pressed) + { + InputEvent evt; + evt.type = INPUT_EVENT_KIND_KEY; + evt.u.key.key = keycode; + evt.u.key.down = pressed; + hid.kbd.eh_entry(&hid, &evt); + } + USBDevice* HIDKbdDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + UsbHIDState* s = new UsbHIDState(port); s->desc.full = &s->desc_dev; s->desc.str = desc_strings; @@ -758,96 +899,87 @@ namespace usb_hid if (usb_desc_parse_config(kbd_config_desc, sizeof(kbd_config_desc), s->desc_dev) < 0) goto fail; - s->usbhid = usbhid; s->dev.speed = USB_SPEED_FULL; s->dev.klass.handle_attach = usb_desc_attach; s->dev.klass.handle_reset = usb_hid_handle_reset; s->dev.klass.handle_control = usb_hid_handle_control; s->dev.klass.handle_data = usb_hid_handle_data; s->dev.klass.unrealize = usb_hid_unrealize; - s->dev.klass.open = usb_hid_open; - s->dev.klass.close = usb_hid_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = s->desc.str[2]; - s->port = port; usb_desc_init(&s->dev); usb_ep_init(&s->dev); s->intr = usb_ep_get(&s->dev, USB_TOKEN_IN, 1); - hid_init(&s->f.hid, HID_KEYBOARD, usb_hid_changed); - s->usbhid->SetHIDState(&s->f.hid); - s->usbhid->SetHIDType(HIDTYPE_KBD); + hid_init(&s->hid, HID_KEYBOARD, usb_hid_changed); - usb_hid_handle_reset((USBDevice*)s); + usb_hid_handle_reset(&s->dev); - return (USBDevice*)s; + s->SetKeycodeMapping(); + + return &s->dev; fail: - usb_hid_unrealize((USBDevice*)s); + usb_hid_unrealize(&s->dev); return nullptr; } - int HIDKbdDevice::Configure(int port, const std::string& api, void* data) + void HIDKbdDevice::SetBindingValue(USBDevice* dev, u32 bind, float value) const { - auto proxy = RegisterUsbHID::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), HIDTYPE_KBD, data); - return RESULT_CANCELED; + UsbHIDState* s = USB_CONTAINER_OF(dev, UsbHIDState, dev); + + const auto it = s->keycode_mapping.find(bind); + if (it == s->keycode_mapping.end()) + return; + + KeyValue kv; + kv.type = KEY_VALUE_KIND_QCODE; + kv.u.qcode = it->second; + s->QueueKeyboardState(kv, (value >= 0.5f)); } - int HIDKbdDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* HIDKbdDevice::Name() const { - auto s = reinterpret_cast(dev); - auto freezed = reinterpret_cast(data); - - if (!s) - return 0; - switch (mode) - { - case FreezeAction::Load: - if (!s) - return -1; - s->f = *freezed; - hid_init(&s->f.hid, HID_KEYBOARD, usb_hid_changed); - - return sizeof(UsbHIDState::freeze); - case FreezeAction::Save: - if (!s) - return -1; - *freezed = s->f; - return sizeof(UsbHIDState::freeze); - case FreezeAction::Size: - return sizeof(UsbHIDState::freeze); - default: - break; - } - return 0; + return "HID Keyboard"; } - USBDevice* HIDMouseDevice::CreateDevice(int port) + const char* HIDKbdDevice::TypeName() const { - UsbHIDState* s; + return "hidkbd"; + } - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - UsbHIDProxyBase* proxy = RegisterUsbHID::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("Invalid HID API: %s\n", varApi.c_str()); - return nullptr; - } + gsl::span HIDKbdDevice::Bindings(u32 subtype) const + { + static constexpr const InputBindingInfo info[] = { + {"Keyboard", "Keyboard", InputBindingInfo::Type::Keyboard, 0, GenericInputBinding::Unknown}, + }; + return info; + } - UsbHID* usbhid = proxy->CreateObject(port, TypeName()); + bool HIDKbdDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + UsbHIDState* s = USB_CONTAINER_OF(dev, UsbHIDState, dev); - if (!usbhid) - return nullptr; + if (!sw.DoMarker("HIDKbdDevice")) + return false; - s = new UsbHIDState(); + sw.DoPODArray(s->hid.kbd.keycodes, std::size(s->hid.kbd.keycodes)); + sw.Do(&s->hid.kbd.modifiers); + sw.Do(&s->hid.kbd.leds); + sw.DoPODArray(&s->hid.kbd.key, std::size(s->hid.kbd.key)); + sw.Do(&s->hid.kbd.keys); + + sw.Do(&s->hid.head); + sw.Do(&s->hid.n); + sw.Do(&s->hid.protocol); + sw.Do(&s->hid.idle); + sw.Do(&s->hid.idle_pending); + + return !sw.HasError(); + } + + USBDevice* HIDMouseDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + UsbHIDState* s = new UsbHIDState(port); s->desc.full = &s->desc_dev; s->desc.str = desc_strings; @@ -857,99 +989,105 @@ namespace usb_hid if (usb_desc_parse_config(qemu_mouse_config_descriptor, sizeof(qemu_mouse_config_descriptor), s->desc_dev) < 0) goto fail; - s->usbhid = usbhid; s->dev.speed = USB_SPEED_FULL; s->dev.klass.handle_attach = usb_desc_attach; s->dev.klass.handle_reset = usb_hid_handle_reset; s->dev.klass.handle_control = usb_hid_handle_control; s->dev.klass.handle_data = usb_hid_handle_data; s->dev.klass.unrealize = usb_hid_unrealize; - s->dev.klass.open = usb_hid_open; - s->dev.klass.close = usb_hid_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = s->desc.str[STR_CONFIG_MOUSE]; - s->port = port; usb_desc_init(&s->dev); usb_ep_init(&s->dev); s->intr = usb_ep_get(&s->dev, USB_TOKEN_IN, 1); - hid_init(&s->f.hid, HID_MOUSE, usb_hid_changed); - s->usbhid->SetHIDState(&s->f.hid); - s->usbhid->SetHIDType(HIDTYPE_MOUSE); + hid_init(&s->hid, HID_MOUSE, usb_hid_changed); - usb_hid_handle_reset((USBDevice*)s); + usb_hid_handle_reset(&s->dev); - return (USBDevice*)s; + return &s->dev; fail: - usb_hid_unrealize((USBDevice*)s); + usb_hid_unrealize(&s->dev); return nullptr; } - int HIDMouseDevice::Configure(int port, const std::string& api, void* data) + const char* HIDMouseDevice::Name() const { - auto proxy = RegisterUsbHID::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), HIDTYPE_MOUSE, data); - return RESULT_CANCELED; + return "HID Mouse"; } - int HIDMouseDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* HIDMouseDevice::TypeName() const { - auto s = reinterpret_cast(dev); - auto freezed = reinterpret_cast(data); + return "hidmouse"; + } - if (!s) - return 0; - switch (mode) + bool HIDMouseDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + UsbHIDState* s = USB_CONTAINER_OF(dev, UsbHIDState, dev); + + if (!sw.DoMarker("HIDMouseDevice")) + return false; + + sw.DoPODArray(s->hid.ptr.queue, std::size(s->hid.ptr.queue)); + sw.Do(&s->hid.ptr.mouse_grabbed); + + sw.Do(&s->hid.head); + sw.Do(&s->hid.n); + sw.Do(&s->hid.protocol); + sw.Do(&s->hid.idle); + sw.Do(&s->hid.idle_pending); + + return !sw.HasError(); + } + + gsl::span HIDMouseDevice::Bindings(u32 subtype) const + { + static constexpr const InputBindingInfo info[] = { + {"Pointer", "Pointer", InputBindingInfo::Type::Pointer, INPUT_BUTTON__MAX, GenericInputBinding::Unknown}, + {"LeftButton", "Left Button", InputBindingInfo::Type::Button, INPUT_BUTTON_LEFT, GenericInputBinding::Unknown}, + {"RightButton", "Right Button", InputBindingInfo::Type::Button, INPUT_BUTTON_RIGHT, GenericInputBinding::Unknown}, + {"MiddleButton", "Middle Button", InputBindingInfo::Type::Button, INPUT_BUTTON_MIDDLE, GenericInputBinding::Unknown}, + }; + return info; + } + + float HIDMouseDevice::GetBindingValue(const USBDevice* dev, u32 bind) const + { + const UsbHIDState* s = USB_CONTAINER_OF(dev, const UsbHIDState, dev); + + if (bind >= INPUT_BUTTON__MAX) { - case FreezeAction::Load: - if (!s) - return -1; - s->f = *freezed; - hid_init(&s->f.hid, HID_MOUSE, usb_hid_changed); - - return sizeof(UsbHIDState::freeze); - case FreezeAction::Save: - if (!s) - return -1; - *freezed = s->f; - return sizeof(UsbHIDState::freeze); - case FreezeAction::Size: - return sizeof(UsbHIDState::freeze); - default: - break; + // axis, don't bother returning, we don't have an absolute value here anyway + return 0.0f; } - return 0; + + const int index = (s->hid.n ? s->hid.head : s->hid.head - 1); + const HIDPointerEvent* e = &s->hid.ptr.queue[index & QUEUE_MASK]; + + static const int bmap[INPUT_BUTTON__MAX] = { + /*[INPUT_BUTTON_LEFT] =*/0x01, + /*[INPUT_BUTTON_MIDDLE] =*/0x04, + /*[INPUT_BUTTON_RIGHT] =*/0x02, + 0, 0, 0, 0}; + + return ((e->buttons_state & bmap[bind]) != 0) ? 1.0f : 0.0f; + } + + void HIDMouseDevice::SetBindingValue(USBDevice* dev, u32 bind, float value) const + { + UsbHIDState* s = USB_CONTAINER_OF(dev, UsbHIDState, dev); + + if (bind < INPUT_BUTTON__MAX) + s->QueueMouseButtonState(static_cast(bind), (value >= 0.5f)); + else + s->QueueMouseAxisState(static_cast(bind - INPUT_BUTTON__MAX), value); } // ---- BeatMania Da Da Da!! ---- - USBDevice* BeatManiaDevice::CreateDevice(int port) + USBDevice* BeatManiaDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { - DevCon.WriteLn("%s\n", __func__); - UsbHIDState* s; - - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - UsbHIDProxyBase* proxy = RegisterUsbHID::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("Invalid HID API: %s\n", varApi.c_str()); - return nullptr; - } - - UsbHID* usbhid = proxy->CreateObject(port, TypeName()); - - if (!usbhid) - return nullptr; - - s = new UsbHIDState(); + UsbHIDState* s = new UsbHIDState(port); s->desc.full = &s->desc_dev; s->desc.str = beatmania_dadada_desc_strings; @@ -959,46 +1097,44 @@ namespace usb_hid if (usb_desc_parse_config(beatmania_config_desc, sizeof(beatmania_config_desc), s->desc_dev) < 0) goto fail; - s->usbhid = usbhid; s->dev.speed = USB_SPEED_FULL; s->dev.klass.handle_attach = usb_desc_attach; s->dev.klass.handle_reset = usb_hid_handle_reset; s->dev.klass.handle_control = usb_hid_handle_control; s->dev.klass.handle_data = usb_hid_handle_data; s->dev.klass.unrealize = usb_hid_unrealize; - s->dev.klass.open = usb_hid_open; - s->dev.klass.close = usb_hid_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = s->desc.str[2]; - s->port = port; usb_desc_init(&s->dev); usb_ep_init(&s->dev); s->intr = usb_ep_get(&s->dev, USB_TOKEN_IN, 1); - hid_init(&s->f.hid, HID_KEYBOARD, usb_hid_changed); - s->f.hid.sub_kind = HID_SUBKIND_BEATMANIA; - s->usbhid->SetHIDState(&s->f.hid); - s->usbhid->SetHIDType(HIDTYPE_KBD); + hid_init(&s->hid, HID_KEYBOARD, usb_hid_changed); + s->hid.sub_kind = HID_SUBKIND_BEATMANIA; - usb_hid_handle_reset((USBDevice*)s); + usb_hid_handle_reset(&s->dev); - return (USBDevice*)s; + s->SetKeycodeMapping(); + + return &s->dev; fail: - usb_hid_unrealize((USBDevice*)s); + usb_hid_unrealize(&s->dev); return nullptr; } - int BeatManiaDevice::Configure(int port, const std::string& api, void* data) + const char* BeatManiaDevice::Name() const { - auto proxy = RegisterUsbHID::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), HIDTYPE_KBD, data); - return RESULT_CANCELED; + return "BeatMania Da Da Da!! Keyboard"; } - int BeatManiaDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* BeatManiaDevice::TypeName() const { - return HIDKbdDevice::Freeze(mode, dev, data); + return "beatmania"; + } + + bool BeatManiaDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + return HIDKbdDevice::Freeze(dev, sw); } } // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/usb-hid.h b/pcsx2/USB/usb-hid/usb-hid.h index 9b905c69b2..627056fe00 100644 --- a/pcsx2/USB/usb-hid/usb-hid.h +++ b/pcsx2/USB/usb-hid/usb-hid.h @@ -15,114 +15,42 @@ #pragma once #include "SaveState.h" -#include "USB/configuration.h" #include "USB/qemu-usb/hid.h" #include #include namespace usb_hid { - - enum HIDType - { - HIDTYPE_KBD, - HIDTYPE_MOUSE, - }; - - class UsbHID + class HIDKbdDevice : public DeviceProxy { public: - UsbHID(int port, const char* dev_type) - : mPort(port) - , mDevType(dev_type) - { - } - virtual ~UsbHID() {} - virtual int Open() = 0; - virtual int Close() = 0; - // virtual int TokenIn(uint8_t *buf, int len) = 0; - virtual int TokenOut(const uint8_t* data, int len) = 0; - virtual int Reset() = 0; - - virtual int Port() { return mPort; } - virtual void Port(int port) { mPort = port; } - virtual void SetHIDState(HIDState* hs) { mHIDState = hs; } - virtual void SetHIDType(HIDType t) { mHIDType = t; } - - protected: - int mPort; - HIDState* mHIDState; - HIDType mHIDType; - const char* mDevType; + const char* Name() const override; + const char* TypeName() const override; + gsl::span Bindings(u32 subtype) const override; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + void SetBindingValue(USBDevice* dev, u32 bind, float value) const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; }; - class HIDKbdDevice + class HIDMouseDevice final : public DeviceProxy { public: - virtual ~HIDKbdDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("HID Keyboard"); - } - static const char* TypeName() - { - return "hidkbd"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + const char* Name() const override; + const char* TypeName() const override; + gsl::span Bindings(u32 subtype) const override; + float GetBindingValue(const USBDevice* dev, u32 bind) const override; + void SetBindingValue(USBDevice* dev, u32 bind, float value) const override; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; }; - class HIDMouseDevice + class BeatManiaDevice final : public HIDKbdDevice { public: - virtual ~HIDMouseDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("HID Mouse"); - } - static const char* TypeName() - { - return "hidmouse"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } - }; - - class BeatManiaDevice - { - public: - virtual ~BeatManiaDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("BeatMania Da Da Da!! Keyboard"); - } - static const char* TypeName() - { - return "beatmania"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + const char* Name() const override; + const char* TypeName() const override; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; }; } // namespace usb_hid diff --git a/pcsx2/USB/usb-lightgun/guncon2.cpp b/pcsx2/USB/usb-lightgun/guncon2.cpp new file mode 100644 index 0000000000..a4c269f75c --- /dev/null +++ b/pcsx2/USB/usb-lightgun/guncon2.cpp @@ -0,0 +1,557 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "USB/deviceproxy.h" +#include "USB/qemu-usb/desc.h" +#include "USB/usb-lightgun/guncon2.h" +#include "USB/qemu-usb/USBinternal.h" +#include "USB/USB.h" +#include "GS/GS.h" +#include "HostDisplay.h" +#include "StateWrapper.h" +#include "VMManager.h" + +#include "Frontend/InputManager.h" + +#include + +namespace usb_lightgun +{ + enum : u32 + { + GUNCON2_FLAG_PROGRESSIVE = 0x0100, + + GUNCON2_CALIBRATION_DELAY = 9 + }; + + enum : u32 + { + BID_C = 1, + BID_B = 2, + BID_A = 3, + BID_DPAD_UP = 4, + BID_DPAD_RIGHT = 5, + BID_DPAD_DOWN = 6, + BID_DPAD_LEFT = 7, + BID_TRIGGER = 13, + BID_SELECT = 14, + BID_START = 15, + BID_SHOOT_OFFSCREEN = 16, + BID_RECALIBRATE = 17, + }; + + // Right pain in the arse. Different games seem to have different scales.. + // Not worth putting these in the gamedb for such few games. + // Values are from the old nuvee plugin. + struct GameConfig + { + const char* serial; + float scale_x, scale_y; + u32 center_x, center_y; + u32 screen_width, screen_height; + }; + + static constexpr const GameConfig s_game_config[] = { + {"SLUS-20485", 90.25, 92.5, 390, 132, 640, 240}, // Dino Stalker (U) + {"SLUS-20389", 89.25, 93.5, 422, 141, 640, 240}, // Endgame (U) + {"SLES-52620", 90.5, 114.75, 390, 146, 640, 256}, // Guncom 2 (E) + {"SLES-51289", 84.5, 89.0, 456, 164, 640, 256}, // Gunfighter 2 - Jesse James (E) + {"SLPS-25165", 90.25, 98.0, 390, 138, 640, 240}, // Gunvari Collection (J) (480i) + // {"SLPS-25165", 86.75, 96.0, 454, 164, 640, 256}, // Gunvari Collection (J) (480p) + {"SCES-50889", 90.25, 94.5, 390, 169, 640, 256}, // Ninja Assault (E) + {"SLUS-20492", 90.25, 92.5, 390, 132, 640, 240}, // Ninja Assault (U) + {"SLES-50650", 84.75, 96.0, 454, 164, 640, 240}, // Resident Evil Survivor 2 (E) + {"SLES-51448", 90.25, 93.5, 420, 132, 640, 240}, // Resident Evil - Dead Aim (U) + {"SLUS-20619", 90.25, 91.75, 453, 154, 640, 256}, // Starsky & Hutch (U) + {"SLUS-20219", 90.25, 97.5, 390, 154, 640, 240}, // Time Crisis 2 (U) + {"SLUS-20645", 90.25, 97.5, 390, 154, 640, 240}, // Time Crisis 3 (U) + {"SLUS-20927", 90.25, 99.0, 390, 153, 640, 240}, // Time Crisis - Crisis Zone (U) (480i) + // {"SLUS-20927", 94.5, 104.75, 423, 407, 768, 768}, // Time Crisis - Crisis Zone (U) (480p) + {"SLUS-20221", 97.5, 104.75, 423, 407, 768, 768}, // Vampire Night (U) + {"SLES-51229", 88.75, 100.0, 454, 164, 640, 256}, // Virtua Cop - Elite Edition (E,J) (480i) + // {"SLES-51229", 85.75, 92.0, 456, 164, 640, 256}, // Virtua Cop - Elite Edition (E,J) (480p) + }; + + static constexpr s32 DEFAULT_SCREEN_WIDTH = 640; + static constexpr s32 DEFAULT_SCREEN_HEIGHT = 480; + static constexpr float DEFAULT_CENTER_X = 320.0f; + static constexpr float DEFAULT_CENTER_Y = 120.0f; + static constexpr float DEFAULT_SCALE_X = 100.0f; + static constexpr float DEFAULT_SCALE_Y = 100.0f; + +#pragma pack(push, 1) + union GunCon2Out + { + u8 bits[6]; + + struct + { + u16 buttons; + s16 pos_x; + s16 pos_y; + }; + }; + static_assert(sizeof(GunCon2Out) == 6); +#pragma pack(pop) + + struct GunCon2State + { + explicit GunCon2State(u32 port_); + + USBDevice dev{}; + USBDesc desc{}; + USBDescDevice desc_dev{}; + + u32 port = 0; + + ////////////////////////////////////////////////////////////////////////// + // Configuration + ////////////////////////////////////////////////////////////////////////// + bool custom_config = false; + u32 screen_width = 640; + u32 screen_height = 240; + float center_x = 320; + float center_y = 120; + float scale_x = 1.0f; + float scale_y = 1.0f; + + ////////////////////////////////////////////////////////////////////////// + // Host State (Not Saved) + ////////////////////////////////////////////////////////////////////////// + u32 button_state = 0; + + ////////////////////////////////////////////////////////////////////////// + // Device State (Saved) + ////////////////////////////////////////////////////////////////////////// + s16 param_x = 0; + s16 param_y = 0; + u16 param_mode = 0; + + u16 calibration_timer = 0; + s16 calibration_pos_x = 0; + s16 calibration_pos_y = 0; + + bool auto_config_done = false; + + void AutoConfigure(); + + std::tuple CalculatePosition(); + }; + + static const USBDescStrings desc_strings = { + "Namco GunCon2", + }; + + /* mostly the same values as the Bochs USB Keyboard device */ + static const uint8_t guncon2_dev_desc[] = { + /* bLength */ 0x12, + /* bDescriptorType */ 0x01, + /* bcdUSB */ WBVAL(0x0100), + /* bDeviceClass */ 0x00, + /* bDeviceSubClass */ 0x00, + /* bDeviceProtocol */ 0x00, + /* bMaxPacketSize0 */ 0x08, + /* idVendor */ WBVAL(0x0b9a), + /* idProduct */ WBVAL(0x016a), + /* bcdDevice */ WBVAL(0x0100), + /* iManufacturer */ 0x00, + /* iProduct */ 0x00, + /* iSerialNumber */ 0x00, + /* bNumConfigurations */ 0x01, + }; + + static const uint8_t guncon2_config_desc[] = { + 0x09, // Length + 0x02, // Type (Config) + 0x19, 0x00, // Total size + + 0x01, // # interfaces + 0x01, // Configuration # + 0x00, // index of string descriptor + 0x80, // Attributes (bus powered) + 0x19, // Max power in mA + + + // Interface + 0x09, // Length + 0x04, // Type (Interface) + + 0x00, // Interface # + 0x00, // Alternative # + 0x01, // # endpoints + + 0xff, // Class + 0x6a, // Subclass + 0x00, // Protocol + 0x00, // index of string descriptor + + + // Endpoint + 0x07, // Length + 0x05, // Type (Endpoint) + + 0x81, // Address + 0x03, // Attributes (interrupt transfers) + 0x08, 0x00, // Max packet size + + 0x08, // Polling interval (frame counts) + }; + + static void guncon2_handle_control( + USBDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) + { + GunCon2State* const us = USB_CONTAINER_OF(dev, GunCon2State, dev); + + // Apply configuration on the first control packet. + // The ELF should be well and truely loaded by then. + if (!us->auto_config_done && !us->custom_config) + { + us->AutoConfigure(); + us->auto_config_done = true; + } + + DevCon.WriteLn("guncon2: req %04X val: %04X idx: %04X len: %d\n", request, value, index, length); + if (usb_desc_handle_control(dev, p, request, value, index, length, data) >= 0) + return; + + if (request == (ClassInterfaceOutRequest | 0x09)) + { + us->param_x = static_cast(data[0]) | (static_cast(data[1]) << 8); + us->param_y = static_cast(data[2]) | (static_cast(data[3]) << 8); + us->param_mode = static_cast(data[4]) | (static_cast(data[5]) << 8); + DevCon.WriteLn("GunCon2 Set Param %04X %d %d", us->param_mode, us->param_x, us->param_y); + return; + } + + p->status = USB_RET_STALL; + } + + static void guncon2_handle_data(USBDevice* dev, USBPacket* p) + { + GunCon2State* const us = USB_CONTAINER_OF(dev, GunCon2State, dev); + + switch (p->pid) + { + case USB_TOKEN_IN: + { + if (p->ep->nr == 1) + { + const auto [pos_x, pos_y] = us->CalculatePosition(); + + // Time Crisis games do a "calibration" by displaying a black frame for a single frame, + // waiting for the gun to report (0, 0), and then computing an offset on the first non-zero + // value. So, after the trigger is pulled, we wait for a few frames, then send the (0, 0) + // report, then go back to normal values. To reduce error if the mouse is moving during + // these frames (unlikely), we store the fire position and keep returning that. + if (us->button_state & (1u << BID_RECALIBRATE) && us->calibration_timer == 0) + { + us->calibration_timer = GUNCON2_CALIBRATION_DELAY; + us->calibration_pos_x = pos_x; + us->calibration_pos_y = pos_y; + } + + // Buttons are active low. + GunCon2Out out; + out.buttons = static_cast(~us->button_state) | (us->param_mode & GUNCON2_FLAG_PROGRESSIVE); + out.pos_x = pos_x; + out.pos_y = pos_y; + + if (us->calibration_timer > 0) + { + // Force trigger down while calibrating. + out.buttons &= ~(1u << BID_TRIGGER); + out.pos_x = us->calibration_pos_x; + out.pos_y = us->calibration_pos_y; + us->calibration_timer--; + + if (us->calibration_timer == 0) + { + out.pos_x = 0; + out.pos_y = 0; + } + } + else if (us->button_state & (1u << BID_SHOOT_OFFSCREEN)) + { + // Offscreen shot - use 0,0. + out.buttons &= ~(1u << BID_TRIGGER); + out.pos_x = 0; + out.pos_y = 0; + } + + usb_packet_copy(p, &out, sizeof(out)); + break; + } + } + [[fallthrough]]; + + case USB_TOKEN_OUT: + default: + { + Console.Error("Unhandled GunCon2 request pid=%d ep=%u", p->pid, p->ep->nr); + p->status = USB_RET_STALL; + } + break; + } + } + + static void usb_hid_unrealize(USBDevice* dev) + { + GunCon2State* us = USB_CONTAINER_OF(dev, GunCon2State, dev); + delete us; + } + + GunCon2State::GunCon2State(u32 port_) + : port(port_) + { + } + + void GunCon2State::AutoConfigure() + { + const std::string serial(VMManager::GetGameSerial()); + for (const GameConfig& gc : s_game_config) + { + if (serial != gc.serial) + continue; + + Console.WriteLn(fmt::format("(GunCon2) Using automatic config for '{}'", serial)); + Console.WriteLn(fmt::format(" Scale: {}x{}", gc.scale_x / 100.0f, gc.scale_y / 100.0f)); + Console.WriteLn(fmt::format(" Center Position: {}x{}", gc.center_x, gc.center_y)); + Console.WriteLn(fmt::format(" Screen Size: {}x{}", gc.screen_width, gc.screen_height)); + + scale_x = gc.scale_x / 100.0f; + scale_y = gc.scale_y / 100.0f; + center_x = static_cast(gc.center_x); + center_y = static_cast(gc.center_y); + screen_width = gc.screen_width; + screen_height = gc.screen_height; + return; + } + + Console.Warning(fmt::format("(GunCon2) No automatic config found for '{}'.", serial)); + } + + std::tuple GunCon2State::CalculatePosition() + { + float pointer_x, pointer_y; + const std::pair abs_pos(InputManager::GetPointerAbsolutePosition(0)); + GSTranslateWindowToDisplayCoordinates(abs_pos.first, abs_pos.second, &pointer_x, &pointer_y); + + s16 pos_x, pos_y; + if (pointer_x < 0.0f || pointer_y < 0.0f || (button_state & BID_SHOOT_OFFSCREEN)) + { + // off-screen + pos_x = 0; + pos_y = 0; + } + else + { + // scale to internal coordinate system and center + float fx = (pointer_x * static_cast(screen_width)) - static_cast(screen_width / 2u); + float fy = (pointer_y * static_cast(screen_height)) - static_cast(screen_height / 2u); + + // apply curvature scale + fx *= scale_x; + fy *= scale_y; + + // and re-center based on game center + s32 x = static_cast(std::round(fx + center_x)); + s32 y = static_cast(std::round(fy + center_y)); + + // apply game-configured offset + if (param_mode & GUNCON2_FLAG_PROGRESSIVE) + { + x -= param_x / 2; + y -= param_y / 2; + } + else + { + x -= param_x; + y -= param_y; + } + + // 0,0 is reserved for offscreen, so ensure we don't send that + pos_x = static_cast(std::max(x, 1)); + pos_y = static_cast(std::max(y, 1)); + } + + return std::tie(pos_x, pos_y); + } + + const char* GunCon2Device::Name() const + { + return "GunCon 2"; + } + + const char* GunCon2Device::TypeName() const + { + return "guncon2"; + } + + USBDevice* GunCon2Device::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + GunCon2State* s = new GunCon2State(port); + s->desc.full = &s->desc_dev; + s->desc.str = desc_strings; + + if (usb_desc_parse_dev(guncon2_dev_desc, sizeof(guncon2_dev_desc), s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(guncon2_config_desc, sizeof(guncon2_config_desc), s->desc_dev) < 0) + goto fail; + + s->dev.speed = USB_SPEED_FULL; + s->dev.klass.handle_attach = usb_desc_attach; + s->dev.klass.handle_control = guncon2_handle_control; + s->dev.klass.handle_data = guncon2_handle_data; + s->dev.klass.unrealize = usb_hid_unrealize; + s->dev.klass.usb_desc = &s->desc; + s->dev.klass.product_desc = s->desc.str[2]; + + usb_desc_init(&s->dev); + usb_ep_init(&s->dev); + + return &s->dev; + fail: + usb_hid_unrealize(&s->dev); + return nullptr; + } + + void GunCon2Device::UpdateSettings(USBDevice* dev, SettingsInterface& si) const + { + GunCon2State* s = USB_CONTAINER_OF(dev, GunCon2State, dev); + + s->custom_config = USB::GetConfigBool(si, s->port, TypeName(), "custom_config", false); + + // Don't override auto config if we've set it. + if (!s->auto_config_done || s->custom_config) + { + s->screen_width = USB::GetConfigInt(si, s->port, TypeName(), "screen_width", DEFAULT_SCREEN_WIDTH); + s->screen_height = USB::GetConfigInt(si, s->port, TypeName(), "screen_height", DEFAULT_SCREEN_HEIGHT); + s->center_x = USB::GetConfigFloat(si, s->port, TypeName(), "center_x", DEFAULT_CENTER_X); + s->center_y = USB::GetConfigFloat(si, s->port, TypeName(), "center_y", DEFAULT_CENTER_Y); + s->scale_x = USB::GetConfigFloat(si, s->port, TypeName(), "scale_x", DEFAULT_SCALE_X) / 100.0f; + s->scale_y = USB::GetConfigFloat(si, s->port, TypeName(), "scale_y", DEFAULT_SCALE_Y) / 100.0f; + } + } + + float GunCon2Device::GetBindingValue(const USBDevice* dev, u32 bind_index) const + { + GunCon2State* s = USB_CONTAINER_OF(dev, GunCon2State, dev); + + const u32 bit = 1u << bind_index; + return ((s->button_state & bit) != 0) ? 1.0f : 0.0f; + } + + void GunCon2Device::SetBindingValue(USBDevice* dev, u32 bind_index, float value) const + { + GunCon2State* s = USB_CONTAINER_OF(dev, GunCon2State, dev); + + const u32 bit = 1u << bind_index; + if (value >= 0.5f) + s->button_state |= bit; + else + s->button_state &= ~bit; + } + + gsl::span GunCon2Device::Bindings(u32 subtype) const + { + static constexpr const InputBindingInfo bindings[] = { + //{"pointer", "Pointer/Aiming", InputBindingInfo::Type::Pointer, BID_POINTER_X, GenericInputBinding::Unknown}, + {"Up", "D-Pad Up", InputBindingInfo::Type::Button, BID_DPAD_UP, GenericInputBinding::DPadUp}, + {"Down", "D-Pad Down", InputBindingInfo::Type::Button, BID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"Left", "D-Pad Left", InputBindingInfo::Type::Button, BID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"Right", "D-Pad Right", InputBindingInfo::Type::Button, BID_DPAD_RIGHT, + GenericInputBinding::DPadRight}, + {"Trigger", "Trigger", InputBindingInfo::Type::Button, BID_TRIGGER, GenericInputBinding::R2}, + {"ShootOffscreen", "Shoot Offscreen", InputBindingInfo::Type::Button, BID_SHOOT_OFFSCREEN, + GenericInputBinding::R1}, + {"Recalibrate", "Calibration Shot", InputBindingInfo::Type::Button, BID_RECALIBRATE, + GenericInputBinding::Unknown}, + {"A", "A", InputBindingInfo::Type::Button, BID_A, GenericInputBinding::Cross}, + {"B", "B", InputBindingInfo::Type::Button, BID_B, GenericInputBinding::Circle}, + {"C", "C", InputBindingInfo::Type::Button, BID_C, GenericInputBinding::Triangle}, + {"Select", "Select", InputBindingInfo::Type::Button, BID_SELECT, GenericInputBinding::Select}, + {"Start", "Start", InputBindingInfo::Type::Button, BID_START, GenericInputBinding::Start}, + }; + + return bindings; + } + + gsl::span GunCon2Device::Settings(u32 subtype) const + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::Boolean, "custom_config", "Manual Screen Configuration", + "Forces the use of the screen parameters below, instead of automatic parameters if available.", + "false"}, + {SettingInfo::Type::Float, "scale_x", "X Scale (Sensitivity)", + "Scales the position to simulate CRT curvature.", "100", "0", "100", "0.1", "%.2f%%", nullptr, nullptr, + 1.0f}, + {SettingInfo::Type::Float, "scale_y", "Y Scale (Sensitivity)", + "Scales the position to simulate CRT curvature.", "100", "0", "100", "0.1", "%.2f%%", nullptr, nullptr, + 1.0f}, + {SettingInfo::Type::Float, "center_x", "Center X", "Sets the center position of the simulated screen.", + "320", "0", "1024", "1", "%.0fpx", nullptr, nullptr, 1.0f}, + {SettingInfo::Type::Float, "center_y", "Center Y", "Sets the center position of the simulated screen.", + "120", "0", "1024", "1", "%.0fpx", nullptr, nullptr, 1.0f}, + {SettingInfo::Type::Integer, "screen_width", "Screen Width", "Sets the width of the simulated screen.", + "640", "1", "1024", "1", "%dpx", nullptr, nullptr, 1.0f}, + {SettingInfo::Type::Integer, "screen_height", "Screen Height", "Sets the height of the simulated screen.", + "240", "1", "1024", "1", "%dpx", nullptr, nullptr, 1.0f}, + }; + return info; + } + + bool GunCon2Device::Freeze(USBDevice* dev, StateWrapper& sw) const + { + GunCon2State* s = USB_CONTAINER_OF(dev, GunCon2State, dev); + + if (!sw.DoMarker("GunCon2Device")) + return false; + + sw.Do(&s->param_x); + sw.Do(&s->param_y); + sw.Do(&s->param_mode); + sw.Do(&s->calibration_timer); + sw.Do(&s->calibration_pos_x); + sw.Do(&s->calibration_pos_y); + sw.Do(&s->auto_config_done); + + float scale_x = s->scale_x; + float scale_y = s->scale_y; + float center_x = s->center_x; + float center_y = s->center_y; + u32 screen_width = s->screen_width; + u32 screen_height = s->screen_height; + sw.Do(&scale_x); + sw.Do(&scale_y); + sw.Do(¢er_x); + sw.Do(¢er_y); + sw.Do(&screen_width); + sw.Do(&screen_height); + + // Only save automatic settings to state. + if (sw.IsReading() && !s->custom_config && s->auto_config_done) + { + s->scale_x = scale_x; + s->scale_y = scale_y; + s->center_x = center_x; + s->center_y = center_y; + s->screen_width = screen_width; + s->screen_height = screen_height; + } + + return !sw.HasError(); + } +} // namespace usb_lightgun diff --git a/pcsx2/USB/usb-lightgun/guncon2.h b/pcsx2/USB/usb-lightgun/guncon2.h new file mode 100644 index 0000000000..7300680504 --- /dev/null +++ b/pcsx2/USB/usb-lightgun/guncon2.h @@ -0,0 +1,34 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once +#include "USB/deviceproxy.h" + +namespace usb_lightgun +{ + class GunCon2Device final : public DeviceProxy + { + public: + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* Name() const override; + const char* TypeName() const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override; + float GetBindingValue(const USBDevice* dev, u32 bind_index) const override; + void SetBindingValue(USBDevice* dev, u32 bind_index, float value) const override; + gsl::span Bindings(u32 subtype) const override; + gsl::span Settings(u32 subtype) const override; + }; +} // namespace usb_lightgun diff --git a/pcsx2/USB/usb-mic/api_init_linux.cpp b/pcsx2/USB/usb-mic/api_init_linux.cpp deleted file mode 100644 index 9c39b4ebdc..0000000000 --- a/pcsx2/USB/usb-mic/api_init_linux.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "audiodeviceproxy.h" -#include "audiodev-noop.h" -#ifdef SPU2X_PULSEAUDIO -#include "audiodev-pulse.h" -#endif - -void usb_mic::RegisterAudioDevice::Register() -{ - auto& inst = RegisterAudioDevice::instance(); - inst.Add(audiodev_noop::APINAME, new AudioDeviceProxy()); -#ifdef SPU2X_PULSEAUDIO - inst.Add(audiodev_pulse::APINAME, new AudioDeviceProxy()); -#endif -} diff --git a/pcsx2/USB/usb-mic/api_init_win32_mic.cpp b/pcsx2/USB/usb-mic/api_init_win32_mic.cpp deleted file mode 100644 index 87dc7a0214..0000000000 --- a/pcsx2/USB/usb-mic/api_init_win32_mic.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "audiodeviceproxy.h" -#include "audiodev-noop.h" -#include "audiodev-wasapi.h" - -void usb_mic::RegisterAudioDevice::Register() -{ - auto& inst = RegisterAudioDevice::instance(); - inst.Add(audiodev_noop::APINAME, new AudioDeviceProxy()); - inst.Add(audiodev_wasapi::APINAME, new AudioDeviceProxy()); -} diff --git a/pcsx2/USB/usb-mic/audiodev-cubeb.cpp b/pcsx2/USB/usb-mic/audiodev-cubeb.cpp new file mode 100644 index 0000000000..118d542689 --- /dev/null +++ b/pcsx2/USB/usb-mic/audiodev-cubeb.cpp @@ -0,0 +1,286 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "USB/usb-mic/audiodev-cubeb.h" +#include "USB/USB.h" +#include "common/Assertions.h" +#include "common/Console.h" + +#include "cubeb/cubeb.h" +#include "fmt/format.h" + +// Since the context gets used to populate the device list, that unfortunately means +// we need locking around it, since the UI thread's gonna be saying hi. The settings +// callbacks don't actually modify the context itself, though, only look at the +// device list. +static cubeb* s_cubeb_context; +static cubeb_device_collection s_cubeb_input_devices; +static cubeb_device_collection s_cubeb_output_devices; +static u32 s_cubeb_refcount = 0; +static std::mutex s_cubeb_context_mutex; + +static cubeb* GetCubebContext(const char* backend = nullptr) +{ + std::unique_lock lock(s_cubeb_context_mutex); + + if (!s_cubeb_context) + { + pxAssert(s_cubeb_refcount == 0); + const int res = cubeb_init(&s_cubeb_context, "PCSX2_USB", backend); + if (res != CUBEB_OK) + { + Console.Error("cubeb_init() failed: %d", res); + return nullptr; + } + + cubeb_enumerate_devices(s_cubeb_context, CUBEB_DEVICE_TYPE_INPUT, &s_cubeb_input_devices); + cubeb_enumerate_devices(s_cubeb_context, CUBEB_DEVICE_TYPE_OUTPUT, &s_cubeb_output_devices); + } + + if (s_cubeb_context) + s_cubeb_refcount++; + + return s_cubeb_context; +} + +static void ReleaseCubebContext() +{ + std::unique_lock lock(s_cubeb_context_mutex); + + pxAssert(s_cubeb_refcount > 0); + if ((--s_cubeb_refcount) == 0) + { + cubeb_device_collection_destroy(s_cubeb_context, &s_cubeb_input_devices); + s_cubeb_input_devices = {}; + cubeb_device_collection_destroy(s_cubeb_context, &s_cubeb_output_devices); + s_cubeb_output_devices = {}; + + cubeb_destroy(s_cubeb_context); + s_cubeb_context = nullptr; + } +} + +static cubeb_devid FindCubebDevice(const char* devname, bool input) +{ + if (std::strcmp(devname, "cubeb_default") == 0) + return nullptr; + + const cubeb_device_collection& col = input ? s_cubeb_input_devices : s_cubeb_output_devices; + for (size_t i = 0; i < col.count; i++) + { + if (std::strcmp(devname, col.device[i].device_id) == 0) + return col.device[i].devid; + } + + Console.Warning("(audiodev_cubeb) Unable to find %s device %s", input ? "input" : "output", devname); + return nullptr; +} + +static void CubebStateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state) +{ +} + +namespace usb_mic +{ + namespace audiodev_cubeb + { + CubebAudioDevice::CubebAudioDevice(u32 port, AudioDir dir, u32 channels, std::string devname, s32 latency) + : AudioDevice(port, dir, channels) + , mLatency(latency) + , mDeviceName(std::move(devname)) + { + mContext = GetCubebContext(); + mDeviceId = FindCubebDevice(mDeviceName.c_str(), (dir == AUDIODIR_SOURCE)); + } + + CubebAudioDevice::~CubebAudioDevice() + { + if (mStream) + CubebAudioDevice::Stop(); + + if (mContext) + ReleaseCubebContext(); + } + + std::vector> CubebAudioDevice::GetDeviceList(bool input) + { + std::vector> ret; + ret.emplace_back("", "Not Connected"); + ret.emplace_back("cubeb_default", input ? "Default Input Device" : "Default Output Device"); + if (GetCubebContext()) + { + const cubeb_device_collection& col = input ? s_cubeb_input_devices : s_cubeb_output_devices; + for (size_t i = 0; i < col.count; i++) + ret.emplace_back(col.device[i].device_id, col.device[i].friendly_name); + + ReleaseCubebContext(); + } + return ret; + } + + bool CubebAudioDevice::Start() + { + if (mStream) + Stop(); + + if (!mDeviceName.empty() && mDeviceName != "cubeb_default" && !mDeviceId) + { + Console.Error("(audiodev_cubeb) Device '%s' is not available.", mDeviceName.c_str()); + return false; + } + + cubeb_stream_params params; + params.format = CUBEB_SAMPLE_S16LE; + params.rate = mSampleRate; + params.channels = mChannels; + params.layout = CUBEB_LAYOUT_UNDEFINED; + params.prefs = CUBEB_STREAM_PREF_NONE; + + // Prefer minimum latency, reduces the chance of dropped samples due to the extra buffer. + u32 streamLatency; + if (cubeb_get_min_latency(mContext, ¶ms, &streamLatency) != CUBEB_OK) + streamLatency = mLatency; + + const bool input = (mAudioDir == AUDIODIR_SOURCE); + int res = cubeb_stream_init(mContext, &mStream, fmt::format("{}", (void*)this).c_str(), + input ? mDeviceId : nullptr, input ? ¶ms : nullptr, input ? nullptr : mDeviceId, + input ? nullptr : ¶ms, (streamLatency * mSampleRate) / 1000u, + &CubebAudioDevice::DataCallback, &CubebStateCallback, this); + if (res != CUBEB_OK) + { + Console.Error("(audiodev_cubeb) cubeb_stream_init() failed: %d", res); + return false; + } + + res = cubeb_stream_start(mStream); + if (res != CUBEB_OK) + { + Console.Error("(audiodev_cubeb) cubeb_stream_start() failed: %d", res); + cubeb_stream_destroy(mStream); + mStream = nullptr; + return false; + } + + ResetBuffers(); + return true; + } + + void CubebAudioDevice::Stop() + { + if (!mStream) + return; + + int res = cubeb_stream_stop(mStream); + if (res != CUBEB_OK) + Console.Error("cubeb_stream_stop() returned %d", res); + + cubeb_stream_destroy(mStream); + mStream = nullptr; + } + + uint32_t CubebAudioDevice::GetBuffer(short* buff, uint32_t frames) + { + if (!mStream) + return frames; + + std::lock_guard lk(mMutex); + u32 samples_to_read = frames * GetChannels(); + short* pDst = (short*)buff; + pxAssert(samples_to_read <= mBuffer.size()); + + while (samples_to_read > 0) + { + u32 samples = std::min(samples_to_read, static_cast(mBuffer.peek_read())); + if (!samples) + break; + memcpy(pDst, mBuffer.front(), samples * sizeof(short)); + mBuffer.read(samples); + pDst += samples; + samples_to_read -= samples; + } + return (frames - (samples_to_read / GetChannels())); + } + + uint32_t CubebAudioDevice::SetBuffer(short* buff, uint32_t frames) + { + if (!mStream) + return frames; + + std::lock_guard lk(mMutex); + size_t nbytes = frames * sizeof(short) * GetChannels(); + mBuffer.write((uint8_t*)buff, nbytes); + + return frames; + } + + bool CubebAudioDevice::GetFrames(uint32_t* size) + { + if (!mStream) + return true; + + std::lock_guard lk(mMutex); + *size = mBuffer.size() / GetChannels(); + return true; + } + + void CubebAudioDevice::SetResampling(int samplerate) + { + const bool was_started = (mStream != nullptr); + + Stop(); + + mSampleRate = samplerate; + + if (was_started) + Start(); + + ResetBuffers(); + } + + bool CubebAudioDevice::Compare(AudioDevice* compare) const + { + if (compare) + { + CubebAudioDevice* src = static_cast(compare); + if (src && mDeviceName == src->mDeviceName) + return true; + } + return false; + } + + void CubebAudioDevice::ResetBuffers() + { + // TODO: Do we want to make the buffer size adjustable? Currently 100ms max. + std::lock_guard lk(mMutex); + const u32 samples = ((mSampleRate * mChannels) * mLatency) / 1000u; + mBuffer.reserve(sizeof(u16) * samples); + } + + long CubebAudioDevice::DataCallback( + cubeb_stream* stream, void* user_ptr, void const* input_buffer, void* output_buffer, long nframes) + { + CubebAudioDevice* const ad = static_cast(user_ptr); + const size_t bytes = ad->mChannels * sizeof(short) * static_cast(nframes); + + std::lock_guard lk(ad->mMutex); + if (ad->mAudioDir == AUDIODIR_SOURCE) + ad->mBuffer.write((u8*)input_buffer, bytes); + else + ad->mBuffer.read((u8*)output_buffer, bytes); + + return nframes; + } + } // namespace audiodev_cubeb +} // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/audiodev-cubeb.h b/pcsx2/USB/usb-mic/audiodev-cubeb.h new file mode 100644 index 0000000000..0882ddbe5a --- /dev/null +++ b/pcsx2/USB/usb-mic/audiodev-cubeb.h @@ -0,0 +1,64 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "USB/shared/ringbuffer.h" +#include "USB/USB.h" +#include "audiodev.h" + +#include +#include +#include + +struct cubeb; +struct cubeb_stream; + +namespace usb_mic +{ + namespace audiodev_cubeb + { + class CubebAudioDevice final : public AudioDevice + { + public: + CubebAudioDevice(u32 port, AudioDir dir, u32 channels, std::string devname, s32 latency); + ~CubebAudioDevice(); + + static std::vector> GetDeviceList(bool input); + + uint32_t GetBuffer(short* buff, uint32_t frames) override; + uint32_t SetBuffer(short* buff, uint32_t frames) override; + bool GetFrames(uint32_t* size) override; + void SetResampling(int samplerate) override; + bool Start() override; + void Stop() override; + bool Compare(AudioDevice* compare) const override; + + protected: + void ResetBuffers(); + + static long DataCallback(struct cubeb_stream* stream, void* user_ptr, void const* input_buffer, + void* output_buffer, long nframes); + + u32 mSampleRate = 48000; + u32 mLatency = 50; + cubeb* mContext; + cubeb_stream* mStream = nullptr; + std::string mDeviceName; + const void* mDeviceId; + + RingBuffer mBuffer; + std::mutex mMutex; + }; + } // namespace audiodev_cubeb +} // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/audiodev-noop.h b/pcsx2/USB/usb-mic/audiodev-noop.h index 55f3687613..3e89dfbe1f 100644 --- a/pcsx2/USB/usb-mic/audiodev-noop.h +++ b/pcsx2/USB/usb-mic/audiodev-noop.h @@ -14,75 +14,37 @@ */ #pragma once -#include "audiodeviceproxy.h" +#include "audiodev.h" +#include namespace usb_mic { namespace audiodev_noop { - - static const char* APINAME = "noop"; - class NoopAudioDevice : public AudioDevice { public: - NoopAudioDevice(int port, const char* dev_type, int mic, AudioDir dir) - : AudioDevice(port, dev_type, mic, dir) + NoopAudioDevice( + u32 port, AudioDir dir, u32 channels) + : AudioDevice(port, dir, channels) { } - ~NoopAudioDevice() {} - void Start() {} - void Stop() {} - virtual bool GetFrames(uint32_t* size) + ~NoopAudioDevice() override {} + bool Start() override { return true; } - virtual uint32_t GetBuffer(int16_t* outBuf, uint32_t outFrames) + void Stop() override {} + bool GetFrames(uint32_t* size) override { return true; } + uint32_t GetBuffer(int16_t* outBuf, uint32_t outFrames) override { + std::memset(outBuf, 0, outFrames * sizeof(int16_t)); return outFrames; } - virtual uint32_t SetBuffer(int16_t* inBuf, uint32_t inFrames) - { - return inFrames; - } - virtual void SetResampling(int samplerate) {} - virtual uint32_t GetChannels() - { - return 1; - } + uint32_t SetBuffer(int16_t* inBuf, uint32_t inFrames) override { return inFrames; } + void SetResampling(int samplerate) override {} - virtual bool Compare(AudioDevice* compare) - { - return false; - } - - static const TCHAR* Name() - { - return TEXT("NOOP"); - } - - static bool AudioInit() - { - return true; - } - - static void AudioDeinit() - { - } - - static void AudioDevices(std::vector& devices, AudioDir) - { - AudioDeviceInfo info; - info.strID = TEXT("silence"); - info.strName = TEXT("Silence"); - devices.push_back(info); - } - - static int Configure(int port, const char* dev_type, void* data) - { - return RESULT_OK; - } + bool Compare(AudioDevice* compare) const override { return false; } }; - } // namespace audiodev_noop -} // namespace usb_mic +} // namespace usb_mic \ No newline at end of file diff --git a/pcsx2/USB/usb-mic/audiodev-pulse.cpp b/pcsx2/USB/usb-mic/audiodev-pulse.cpp deleted file mode 100644 index 44f9429934..0000000000 --- a/pcsx2/USB/usb-mic/audiodev-pulse.cpp +++ /dev/null @@ -1,895 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "USB/gtk.h" -#include "audiodev-pulse.h" -#ifdef DYNLINK_PULSE -#include "USB/dynlink/pulse.h" -#endif - -namespace usb_mic -{ - namespace audiodev_pulse - { - - static void pa_context_state_cb(pa_context* c, void* userdata) - { - pa_context_state_t state; - int* pa_ready = (int*)userdata; - - state = pa_context_get_state(c); - switch (state) - { - // There are just here for reference - case PA_CONTEXT_UNCONNECTED: - *pa_ready = 3; - break; - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - default: - break; - case PA_CONTEXT_FAILED: - case PA_CONTEXT_TERMINATED: - *pa_ready = 2; - break; - case PA_CONTEXT_READY: - *pa_ready = 1; - break; - } - } - - static void pa_sourcelist_cb(pa_context* c, const pa_source_info* l, int eol, void* userdata) - { - AudioDeviceInfoList* devicelist = static_cast(userdata); - - if (eol > 0) - { - return; - } - - AudioDeviceInfo dev; - dev.strID = l->name; - dev.strName = l->description; - //dev.intID = l->index; - devicelist->push_back(dev); - } - - static void pa_sinklist_cb(pa_context* c, const pa_sink_info* l, int eol, void* userdata) - { - AudioDeviceInfoList* devicelist = static_cast(userdata); - - if (eol > 0) - { - return; - } - - AudioDeviceInfo dev; - dev.strID = l->name; - dev.strName = l->description; - //dev.intID = l->index; - devicelist->push_back(dev); - } - - static int pa_get_devicelist(AudioDeviceInfoList& list, AudioDir dir) - { - pa_mainloop* pa_ml; - pa_mainloop_api* pa_mlapi; - pa_operation* pa_op; - pa_context* pa_ctx; - - int state = 0; - int pa_ready = 0; - - pa_ml = pa_mainloop_new(); - pa_mlapi = pa_mainloop_get_api(pa_ml); - pa_ctx = pa_context_new(pa_mlapi, "USB-devicelist"); - - pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); - - pa_context_set_state_callback(pa_ctx, pa_context_state_cb, &pa_ready); - - for (;;) - { - - if (pa_ready == 0) - { - pa_mainloop_iterate(pa_ml, 1, NULL); - continue; - } - - // Connection failed - if (pa_ready == 2) - { - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); - return -1; - } - - switch (state) - { - case 0: - if (dir == AUDIODIR_SOURCE) - pa_op = pa_context_get_source_info_list(pa_ctx, - pa_sourcelist_cb, - &list); - else - pa_op = pa_context_get_sink_info_list(pa_ctx, - pa_sinklist_cb, - &list); - state++; - break; - case 1: - if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) - { - pa_operation_unref(pa_op); - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); - return 0; - } - break; - default: - return -1; - } - pa_mainloop_iterate(pa_ml, 1, NULL); - } - } - - // GTK+ config. dialog stuff - static void populateDeviceWidget(GtkComboBox* widget, const std::string& devName, const AudioDeviceInfoList& devs) - { - gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(widget))); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), "None"); - gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); - - int i = 1; - for (auto& dev : devs) - { - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), dev.strName.c_str()); - if (!devName.empty() && devName == dev.strID) - gtk_combo_box_set_active(GTK_COMBO_BOX(widget), i); - i++; - } - } - - static void deviceChanged(GtkComboBox* widget, gpointer data) - { - *(int*)data = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - } - - static int GtkConfigure(int port, const char* dev_type, void* data) - { - GtkWidget* ro_frame; - - int dev_idxs[] = {0, 0, 0, 0}; - - AudioDeviceInfoList srcDevs; - if (pa_get_devicelist(srcDevs, AUDIODIR_SOURCE) != 0) - { - return RESULT_FAILED; - } - - AudioDeviceInfoList sinkDevs; - if (pa_get_devicelist(sinkDevs, AUDIODIR_SINK) != 0) - { - return RESULT_FAILED; - } - - GtkWidget* dlg = gtk_dialog_new_with_buttons( - "PulseAudio Settings", GTK_WINDOW(data), GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE); - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - - - GtkWidget* main_vbox = gtk_vbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(dlg_area_box), main_vbox, TRUE, FALSE, 5); - - ro_frame = gtk_frame_new("Audio Devices"); - gtk_box_pack_start(GTK_BOX(main_vbox), ro_frame, TRUE, FALSE, 5); - - GtkWidget* frame_vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(ro_frame), frame_vbox); - - const char* labels[] = {"Source 1", "Source 2", "Sink 1", "Sink 2"}; - for (int i = 0; i < 2; i++) - { - std::string devName; - LoadSetting(dev_type, port, APINAME, (i ? N_AUDIO_SOURCE1 : N_AUDIO_SOURCE0), devName); - - GtkWidget* cb = new_combobox(labels[i], frame_vbox); - g_signal_connect(G_OBJECT(cb), "changed", G_CALLBACK(deviceChanged), (gpointer)&dev_idxs[i]); - populateDeviceWidget(GTK_COMBO_BOX(cb), devName, srcDevs); - } - - //TODO only one for now - for (int i = 2; i < 3 /*4*/; i++) - { - std::string devName; - LoadSetting(dev_type, port, APINAME, (i - 2 ? N_AUDIO_SINK1 : N_AUDIO_SINK0), devName); - - GtkWidget* cb = new_combobox(labels[i], frame_vbox); - g_signal_connect(G_OBJECT(cb), "changed", G_CALLBACK(deviceChanged), (gpointer)&dev_idxs[i]); - populateDeviceWidget(GTK_COMBO_BOX(cb), devName, sinkDevs); - } - - ro_frame = gtk_frame_new("Buffer lengths"); - gtk_box_pack_start(GTK_BOX(main_vbox), ro_frame, TRUE, FALSE, 5); - - frame_vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(ro_frame), frame_vbox); - - const char* labels_buff[] = {"Sources", "Sinks"}; - const char* buff_var_name[] = {N_BUFFER_LEN_SRC, N_BUFFER_LEN_SINK}; - GtkWidget* scales[2]; - - GtkWidget* table = gtk_table_new(2, 2, true); - gtk_container_add(GTK_CONTAINER(frame_vbox), table); - gtk_table_set_homogeneous(GTK_TABLE(table), FALSE); - GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default - - for (int i = 0; i < 2; i++) - { - GtkWidget* label = gtk_label_new(labels_buff[i]); - gtk_table_attach(GTK_TABLE(table), label, - 0, 1, - 0 + i, 1 + i, - GTK_SHRINK, GTK_SHRINK, 5, 1); - - //scales[i] = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 1000, 1); - scales[i] = gtk_hscale_new_with_range(1, 1000, 1); - gtk_table_attach(GTK_TABLE(table), scales[i], - 1, 2, - 0 + i, 1 + i, - opt, opt, 5, 1); - - int32_t var; - if (LoadSetting(dev_type, port, APINAME, buff_var_name[i], var)) - gtk_range_set_value(GTK_RANGE(scales[i]), var); - else - gtk_range_set_value(GTK_RANGE(scales[i]), 50); - } - - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - - int scale_vals[2]; - for (int i = 0; i < 2; i++) - { - scale_vals[i] = gtk_range_get_value(GTK_RANGE(scales[i])); - } - - gtk_widget_destroy(dlg); - - // Wait for all gtk events to be consumed ... - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); - - int ret = RESULT_CANCELED; - if (result == GTK_RESPONSE_OK) - { - ret = RESULT_OK; - for (int i = 0; i < 2; i++) - { - int idx = dev_idxs[i]; - { - std::string var; - - if (idx > 0) - var = srcDevs[idx - 1].strID; - - if (!SaveSetting(dev_type, port, APINAME, (i ? N_AUDIO_SOURCE1 : N_AUDIO_SOURCE0), var)) - ret = RESULT_FAILED; - } - - idx = dev_idxs[i + 2]; - { - std::string var; - - if (idx > 0) - var = sinkDevs[idx - 1].strID; - - if (!SaveSetting(dev_type, port, APINAME, (i ? N_AUDIO_SINK1 : N_AUDIO_SINK0), var)) - ret = RESULT_FAILED; - } - - // Save buffer lengths - if (!SaveSetting(dev_type, port, APINAME, buff_var_name[i], scale_vals[i])) - ret = RESULT_FAILED; - } - } - - return ret; - } - - uint32_t PulseAudioDevice::GetBuffer(short* buff, uint32_t frames) - { - auto now = hrc::now(); - auto dur = std::chrono::duration_cast(now - mLastGetBuffer).count(); - - // init time point - if (mLastOut.time_since_epoch().count() == 0) - mLastOut = now; - - //Disconnected, try reconnect after every 1sec, hopefully game retries to read samples - if (mPAready == 3 && dur >= 1000) - { - mLastGetBuffer = now; - [[maybe_unused]] - int ret = pa_context_connect(mPContext, - mServer, - PA_CONTEXT_NOFLAGS, - NULL); - - //TODO reconnect stream as well? - - } - else - mLastGetBuffer = now; - - std::lock_guard lk(mMutex); - ssize_t samples_to_read = frames * GetChannels(); - short* pDst = (short*)buff; - assert(samples_to_read <= mOutBuffer.size()); - - while (samples_to_read > 0) - { - ssize_t samples = std::min(samples_to_read, (ssize_t)mOutBuffer.peek_read()); - if (!samples) - break; - memcpy(pDst, mOutBuffer.front(), samples * sizeof(short)); - mOutBuffer.read(samples); - pDst += samples; - samples_to_read -= samples; - } - return (frames - (samples_to_read / GetChannels())); - } - - uint32_t PulseAudioDevice::SetBuffer(short* buff, uint32_t frames) - { - auto now = hrc::now(); - auto dur = std::chrono::duration_cast(now - mLastGetBuffer).count(); - - // init time point - if (mLastOut.time_since_epoch().count() == 0) - mLastOut = now; - - //Disconnected, try reconnect after every 1sec - if (mPAready == 3 && dur >= 1000) - { - mLastGetBuffer = now; - int ret = pa_context_connect(mPContext, - mServer, - PA_CONTEXT_NOFLAGS, - NULL); - - //TODO reconnect stream as well? - - if (ret != PA_OK) - return frames; - } - else - mLastGetBuffer = now; - - std::lock_guard lk(mMutex); - size_t nbytes = frames * sizeof(short) * GetChannels(); - mInBuffer.write((uint8_t*)buff, nbytes); - - return frames; - } - - bool PulseAudioDevice::GetFrames(uint32_t* size) - { - std::lock_guard lk(mMutex); - *size = mOutBuffer.size() / GetChannels(); - return true; - } - - void PulseAudioDevice::SetResampling(int samplerate) - { - mSamplesPerSec = samplerate; - if (mAudioDir == AUDIODIR_SOURCE) - mResampleRatio = double(samplerate) / double(mSSpec.rate); - else - mResampleRatio = double(mSSpec.rate) / double(samplerate); - //mResample = true; - ResetBuffers(); - } - - void PulseAudioDevice::Start() - { - ResetBuffers(); - mPaused = false; - if (mStream) - { - pa_threaded_mainloop_lock(mPMainLoop); - if (pa_stream_is_corked(mStream) > 0) - { - pa_operation* op = pa_stream_cork(mStream, 0, stream_success_cb, this); - if (op) - pa_operation_unref(op); - } - pa_threaded_mainloop_unlock(mPMainLoop); - } - } - - void PulseAudioDevice::Stop() - { - mPaused = true; - if (mStream) - { - pa_threaded_mainloop_lock(mPMainLoop); - if (!pa_stream_is_corked(mStream)) - { - pa_operation* op = pa_stream_cork(mStream, 1, stream_success_cb, this); - if (op) - pa_operation_unref(op); - } - pa_threaded_mainloop_unlock(mPMainLoop); - } - } - - bool PulseAudioDevice::Compare(AudioDevice* compare) - { - if (compare) - { - PulseAudioDevice* src = static_cast(compare); - if (src && mDeviceName == src->mDeviceName) - return true; - } - return false; - } - - void PulseAudioDevice::Uninit() - { - if (mStream) - { - pa_threaded_mainloop_lock(mPMainLoop); - [[maybe_unused]]int ret = pa_stream_disconnect(mStream); - pa_stream_unref(mStream); - mStream = nullptr; - pa_threaded_mainloop_unlock(mPMainLoop); - } - if (mPMainLoop) - { - pa_threaded_mainloop_stop(mPMainLoop); - } - if (mPContext) - { - pa_context_disconnect(mPContext); - pa_context_unref(mPContext); - mPContext = nullptr; - } - if (mPMainLoop) - { - pa_threaded_mainloop_free(mPMainLoop); - mPMainLoop = nullptr; - } - } - - bool PulseAudioDevice::Init() - { - int ret = 0; - pa_operation* pa_op = nullptr; - - mPMainLoop = pa_threaded_mainloop_new(); - pa_mainloop_api* mlapi = pa_threaded_mainloop_get_api(mPMainLoop); - - mPContext = pa_context_new(mlapi, "USB"); - - pa_context_set_state_callback(mPContext, - context_state_cb, - this); - - // Lock the mainloop so that it does not run and crash before the context is ready - pa_threaded_mainloop_lock(mPMainLoop); - pa_threaded_mainloop_start(mPMainLoop); - - ret = pa_context_connect(mPContext, - mServer, - PA_CONTEXT_NOFLAGS, - NULL); - - if (ret != PA_OK) - goto unlock_and_fail; - - // wait for pa_context_state_cb - for (;;) - { - if (mPAready == 1) - break; - if (mPAready == 2 || mQuit) - goto unlock_and_fail; - pa_threaded_mainloop_wait(mPMainLoop); - } - - mStream = pa_stream_new(mPContext, - "USB-pulse", - &mSSpec, - NULL); - - if (!mStream) - goto unlock_and_fail; - - pa_stream_set_state_callback(mStream, stream_state_cb, this); - - // Sets individual read callback fragsize but recording itself - // still "lags" ~1sec (read_cb is called in bursts) without - // PA_STREAM_ADJUST_LATENCY - pa_buffer_attr buffer_attr; - buffer_attr.maxlength = (uint32_t)-1; - buffer_attr.tlength = (uint32_t)-1; - buffer_attr.prebuf = (uint32_t)-1; - buffer_attr.minreq = (uint32_t)-1; - buffer_attr.fragsize = pa_usec_to_bytes(mBuffering * 1000, &mSSpec); - - if (mAudioDir == AUDIODIR_SOURCE) - { - pa_stream_set_read_callback(mStream, - stream_read_cb, - this); - - ret = pa_stream_connect_record(mStream, - mDeviceName.c_str(), - &buffer_attr, - PA_STREAM_ADJUST_LATENCY); - } - else - { - pa_stream_set_write_callback(mStream, - stream_write_cb, - this); - - buffer_attr.maxlength = pa_bytes_per_second(&mSSpec); - buffer_attr.prebuf = 0; // Don't stop on underrun but then - // stream also only starts manually with uncorking. - buffer_attr.tlength = pa_usec_to_bytes(mBuffering * 1000, &mSSpec); - pa_stream_flags_t flags = (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING | - PA_STREAM_NOT_MONOTONIC | - PA_STREAM_AUTO_TIMING_UPDATE | - //PA_STREAM_VARIABLE_RATE | - PA_STREAM_ADJUST_LATENCY); - - ret = pa_stream_connect_playback(mStream, - mDeviceName.c_str(), - &buffer_attr, - flags, - nullptr, - nullptr); - } - - if (ret != PA_OK) - goto unlock_and_fail; - - // Wait for the stream to be ready - for (;;) - { - pa_stream_state_t stream_state = pa_stream_get_state(mStream); - assert(PA_STREAM_IS_GOOD(stream_state)); - if (stream_state == PA_STREAM_READY) - break; - if (stream_state == PA_STREAM_FAILED) - goto unlock_and_fail; - pa_threaded_mainloop_wait(mPMainLoop); - } - - - pa_op = pa_stream_cork(mStream, 0, stream_success_cb, this); - if (pa_op) - pa_operation_unref(pa_op); - - pa_threaded_mainloop_unlock(mPMainLoop); - - pa_op = pa_stream_update_timing_info(mStream, stream_success_cb, nullptr); - if (pa_op) - pa_operation_unref(pa_op); - //const pa_timing_info* pa_ti = pa_stream_get_timing_info(mStream); - - pa_usec_t r_usec; - int negative; - ret = pa_stream_get_latency(mStream, &r_usec, &negative); - - // Setup resampler - mResampler = src_delete(mResampler); - - mResampler = src_new(SRC_SINC_FASTEST, GetChannels(), &ret); - if (!mResampler) - { - goto error; - } - - mLastGetBuffer = hrc::now(); - return true; - unlock_and_fail: - pa_threaded_mainloop_unlock(mPMainLoop); - error: - Uninit(); - return false; - } - - void PulseAudioDevice::ResetBuffers() - { - size_t bytes; - std::lock_guard lk(mMutex); - pa_sample_spec ss(mSSpec); - ss.rate = mSamplesPerSec; - - if (mAudioDir == AUDIODIR_SOURCE) - { - bytes = pa_bytes_per_second(&mSSpec) * mBuffering / 1000; - bytes += bytes % pa_frame_size(&mSSpec); //align just in case - mInBuffer.reserve(bytes); - - bytes = pa_bytes_per_second(&ss) * mBuffering / 1000; - bytes += bytes % pa_frame_size(&ss); - mOutBuffer.reserve(bytes); - } - else - { - bytes = pa_bytes_per_second(&mSSpec) * mBuffering / 1000; - bytes += bytes % pa_frame_size(&mSSpec); - mOutBuffer.reserve(bytes); - - bytes = pa_bytes_per_second(&ss) * mBuffering / 1000; - bytes += bytes % pa_frame_size(&ss); - mInBuffer.reserve(bytes); - } - - src_reset(mResampler); - } - - int PulseAudioDevice::Configure(int port, const char* dev_type, void* data) - { - int ret = RESULT_FAILED; - if (PulseAudioDevice::AudioInit()) - { - ret = GtkConfigure(port, dev_type, data); - PulseAudioDevice::AudioDeinit(); - } - return ret; - } - - void PulseAudioDevice::AudioDevices(std::vector& devices, AudioDir& dir) - { - pa_get_devicelist(devices, dir); - } - - bool PulseAudioDevice::AudioInit() - { -#ifdef DYNLINK_PULSE - return DynLoadPulse(); -#else - return true; -#endif - } - - void PulseAudioDevice::AudioDeinit() - { -#ifdef DYNLINK_PULSE - DynUnloadPulse(); -#endif - } - - void PulseAudioDevice::context_state_cb(pa_context* c, void* userdata) - { - pa_context_state_t state; - PulseAudioDevice* padev = (PulseAudioDevice*)userdata; - - state = pa_context_get_state(c); - switch (state) - { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - default: - break; - case PA_CONTEXT_UNCONNECTED: - padev->mPAready = 3; - break; - case PA_CONTEXT_FAILED: - case PA_CONTEXT_TERMINATED: - padev->mPAready = 2; - break; - case PA_CONTEXT_READY: - padev->mPAready = 1; - break; - } - - pa_threaded_mainloop_signal(padev->mPMainLoop, 0); - } - - void PulseAudioDevice::stream_state_cb(pa_stream* s, void* userdata) - { - PulseAudioDevice* padev = (PulseAudioDevice*)userdata; - pa_threaded_mainloop_signal(padev->mPMainLoop, 0); - } - - void PulseAudioDevice::stream_read_cb(pa_stream* p, size_t nbytes, void* userdata) - { - std::vector rebuf; - SRC_DATA data; - PulseAudioDevice* padev = (PulseAudioDevice*)userdata; - const void* padata = NULL; - - if (padev->mQuit) - return; - - - int ret = pa_stream_peek(p, &padata, &nbytes); - - if (ret != PA_OK) - return; - - //auto dur = std::chrono::duration_cast(hrc::now() - padev->mLastGetBuffer).count(); - if (padev->mPaused /*|| dur > 5000*/ || (!padata && nbytes /* hole */)) - { - ret = pa_stream_drop(p); - return; - } - - std::lock_guard lock(padev->mMutex); - - padev->mInBuffer.write((uint8_t*)padata, nbytes); - - //if copy succeeded, drop samples at pulse's side - ret = pa_stream_drop(p); - - size_t resampled = static_cast(padev->mInBuffer.size() * padev->mResampleRatio * padev->mTimeAdjust); - if (resampled == 0) - resampled = padev->mInBuffer.size(); - rebuf.resize(resampled); - - size_t output_frames_gen = 0, input_frames_used = 0; - float* pBegin = rebuf.data(); - float* pEnd = pBegin + rebuf.size(); - - memset(&data, 0, sizeof(SRC_DATA)); - - while (padev->mInBuffer.peek_read() > 0) - { - data.data_in = (float*)padev->mInBuffer.front(); - data.input_frames = padev->mInBuffer.peek_read() / padev->GetChannels(); - data.data_out = pBegin; - data.output_frames = (pEnd - pBegin) / padev->GetChannels(); - data.src_ratio = padev->mResampleRatio * padev->mTimeAdjust; - - src_process(padev->mResampler, &data); - output_frames_gen += data.output_frames_gen; - pBegin += data.output_frames_gen * padev->GetChannels(); - input_frames_used += data.input_frames_used; - - size_t samples = data.input_frames_used * padev->GetChannels(); - if (!samples) - break; //TODO happens? - padev->mInBuffer.read(samples); - } - - size_t output_samples = output_frames_gen * padev->GetChannels(); - float* pSrc = rebuf.data(); - while (output_samples > 0) - { - size_t samples = std::min(output_samples, padev->mOutBuffer.peek_write(true)); - src_float_to_short_array(pSrc, padev->mOutBuffer.back(), samples); - padev->mOutBuffer.write(samples); - output_samples -= samples; - pSrc += samples; - } - } - - void PulseAudioDevice::stream_write_cb(pa_stream* p, size_t nbytes, void* userdata) - { - void* pa_buffer = NULL; - size_t pa_bytes; - // The length of the data to write in bytes, must be in multiples of the stream's sample spec frame size - ssize_t remaining_bytes = nbytes; - int ret = PA_OK; - std::vector inFloats; - SRC_DATA data; - memset(&data, 0, sizeof(SRC_DATA)); - - PulseAudioDevice* padev = (PulseAudioDevice*)userdata; - if (padev->mQuit) - return; - - { - std::lock_guard lock(padev->mMutex); - // Convert short samples to float and to final output sample rate - if (padev->mInBuffer.size() > 0) - { - inFloats.resize(padev->mInBuffer.size()); - float* pDst = inFloats.data(); - - while (padev->mInBuffer.peek_read() > 0) - { - size_t samples = padev->mInBuffer.peek_read(); - src_short_to_float_array( - (const short*)padev->mInBuffer.front(), - pDst, samples); - pDst += samples; - padev->mInBuffer.read(samples); - } - - size_t input_frames_used = 0; - size_t in_offset = 0; - while (padev->mOutBuffer.peek_write() > 0) - { - data.data_in = inFloats.data() + in_offset; - data.input_frames = (inFloats.size() - in_offset) / padev->GetChannels(); - data.data_out = padev->mOutBuffer.back(); - data.output_frames = padev->mOutBuffer.peek_write() / padev->GetChannels(); - data.src_ratio = padev->mResampleRatio * padev->mTimeAdjust; - - src_process(padev->mResampler, &data); - - input_frames_used += data.input_frames_used; - in_offset = input_frames_used * padev->GetChannels(); - - padev->mOutBuffer.write(data.output_frames_gen * padev->GetChannels()); - - if (inFloats.size() <= in_offset || data.output_frames_gen == 0) - break; - } - } - } - - // Write converted float samples or silence to PulseAudio stream - while (remaining_bytes > 0) - { - pa_bytes = remaining_bytes; - - ret = pa_stream_begin_write(padev->mStream, &pa_buffer, &pa_bytes); - if (ret != PA_OK) - { - goto exit; - } - - ssize_t final_bytes = 0; - // read twice because possible wrap - while (padev->mOutBuffer.size() > 0) - { - ssize_t read = std::min((ssize_t)pa_bytes - final_bytes, (ssize_t)padev->mOutBuffer.peek_read()); - if (read <= 0) - break; - - memcpy((uint8_t*)pa_buffer + final_bytes, padev->mOutBuffer.front(), read); - final_bytes += read; - padev->mOutBuffer.read(read); - } - - if ((ssize_t)pa_bytes > final_bytes) - memset((uint8_t*)pa_buffer + final_bytes, 0, pa_bytes - final_bytes); - - ret = pa_stream_write(padev->mStream, pa_buffer, pa_bytes, NULL, 0LL, PA_SEEK_RELATIVE); - if (ret != PA_OK) - { - pa_stream_cancel_write(padev->mStream); //TODO needed? - goto exit; - } - - remaining_bytes -= pa_bytes; - } - - exit: - - return; - } - - } // namespace audiodev_pulse -} // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/audiodev-pulse.h b/pcsx2/USB/usb-mic/audiodev-pulse.h deleted file mode 100644 index b77796ee81..0000000000 --- a/pcsx2/USB/usb-mic/audiodev-pulse.h +++ /dev/null @@ -1,168 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include -#include -#include -#include "USB/shared/ringbuffer.h" -#include "audiodeviceproxy.h" -#include -//#include -//#include -#include -#include - -namespace usb_mic -{ - namespace audiodev_pulse - { - -// macros for string concat -#undef APINAME_ -#define APINAME_ "pulse" - - static const char* APINAME = "pulse"; - - using hrc = std::chrono::high_resolution_clock; - using ms = std::chrono::milliseconds; - using us = std::chrono::microseconds; - using ns = std::chrono::nanoseconds; - using sec = std::chrono::seconds; - - class PulseAudioDevice : public AudioDevice - { - public: - PulseAudioDevice(int port, const char* dev_type, int device, AudioDir dir) - : AudioDevice(port, dev_type, device, dir) - , mBuffering(50) - , mPaused(true) - , mQuit(false) - , mPMainLoop(nullptr) - , mPContext(nullptr) - , mStream(nullptr) - , mServer(nullptr) - , mPAready(0) - , mResampleRatio(1.0) - , mTimeAdjust(1.0) - , mSamplesPerSec(48000) - , mResampler(nullptr) - , mOutSamples(0) - { - int i = dir == AUDIODIR_SOURCE ? 0 : 2; - const char* var_names[] = { - N_AUDIO_SOURCE0, - N_AUDIO_SOURCE1, - N_AUDIO_SINK0, - N_AUDIO_SINK1}; - - if (!LoadSetting(mDevType, mPort, APINAME, (device ? var_names[i + 1] : var_names[i]), mDeviceName) || mDeviceName.empty()) - throw AudioDeviceError(APINAME_ ": failed to load device settings"); - - LoadSetting(mDevType, mPort, APINAME, (dir == AUDIODIR_SOURCE ? N_BUFFER_LEN_SRC : N_BUFFER_LEN_SINK), mBuffering); - mBuffering = MIN(1000, MAX(1, mBuffering)); - - if (!AudioInit()) - throw AudioDeviceError(APINAME_ ": failed to bind pulseaudio library"); - - mSSpec.format = PA_SAMPLE_FLOAT32LE; //PA_SAMPLE_S16LE; - mSSpec.channels = 2; - mSSpec.rate = 48000; - - if (!Init()) - throw AudioDeviceError(APINAME_ ": failed to init"); - } - - ~PulseAudioDevice() - { - mQuit = true; - std::lock_guard lock(mMutex); - Uninit(); - AudioDeinit(); - mResampler = src_delete(mResampler); - if (file) - fclose(file); - } - - uint32_t GetBuffer(short* buff, uint32_t frames); - uint32_t SetBuffer(short* buff, uint32_t frames); - bool GetFrames(uint32_t* size); - void SetResampling(int samplerate); - void Start(); - void Stop(); - virtual bool Compare(AudioDevice* compare); - void Uninit(); - bool Init(); - void ResetBuffers(); - - inline uint32_t GetChannels() - { - return mSSpec.channels; - } - - static const char* TypeName() - { - return APINAME; - } - - static const TCHAR* Name() - { - return "PulseAudio"; - } - - static int Configure(int port, const char* dev_type, void* data); - - static void AudioDevices(std::vector& devices, AudioDir& dir); - - static bool AudioInit(); - static void AudioDeinit(); - - static void context_state_cb(pa_context* c, void* userdata); - static void stream_state_cb(pa_stream* s, void* userdata); - static void stream_read_cb(pa_stream* p, size_t nbytes, void* userdata); - static void stream_write_cb(pa_stream* p, size_t nbytes, void* userdata); - static void stream_success_cb(pa_stream* p, int success, void* userdata) {} - - protected: - int mChannels; - int mBuffering; - std::string mDeviceName; - pa_sample_spec mSSpec; - - RingBuffer mOutBuffer; - RingBuffer mInBuffer; - //std::thread mThread; - //std::condition_variable mEvent; - std::mutex mMutex; - bool mPaused; - bool mQuit; - hrc::time_point mLastGetBuffer; - - pa_threaded_mainloop* mPMainLoop; - pa_context* mPContext; - pa_stream* mStream; - char* mServer; //TODO add server selector? - int mPAready; - double mResampleRatio; - // Speed up or slow down audio - double mTimeAdjust; - int mSamplesPerSec; - SRC_STATE* mResampler; - - int mOutSamples; - hrc::time_point mLastOut; - FILE* file = nullptr; - }; - } // namespace audiodev_pulse -} // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/audiodev-wasapi.cpp b/pcsx2/USB/usb-mic/audiodev-wasapi.cpp deleted file mode 100644 index 158e8cb776..0000000000 --- a/pcsx2/USB/usb-mic/audiodev-wasapi.cpp +++ /dev/null @@ -1,1019 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -// Used OBS as example - -#include "PrecompiledHeader.h" -#include -#include -#include -#include -#include -#include "audiodev-wasapi.h" -#include "USB/Win32/Config_usb.h" -#include "USB/Win32/resource_usb.h" - -#define SafeRelease(x) \ - if (x) \ - { \ - x->Release(); \ - x = NULL; \ - } -#define ConvertMSTo100NanoSec(ms) (ms * 1000 * 10) //1000 microseconds, then 10 "100nanosecond" segments - -namespace usb_mic -{ - namespace audiodev_wasapi - { - - static FILE* file = nullptr; - - //Config dlg temporaries - struct WASAPISettings - { - int port; - const char* dev_type; - AudioDeviceInfoList sourceDevs; - AudioDeviceInfoList sinkDevs; - std::wstring selectedDev[3]; - }; - - static BOOL CALLBACK WASAPIDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); - - LARGE_INTEGER clockFreq = {0}; - __declspec(thread) LONGLONG lastQPCTime = 0; - - LONGLONG GetQPCTimeMS() - { - LARGE_INTEGER currentTime; - QueryPerformanceCounter(¤tTime); - - if (currentTime.QuadPart < lastQPCTime) - - lastQPCTime = currentTime.QuadPart; - - LONGLONG timeVal = currentTime.QuadPart; - timeVal *= 1000; - timeVal /= clockFreq.QuadPart; - - return timeVal; - } - - LONGLONG GetQPCTime100NS() - { - LARGE_INTEGER currentTime; - QueryPerformanceCounter(¤tTime); - - lastQPCTime = currentTime.QuadPart; - - double timeVal = double(currentTime.QuadPart); - timeVal *= 10000000.0; - timeVal /= double(clockFreq.QuadPart); - - return LONGLONG(timeVal); - } - - MMAudioDevice::~MMAudioDevice() - { - mQuit = true; - if (mThread != INVALID_HANDLE_VALUE) - { - if (WaitForSingleObject(mThread, 30000) != WAIT_OBJECT_0) - TerminateThread(mThread, 0); - } - - FreeData(); - SafeRelease(mmEnumerator); - mResampler = src_delete(mResampler); - if (file) - fclose(file); - file = nullptr; - - CloseHandle(mThread); - mThread = INVALID_HANDLE_VALUE; - CloseHandle(mMutex); - mMutex = INVALID_HANDLE_VALUE; - } - - void MMAudioDevice::FreeData() - { - SafeRelease(mmCapture); - SafeRelease(mmRender); - SafeRelease(mmClient); - SafeRelease(mmDevice); - SafeRelease(mmClock); - //clear mBuffer - } - - bool MMAudioDevice::Init() - { - const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); - const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); - - if (mAudioDir == AUDIODIR_SOURCE) - { - if (!LoadSetting(mDevType, mPort, APINAME, (mDevice ? N_AUDIO_SOURCE1 : N_AUDIO_SOURCE0), mDevID)) - { - throw AudioDeviceError("MMAudioDevice:: failed to load source from ini!"); - } - } - else - { - if (!LoadSetting(mDevType, mPort, APINAME, (mDevice ? N_AUDIO_SINK1 : N_AUDIO_SINK0), mDevID)) - { - throw AudioDeviceError("MMAudioDevice:: failed to load sink from ini!"); - } - } - - if (!mDevID.length()) - return false; - - { - int var; - if (LoadSetting(mDevType, mPort, APINAME, (mAudioDir == AUDIODIR_SOURCE ? N_BUFFER_LEN_SRC : N_BUFFER_LEN_SINK), var)) - mBuffering = std::min(1000, std::max(1, var)); - } - - - HRESULT err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); - if (FAILED(err)) - { - Console.WriteLn("MMAudioDevice::Init(): Could not create IMMDeviceEnumerator = %08lX\n", err); - return false; - } - //TODO Not starting thread here unnecesserily - //mThread = CreateThread(NULL, 0, MMAudioDevice::CaptureThread, this, 0, 0); - return true; - } - - bool MMAudioDevice::Reinitialize() - { - const IID IID_IAudioClient = __uuidof(IAudioClient); - const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); - const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); - HRESULT err; - - if (!mDeviceLost && mmClock) - return true; - else - { - if (GetQPCTimeMS() - mLastTimeMS < 1000) - return false; - mLastTimeMS = GetQPCTimeMS(); - } - - err = mmEnumerator->GetDevice(mDevID.c_str(), &mmDevice); - - if (FAILED(err)) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Could not create IMMDevice = %08lX\n", err); - return false; - } - - err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); - if (FAILED(err)) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Could not create IAudioClient = %08lX\n", err); - return false; - } - - // get name - - /*IPropertyStore *store; - if(SUCCEEDED(mmDevice->OpenPropertyStore(STGM_READ, &store))) - { - PROPVARIANT varName; - - PropVariantInit(&varName); - if(SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &varName))) - { - const WCHAR* wstrName = varName.pwszVal; - mDeviceName = wstrName; - } - - store->Release(); - }*/ - - // get format - - WAVEFORMATEX* pwfx; - err = mmClient->GetMixFormat(&pwfx); - if (FAILED(err)) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Could not get mix format from audio client = %08lX\n", err); - return false; - } - - WAVEFORMATEXTENSIBLE* wfext = NULL; - - if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - { - wfext = (WAVEFORMATEXTENSIBLE*)pwfx; - mInputChannelMask = wfext->dwChannelMask; - - if (wfext->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Unsupported wave format\n"); - CoTaskMemFree(pwfx); - return false; - } - } - else if (pwfx->wFormatTag != WAVE_FORMAT_IEEE_FLOAT) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Unsupported wave format\n"); - CoTaskMemFree(pwfx); - return false; - } - - mFloat = true; - mDeviceChannels = pwfx->nChannels; - mDeviceBitsPerSample = 32; - mDeviceBlockSize = pwfx->nBlockAlign; - mDeviceSamplesPerSec = pwfx->nSamplesPerSec; - //sampleWindowSize = (inputSamplesPerSec/100); - - DWORD flags = 0; //useInputDevice ? 0 : AUDCLNT_STREAMFLAGS_LOOPBACK; - - //Random limit of 1ms to 1 seconds - if (mBuffering == 0) - mBuffering = 50; - mBuffering = std::min(std::max(mBuffering, 1LL), 1000LL); - - err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(mBuffering), 0, pwfx, NULL); - //err = AUDCLNT_E_UNSUPPORTED_FORMAT; - - if (FAILED(err)) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Could not initialize audio client, result = %08lX\n", err); - CoTaskMemFree(pwfx); - return false; - } - - // acquire services - if (mAudioDir == AUDIODIR_SOURCE) - err = mmClient->GetService(IID_IAudioCaptureClient, (void**)&mmCapture); - else - err = mmClient->GetService(IID_IAudioRenderClient, (void**)&mmRender); - - if (FAILED(err)) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Could not get audio %s client, result = %08lX\n", - (mAudioDir == AUDIODIR_SOURCE ? TEXT("capture") : TEXT("render")), err); - CoTaskMemFree(pwfx); - return false; - } - - err = mmClient->GetService(__uuidof(IAudioClock), (void**)&mmClock); - - if (FAILED(err)) - { - if (!mDeviceLost) - Console.WriteLn("MMAudioDevice::Reinitialize(): Could not get audio capture clock, result = %08lX\n", err); - CoTaskMemFree(pwfx); - return false; - } - - CoTaskMemFree(pwfx); - - // Setup resampler - int converterType = SRC_SINC_FASTEST; - int errVal = 0; - - mResampler = src_delete(mResampler); - mResampler = src_new(converterType, mDeviceChannels, &errVal); - - if (!mResampler) - { -#ifndef _DEBUG - Console.WriteLn("USB: Failed to create resampler: error %08lX", errVal); -#endif - return false; - } - - ResetBuffers(); - - if (mDeviceLost && !mFirstSamples) //TODO really lost and just first run. Call Start() from ctor always anyway? - this->Start(); - - mDeviceLost = false; - - return true; - } - - void MMAudioDevice::Start() - { - src_reset(mResampler); - if (mmClient) - mmClient->Start(); - mPaused = false; - } - - void MMAudioDevice::Stop() - { - mPaused = true; - if (mmClient) - mmClient->Stop(); - } - - void MMAudioDevice::ResetBuffers() - { - if (WaitForSingleObject(mMutex, 5000) != WAIT_OBJECT_0) - { - return; - } - - size_t bytes; - if (mAudioDir == AUDIODIR_SOURCE) - { - bytes = mDeviceChannels * mDeviceSamplesPerSec * sizeof(float) * mBuffering / 1000; - bytes += bytes % (mDeviceChannels * sizeof(float)); - mInBuffer.reserve(bytes); - - bytes = mDeviceChannels * mSamplesPerSec * sizeof(short) * mBuffering / 1000; - bytes += bytes % (mDeviceChannels * sizeof(short)); - mOutBuffer.reserve(bytes); - } - else - { - bytes = mDeviceChannels * mDeviceSamplesPerSec * sizeof(float) * mBuffering / 1000; - bytes += bytes % (mDeviceChannels * sizeof(float)); - mOutBuffer.reserve(bytes); - - bytes = mDeviceChannels * mSamplesPerSec * sizeof(short) * mBuffering / 1000; - bytes += bytes % (mDeviceChannels * sizeof(short)); - mInBuffer.reserve(bytes); - } - - ReleaseMutex(mMutex); - } - - //TODO or just return samples count in mOutBuffer? - bool MMAudioDevice::GetFrames(uint32_t* size) - { - if (WaitForSingleObject(mMutex, 5000) != WAIT_OBJECT_0) - { - *size = 0; - return false; - } - *size = mOutBuffer.size() / mDeviceChannels; - ReleaseMutex(mMutex); - return true; - } - - unsigned WINAPI MMAudioDevice::CaptureThread(LPVOID ptr) - { - MMAudioDevice* src = (MMAudioDevice*)ptr; - std::vector rebuf; - unsigned ret = 1; - bool bThreadComInitialized = false; - - //TODO APARTMENTTHREADED instead? - HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if ((S_OK != hr) && (S_FALSE != hr) /* already inited */ && (hr != RPC_E_CHANGED_MODE)) - { - goto error; - } - - if (hr != RPC_E_CHANGED_MODE) - bThreadComInitialized = true; - - //Call mmClient->Start() here instead? - - while (!src->mQuit) - { - while (src->mPaused) - { - Sleep(100); - if (src->mQuit) - goto quit; - } - - src->GetMMBuffer(); - if (src->mInBuffer.size()) - { - size_t resampled = static_cast(src->mInBuffer.size() * src->mResampleRatio * src->mTimeAdjust); - if (resampled == 0) - resampled = src->mInBuffer.size(); - rebuf.resize(resampled); - - SRC_DATA srcData; - memset(&srcData, 0, sizeof(SRC_DATA)); - size_t output_frames = 0; - float* pBegin = rebuf.data(); - float* pEnd = pBegin + rebuf.size(); - - memset(&srcData, 0, sizeof(SRC_DATA)); - - while (src->mInBuffer.peek_read() > 0) - { - srcData.data_in = src->mInBuffer.front(); - srcData.input_frames = src->mInBuffer.peek_read() / src->GetChannels(); - srcData.data_out = pBegin; - srcData.output_frames = (pEnd - pBegin) / src->GetChannels(); - srcData.src_ratio = src->mResampleRatio * src->mTimeAdjust; - - src_process(src->mResampler, &srcData); - output_frames += srcData.output_frames_gen; - pBegin += srcData.output_frames_gen * src->GetChannels(); - - size_t samples = srcData.input_frames_used * src->GetChannels(); - if (!samples) - break; //TODO happens? - src->mInBuffer.read(samples); - } - - DWORD resMutex = WaitForSingleObject(src->mMutex, 30000); - if (resMutex != WAIT_OBJECT_0) - { - goto error; - } - - size_t len = output_frames * src->GetChannels(); - float* pSrc = rebuf.data(); - while (len > 0) - { - size_t samples = std::min(len, src->mOutBuffer.peek_write(true)); - src_float_to_short_array(pSrc, src->mOutBuffer.back(), samples); - src->mOutBuffer.write(samples); - len -= samples; - pSrc += samples; - } - - if (!ReleaseMutex(src->mMutex)) - { - goto error; - } - } - Sleep(src->mDeviceLost ? 1000 : 1); - } - - quit: - ret = 0; - error: - if (bThreadComInitialized) - CoUninitialize(); - - _endthreadex(ret); - return ret; - } - - unsigned WINAPI MMAudioDevice::RenderThread(LPVOID ptr) - { - MMAudioDevice* src = (MMAudioDevice*)ptr; - std::vector buffer; - UINT32 bufferFrameCount, numFramesPadding, numFramesAvailable; - BYTE* pData; - SRC_DATA srcData; - unsigned ret = 1; - HRESULT hr = 0; - bool bThreadComInitialized = false; - - memset(&srcData, 0, sizeof(SRC_DATA)); - - //TODO APARTMENTTHREADED instead? - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if ((S_OK != hr) && (S_FALSE != hr) /* already inited */ && (hr != RPC_E_CHANGED_MODE)) - { - goto error; - } - - if (hr != RPC_E_CHANGED_MODE) - bThreadComInitialized = true; - - //Call mmClient->Start() here instead? - - while (!src->mQuit) - { - while (src->mPaused) - { - Sleep(100); - if (src->mQuit) - goto quit; - } - - if (src->mDeviceLost && !src->Reinitialize()) - { - Sleep(1000); - continue; - } - - DWORD resMutex = WaitForSingleObject(src->mMutex, 5000); - if (resMutex != WAIT_OBJECT_0) - { - goto error; - } - - hr = src->mmClient->GetBufferSize(&bufferFrameCount); - if (FAILED(hr)) - goto device_error; - - hr = src->mmClient->GetCurrentPadding(&numFramesPadding); - if (FAILED(hr)) - goto device_error; - - numFramesAvailable = std::min(bufferFrameCount - numFramesPadding, UINT32(src->mInBuffer.size() / src->GetChannels())); - - if (src->mInBuffer.size()) - { - buffer.resize(src->mInBuffer.size()); - size_t read = buffer.size(); - - while (read > 0 && numFramesAvailable > 0) - { - size_t samples = std::min(src->mInBuffer.peek_read(), read); - src_short_to_float_array(src->mInBuffer.front(), - buffer.data(), samples); - - //XXX May get AUDCLNT_E_BUFFER_TOO_LARGE - hr = src->mmRender->GetBuffer(numFramesAvailable, &pData); - if (FAILED(hr)) - goto device_error; - - srcData.data_in = buffer.data(); - srcData.input_frames = samples / src->GetChannels(); - srcData.data_out = (float*)pData; - srcData.output_frames = numFramesAvailable; - srcData.src_ratio = src->mResampleRatio; - - src_process(src->mResampler, &srcData); - - hr = src->mmRender->ReleaseBuffer(srcData.output_frames_gen, 0); - if (FAILED(hr)) - goto device_error; - - read -= srcData.input_frames_used * src->GetChannels(); - src->mInBuffer.read(srcData.input_frames_used * src->GetChannels()); - } - } - // TODO WASAPI seems to stop playing when buffer underrun, so skip this - /*else - { - if (numFramesPadding < src->mDeviceSamplesPerSec / 1000) - { - numFramesAvailable = std::min(bufferFrameCount - numFramesPadding, (src->mDeviceSamplesPerSec / 1000)); - - hr = src->mmRender->GetBuffer(numFramesAvailable, &pData); - if (FAILED(hr)) - goto device_error; - hr = src->mmRender->ReleaseBuffer(numFramesAvailable, AUDCLNT_BUFFERFLAGS_SILENT); - } - }*/ - - device_error: - if (!ReleaseMutex(src->mMutex)) - { - goto error; - } - - if (FAILED(hr)) - { - if (hr == AUDCLNT_E_DEVICE_INVALIDATED) - { - src->mDeviceLost = true; - } - else - goto error; - } - - Sleep(src->mDeviceLost ? 1000 : 10); - } - - quit: - ret = 0; - error: - if (bThreadComInitialized) - CoUninitialize(); - - _endthreadex(ret); - return ret; - } - - uint32_t MMAudioDevice::GetBuffer(int16_t* outBuf, uint32_t outFrames) - { - if (!mQuit && (mThread == INVALID_HANDLE_VALUE || - WaitForSingleObject(mThread, 0) == WAIT_OBJECT_0)) //Thread got killed prematurely - { - mThread = (HANDLE)_beginthreadex(NULL, 0, MMAudioDevice::CaptureThread, this, 0, NULL); - } - - DWORD resMutex = WaitForSingleObject(mMutex, 1000); - if (resMutex != WAIT_OBJECT_0) - { - return 0; - } - - //mSamples += outFrames; - //mTime = GetQPCTime100NS(); - //if (mLastTimeNS == 0) mLastTimeNS = mTime; - //LONGLONG diff = mTime - mLastTimeNS; - //if (diff >= LONGLONG(1e7)) - //{ - // mTimeAdjust = (mSamples / (diff / 1e7)) / mSamplesPerSec; - // //if(mTimeAdjust > 1.0) mTimeAdjust = 1.0; //If game is in 'turbo mode', just return zero samples or...? - // mLastTimeNS = mTime; - // mSamples = 0; - //} - - int samples_to_read = outFrames * mDeviceChannels; - short* pDst = (short*)outBuf; - //assert(samples_to_read <= mOutBuffer.size()); - - while (samples_to_read > 0) - { - int samples = std::min(samples_to_read, (int)mOutBuffer.peek_read()); - if (!samples) - break; - memcpy(pDst, mOutBuffer.front(), samples * sizeof(short)); - - mOutBuffer.read(samples); - pDst += samples; - samples_to_read -= samples; - } - - resMutex = ReleaseMutex(mMutex); - return (outFrames - (samples_to_read / mDeviceChannels)); - } - - uint32_t MMAudioDevice::SetBuffer(int16_t* inBuf, uint32_t inFrames) - { - if (!mQuit && (mThread == INVALID_HANDLE_VALUE || - WaitForSingleObject(mThread, 0) == WAIT_OBJECT_0)) //Thread got killed prematurely - { - mThread = (HANDLE)_beginthreadex(NULL, 0, MMAudioDevice::RenderThread, this, 0, NULL); - } - - DWORD resMutex = WaitForSingleObject(mMutex, 1000); - if (resMutex != WAIT_OBJECT_0) - { - return 0; - } - - size_t nbytes = inFrames * sizeof(short) * GetChannels(); - mInBuffer.write((uint8_t*)inBuf, nbytes); - - if (!ReleaseMutex(mMutex)) - { - } - - return inFrames; - } - - /* - Returns read frame count. -*/ - uint32_t MMAudioDevice::GetMMBuffer() - { - UINT64 devPosition, qpcTimestamp; - LPBYTE captureBuffer; - UINT32 numFramesRead; - DWORD dwFlags = 0; - - if (mDeviceLost) - { - FreeData(); - if (Reinitialize()) - { - Start(); - } - else - { - return 0; - } - } - - UINT32 captureSize = 0; - HRESULT hRes = mmCapture->GetNextPacketSize(&captureSize); - - if (FAILED(hRes)) - { - if (hRes == AUDCLNT_E_DEVICE_INVALIDATED) - { - mDeviceLost = true; - FreeData(); - } - return 0; - } - - if (!captureSize) - return 0; - - if (SUCCEEDED(mmCapture->GetBuffer(&captureBuffer, &numFramesRead, &dwFlags, &devPosition, &qpcTimestamp))) - { - size_t totalLen = numFramesRead * mDeviceChannels; - if (dwFlags & AUDCLNT_BUFFERFLAGS_SILENT) - { - while (totalLen && mInBuffer.peek_write() > 0) - { - size_t len = std::min(totalLen, mInBuffer.peek_write()); - memset(mInBuffer.back(), 0, sizeof(float) * len); - mInBuffer.write(len); - totalLen -= len; - } - } - else - { - mInBuffer.write((uint8_t*)captureBuffer, sizeof(float) * totalLen); - } - - mmCapture->ReleaseBuffer(numFramesRead); - } - - return numFramesRead; - } - - void MMAudioDevice::SetResampling(int samplerate) - { - if (mDeviceSamplesPerSec == samplerate) - { - mResample = false; - return; - } - mSamplesPerSec = samplerate; - if (mAudioDir == AUDIODIR_SOURCE) - mResampleRatio = double(samplerate) / double(mDeviceSamplesPerSec); - else - mResampleRatio = double(mDeviceSamplesPerSec) / double(samplerate); - mResample = true; - ResetBuffers(); - } - - bool MMAudioDevice::AudioInit() - { - QueryPerformanceFrequency(&clockFreq); - return true; - } - - void MMAudioDevice::AudioDevices(std::vector& devices, AudioDir dir) - { - const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); - const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); - IMMDeviceEnumerator* mmEnumerator; - HRESULT err; - - err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); - if (FAILED(err)) - { - Console.WriteLn("AudioDevices: Could not create IMMDeviceEnumerator\n"); - return; - } - - IMMDeviceCollection* collection; - EDataFlow audioDeviceType = (dir == AUDIODIR_SOURCE ? eCapture : eRender); - DWORD flags = DEVICE_STATE_ACTIVE; - //if (!bConnectedOnly) - flags |= DEVICE_STATE_UNPLUGGED; - - err = mmEnumerator->EnumAudioEndpoints(audioDeviceType, flags, &collection); - if (FAILED(err)) - { - Console.WriteLn("AudioDevices: Could not enumerate audio endpoints\n"); - SafeRelease(mmEnumerator); - return; - } - - UINT count; - if (SUCCEEDED(collection->GetCount(&count))) - { - for (UINT i = 0; i < count; i++) - { - IMMDevice* device; - if (SUCCEEDED(collection->Item(i, &device))) - { - const WCHAR* wstrID; - if (SUCCEEDED(device->GetId((LPWSTR*)&wstrID))) - { - IPropertyStore* store; - if (SUCCEEDED(device->OpenPropertyStore(STGM_READ, &store))) - { - PROPVARIANT varName; - - PropVariantInit(&varName); - if (SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &varName))) - { - const WCHAR* wstrName = varName.pwszVal; - - AudioDeviceInfo info; - info.strID = wstrID; - info.strName = wstrName; - devices.push_back(info); - } - } - - CoTaskMemFree((LPVOID)wstrID); - } - - SafeRelease(device); - } - } - } - - SafeRelease(collection); - SafeRelease(mmEnumerator); - } - - int MMAudioDevice::Configure(int port, const char* dev_type, void* data) - { - Win32Handles h = *(Win32Handles*)data; - WASAPISettings settings; - settings.port = port; - settings.dev_type = dev_type; - - return (int)DialogBoxParam(h.hInst, - MAKEINTRESOURCE(IDD_DLGWASAPI_USB), - h.hWnd, - (DLGPROC)WASAPIDlgProc, (LPARAM)&settings); - } - - static void RefreshInputAudioList(HWND hW, LRESULT idx, WASAPISettings* settings) - { - settings->sourceDevs.clear(); - - SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_RESETCONTENT, 0, 0); - SendDlgItemMessage(hW, IDC_COMBO2_USB, CB_RESETCONTENT, 0, 0); - - SendDlgItemMessageW(hW, IDC_COMBO1_USB, CB_ADDSTRING, 0, (LPARAM)L"None"); - SendDlgItemMessageW(hW, IDC_COMBO2_USB, CB_ADDSTRING, 0, (LPARAM)L"None"); - - SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_SETCURSEL, 0, 0); - SendDlgItemMessage(hW, IDC_COMBO2_USB, CB_SETCURSEL, 0, 0); - - MMAudioDevice::AudioDevices(settings->sourceDevs, AUDIODIR_SOURCE); - AudioDeviceInfoList::iterator it; - int i = 0; - for (it = settings->sourceDevs.begin(); it != settings->sourceDevs.end(); ++it) - { - SendDlgItemMessageW(hW, IDC_COMBO1_USB, CB_ADDSTRING, 0, (LPARAM)it->strName.c_str()); - SendDlgItemMessageW(hW, IDC_COMBO2_USB, CB_ADDSTRING, 0, (LPARAM)it->strName.c_str()); - - i++; - if (it->strID == settings->selectedDev[0]) - SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_SETCURSEL, i, i); - if (it->strID == settings->selectedDev[1]) - SendDlgItemMessage(hW, IDC_COMBO2_USB, CB_SETCURSEL, i, i); - } - } - - static void RefreshOutputAudioList(HWND hW, LRESULT idx, WASAPISettings* settings) - { - settings->sinkDevs.clear(); - - SendDlgItemMessage(hW, IDC_COMBO3_USB, CB_RESETCONTENT, 0, 0); - SendDlgItemMessageW(hW, IDC_COMBO3_USB, CB_ADDSTRING, 0, (LPARAM)L"None"); - SendDlgItemMessage(hW, IDC_COMBO3_USB, CB_SETCURSEL, 0, 0); - - MMAudioDevice::AudioDevices(settings->sinkDevs, AUDIODIR_SINK); - AudioDeviceInfoList::iterator it; - int i = 0; - for (it = settings->sinkDevs.begin(); it != settings->sinkDevs.end(); ++it) - { - SendDlgItemMessageW(hW, IDC_COMBO3_USB, CB_ADDSTRING, 0, (LPARAM)it->strName.c_str()); - - i++; - if (it->strID == settings->selectedDev[2]) - SendDlgItemMessage(hW, IDC_COMBO3_USB, CB_SETCURSEL, i, i); - } - } - - static BOOL CALLBACK WASAPIDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - WASAPISettings* s; - - switch (uMsg) - { - case WM_CREATE: - SetWindowLongPtr(hW, GWLP_USERDATA, lParam); - break; - case WM_INITDIALOG: - { - s = (WASAPISettings*)lParam; - SetWindowLongPtr(hW, GWLP_USERDATA, lParam); - int buffering = 50; - LoadSetting(s->dev_type, s->port, APINAME, N_BUFFER_LEN_SRC, buffering); - - SendDlgItemMessage(hW, IDC_SLIDER1_USB, TBM_SETRANGEMIN, TRUE, 1); - SendDlgItemMessage(hW, IDC_SLIDER1_USB, TBM_SETRANGEMAX, TRUE, 1000); - SendDlgItemMessage(hW, IDC_SLIDER1_USB, TBM_SETPOS, TRUE, buffering); - SetDlgItemInt(hW, IDC_BUFFER1_USB, buffering, FALSE); - - buffering = 50; - LoadSetting(s->dev_type, s->port, APINAME, N_BUFFER_LEN_SINK, buffering); - - SendDlgItemMessage(hW, IDC_SLIDER2_USB, TBM_SETRANGEMIN, TRUE, 1); - SendDlgItemMessage(hW, IDC_SLIDER2_USB, TBM_SETRANGEMAX, TRUE, 1000); - SendDlgItemMessage(hW, IDC_SLIDER2_USB, TBM_SETPOS, TRUE, buffering); - SetDlgItemInt(hW, IDC_BUFFER2_USB, buffering, FALSE); - - for (int i = 0; i < 2; i++) - { - LoadSetting(s->dev_type, s->port, APINAME, (i ? N_AUDIO_SOURCE1 : N_AUDIO_SOURCE0), s->selectedDev[i]); - } - - LoadSetting(s->dev_type, s->port, APINAME, N_AUDIO_SINK0, s->selectedDev[2]); - - RefreshInputAudioList(hW, -1, s); - RefreshOutputAudioList(hW, -1, s); - return TRUE; - } - case WM_HSCROLL: - if ((HWND)lParam == GetDlgItem(hW, IDC_SLIDER1_USB)) - { - int pos = SendDlgItemMessage(hW, IDC_SLIDER1_USB, TBM_GETPOS, 0, 0); - SetDlgItemInt(hW, IDC_BUFFER1_USB, pos, FALSE); - break; - } - else if ((HWND)lParam == GetDlgItem(hW, IDC_SLIDER2_USB)) - { - int pos = SendDlgItemMessage(hW, IDC_SLIDER2_USB, TBM_GETPOS, 0, 0); - SetDlgItemInt(hW, IDC_BUFFER2_USB, pos, FALSE); - break; - } - break; - - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case EN_CHANGE: - { - int tmp = 0; - switch (LOWORD(wParam)) - { - case IDC_BUFFER1_USB: - CHECKED_SET_MAX_INT(tmp, hW, IDC_BUFFER1_USB, FALSE, 1, 1000); - SendDlgItemMessage(hW, IDC_SLIDER1_USB, TBM_SETPOS, TRUE, tmp); - break; - case IDC_BUFFER2_USB: - CHECKED_SET_MAX_INT(tmp, hW, IDC_BUFFER2_USB, FALSE, 1, 1000); - SendDlgItemMessage(hW, IDC_SLIDER2_USB, TBM_SETPOS, TRUE, tmp); - break; - } - } - break; - case BN_CLICKED: - { - switch (LOWORD(wParam)) - { - case IDOK: - { - int p[3]; - s = (WASAPISettings*)GetWindowLongPtr(hW, GWLP_USERDATA); - INT_PTR res = RESULT_OK; - p[0] = SendDlgItemMessage(hW, IDC_COMBO1_USB, CB_GETCURSEL, 0, 0); - p[1] = SendDlgItemMessage(hW, IDC_COMBO2_USB, CB_GETCURSEL, 0, 0); - p[2] = SendDlgItemMessage(hW, IDC_COMBO3_USB, CB_GETCURSEL, 0, 0); - - for (int i = 0; i < 3; i++) - { - s->selectedDev[i].clear(); - - if (p[i] > 0) - s->selectedDev[i] = ((i < 2 ? s->sourceDevs.begin() : s->sinkDevs.begin()) + p[i] - 1)->strID; - } - - const wchar_t* n[] = {N_AUDIO_SOURCE0, N_AUDIO_SOURCE1, N_AUDIO_SINK0}; - for (int i = 0; i < 3; i++) - { - if (!SaveSetting(s->dev_type, s->port, APINAME, n[i], s->selectedDev[i])) - res = RESULT_FAILED; - } - - if (!SaveSetting(s->dev_type, s->port, APINAME, N_BUFFER_LEN_SRC, (int32_t)SendDlgItemMessage(hW, IDC_SLIDER1_USB, TBM_GETPOS, 0, 0))) - res = RESULT_FAILED; - - if (!SaveSetting(s->dev_type, s->port, APINAME, N_BUFFER_LEN_SINK, (int32_t)SendDlgItemMessage(hW, IDC_SLIDER2_USB, TBM_GETPOS, 0, 0))) - res = RESULT_FAILED; - - EndDialog(hW, res); - return TRUE; - } - case IDCANCEL: - EndDialog(hW, RESULT_CANCELED); - return TRUE; - } - } - break; - } - } - return FALSE; - } - - } // namespace audiodev_wasapi -} // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/audiodev-wasapi.h b/pcsx2/USB/usb-mic/audiodev-wasapi.h deleted file mode 100644 index 981d0738a4..0000000000 --- a/pcsx2/USB/usb-mic/audiodev-wasapi.h +++ /dev/null @@ -1,161 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -// Used OBS as example - -#include "audiodeviceproxy.h" -#include -#include "USB/shared/ringbuffer.h" - -#include -#include - -namespace usb_mic -{ - namespace audiodev_wasapi - { - - static const char* APINAME = "wasapi"; - - class MMAudioDevice : public AudioDevice - { - public: - MMAudioDevice(int port, const char* dev_type, int device, AudioDir dir) - : AudioDevice(port, dev_type, device, dir) - , mmCapture(NULL) - , mmRender(NULL) - , mmClient(NULL) - , mmDevice(NULL) - , mmClock(NULL) - , mmEnumerator(NULL) - , mResampler(NULL) - , mDeviceLost(false) - , mResample(false) - , mFirstSamples(true) - , mSamplesPerSec(48000) - , mResampleRatio(1.0) - , mTimeAdjust(1.0) - , mThread(INVALID_HANDLE_VALUE) - , mQuit(false) - , mPaused(true) - , mLastGetBufferMS(0) - , mBuffering(50) - { - mMutex = CreateMutex(NULL, FALSE, TEXT("ResampledQueueMutex")); - if (!Init()) - throw AudioDeviceError("MMAudioDevice:: device name is empty, skipping"); - if (!Reinitialize()) - throw AudioDeviceError("MMAudioDevice:: WASAPI init failed!"); - } - - ~MMAudioDevice(); - void FreeData(); - bool Init(); - bool Reinitialize(); - void Start(); - void Stop(); - void ResetBuffers(); - //TODO or just return samples count in mOutBuffer? - virtual bool GetFrames(uint32_t* size); - - static unsigned WINAPI CaptureThread(LPVOID ptr); - static unsigned WINAPI RenderThread(LPVOID ptr); - - virtual uint32_t GetBuffer(int16_t* outBuf, uint32_t outFrames); - virtual uint32_t SetBuffer(int16_t* inBuf, uint32_t inFrames); - /* - Returns read frame count. - */ - uint32_t GetMMBuffer(); - virtual void SetResampling(int samplerate); - virtual uint32_t GetChannels() - { - return mDeviceChannels; - } - - virtual bool Compare(AudioDevice* compare) - { - if (compare) - { - MMAudioDevice* src = static_cast(compare); - if (src && mDevID == src->mDevID) - return true; - } - return false; - } - - static const char* TypeName() - { - return APINAME; - } - - static const TCHAR* Name() - { - return TEXT("WASAPI"); - } - - static bool AudioInit(); - - static void AudioDeinit() - { - } - - static void AudioDevices(std::vector& devices, AudioDir dir); - static int Configure(int port, const char* dev_type, void* data); - - private: - IMMDeviceEnumerator* mmEnumerator; - - IMMDevice* mmDevice; - IAudioClient* mmClient; - IAudioCaptureClient* mmCapture; - IAudioRenderClient* mmRender; - IAudioClock* mmClock; - - bool mResample; - bool mFloat; - bool mFirstSamples; //On the first call, empty the buffer to lower latency - UINT mDeviceChannels; - UINT mDeviceSamplesPerSec; - UINT mSamplesPerSec; - UINT mDeviceBitsPerSample; - UINT mDeviceBlockSize; - DWORD mInputChannelMask; - - std::wstring mDevID; - bool mDeviceLost; - std::wstring mDeviceName; - LONGLONG mBuffering; - - SRC_STATE* mResampler; - double mResampleRatio; - // Speed up or slow down audio - double mTimeAdjust; - RingBuffer mInBuffer; - RingBuffer mOutBuffer; - HANDLE mThread; - HANDLE mMutex; - bool mQuit; - bool mPaused; - LONGLONG mLastGetBufferMS; - - LONGLONG mTime = 0; - int mSamples = 0; - LONGLONG mLastTimeMS = 0; - LONGLONG mLastTimeNS = 0; - }; - - } // namespace audiodev_wasapi -} // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/audiodev.h b/pcsx2/USB/usb-mic/audiodev.h index 3f389c88cb..dc16e16e0e 100644 --- a/pcsx2/USB/usb-mic/audiodev.h +++ b/pcsx2/USB/usb-mic/audiodev.h @@ -17,25 +17,12 @@ // Types to shared by platforms and config. dialog. // -#ifndef AUDIODEV_H -#define AUDIODEV_H +#pragma once +#include #include #include -#include - -#define S_AUDIO_SOURCE0 TEXT("Audio source 1") -#define S_AUDIO_SOURCE1 TEXT("Audio source 2") -#define S_AUDIO_SINK0 TEXT("Audio sink 1") -#define S_AUDIO_SINK1 TEXT("Audio sink 2") -#define N_AUDIO_SOURCE0 TEXT("audio_src_0") -#define N_AUDIO_SOURCE1 TEXT("audio_src_1") -#define N_AUDIO_SINK0 TEXT("audio_sink_0") -#define N_AUDIO_SINK1 TEXT("audio_sink_1") -#define S_BUFFER_LEN TEXT("Buffer length") -#define N_BUFFER_LEN TEXT("buffer_len") -#define N_BUFFER_LEN_SRC TEXT("buffer_len_src") -#define N_BUFFER_LEN_SINK TEXT("buffer_len_sink") +#include enum MicMode { @@ -53,46 +40,28 @@ enum AudioDir AUDIODIR_SINK }; -//TODO sufficient for linux too? -struct AudioDeviceInfoA -{ - //int intID; //optional ID - std::string strID; - std::string strName; //gui name -}; - -struct AudioDeviceInfoW -{ - //int intID; //optional ID - std::wstring strID; - std::wstring strName; //gui name -}; - -#if _WIN32 -#define AudioDeviceInfo AudioDeviceInfoW -#else -#define AudioDeviceInfo AudioDeviceInfoA -#endif - class AudioDevice { public: - AudioDevice(int port, const char* dev_type, int device, AudioDir dir) + static constexpr s32 DEFAULT_LATENCY = 100; + static constexpr const char* DEFAULT_LATENCY_STR = "100"; + + AudioDevice(u32 port, AudioDir dir, u32 channels) : mPort(port) - , mDevType(dev_type) - , mDevice(device) , mAudioDir(dir) + , mChannels(channels) { } protected: - int mPort; - const char* mDevType; - int mDevice; + u32 mPort; + s32 mSubDevice; AudioDir mAudioDir; + u32 mChannels; public: - virtual ~AudioDevice() {} + virtual ~AudioDevice() = default; + //get buffer, converted to 16bit int format virtual uint32_t GetBuffer(int16_t* buff, uint32_t len) = 0; virtual uint32_t SetBuffer(int16_t* buff, uint32_t len) = 0; @@ -102,17 +71,16 @@ public: */ virtual bool GetFrames(uint32_t* size) = 0; virtual void SetResampling(int samplerate) = 0; - virtual uint32_t GetChannels() = 0; + uint32_t GetChannels() { return mChannels; } - virtual void Start() {} - virtual void Stop() {} + virtual bool Start() = 0; + virtual void Stop() = 0; // Compare if another instance is using the same device - virtual bool Compare(AudioDevice* compare) = 0; + virtual bool Compare(AudioDevice* compare) const = 0; - //Remember to add to your class - //static const wchar_t* GetName(); + static std::unique_ptr CreateDevice(u32 port, AudioDir dir, u32 channels, std::string devname, s32 latency); + static std::unique_ptr CreateNoopDevice(u32 port, AudioDir dir, u32 channels); + static std::vector> GetInputDeviceList(); + static std::vector> GetOutputDeviceList(); }; - -typedef std::vector AudioDeviceInfoList; -#endif diff --git a/pcsx2/USB/usb-mic/audiodeviceproxy.h b/pcsx2/USB/usb-mic/audiodeviceproxy.h deleted file mode 100644 index 3998048da9..0000000000 --- a/pcsx2/USB/usb-mic/audiodeviceproxy.h +++ /dev/null @@ -1,109 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef AUDIODEVICEPROXY_H -#define AUDIODEVICEPROXY_H -#include -#include -#include -#include -#include -#include -#include "USB/helpers.h" -#include "USB/configuration.h" -#include "USB/deviceproxy.h" -#include "audiodev.h" - -namespace usb_mic -{ - - class AudioDeviceError : public std::runtime_error - { - public: - AudioDeviceError(const char* msg) - : std::runtime_error(msg) - { - } - virtual ~AudioDeviceError() throw() {} - }; - - class AudioDeviceProxyBase : public ProxyBase - { - AudioDeviceProxyBase(const AudioDeviceProxyBase&) = delete; - AudioDeviceProxyBase& operator=(const AudioDeviceProxyBase&) = delete; - - public: - AudioDeviceProxyBase(){}; - AudioDeviceProxyBase(const std::string& name); - virtual AudioDevice* CreateObject(int port, const char* dev_type, int mic, AudioDir dir) const = 0; //Can be generalized? Probably not - virtual void AudioDevices(std::vector& devices, AudioDir) const = 0; - virtual bool AudioInit() = 0; - virtual void AudioDeinit() = 0; - }; - - template - class AudioDeviceProxy : public AudioDeviceProxyBase - { - AudioDeviceProxy(const AudioDeviceProxy&) = delete; - - public: - AudioDeviceProxy() {} - AudioDeviceProxy(const std::string& name) - : AudioDeviceProxyBase(name) - { - } //Why can't it automagically, ugh - ~AudioDeviceProxy() { } - - AudioDevice* CreateObject(int port, const char* dev_type, int mic, AudioDir dir) const - { - try - { - return new T(port, dev_type, mic, dir); - } - catch (AudioDeviceError& err) - { - (void)err; - return nullptr; - } - } - virtual const TCHAR* Name() const - { - return T::Name(); - } - virtual int Configure(int port, const char* dev_type, void* data) - { - return T::Configure(port, dev_type, data); - } - virtual void AudioDevices(std::vector& devices, AudioDir dir) const - { - T::AudioDevices(devices, dir); - } - virtual bool AudioInit() - { - return T::AudioInit(); - } - virtual void AudioDeinit() - { - T::AudioDeinit(); - } - }; - - class RegisterAudioDevice : public RegisterProxy - { - public: - static void Register(); - }; -} // namespace usb_mic -#endif diff --git a/pcsx2/USB/usb-mic/usb-headset.cpp b/pcsx2/USB/usb-mic/usb-headset.cpp index 073232e8ed..6101c622a0 100644 --- a/pcsx2/USB/usb-mic/usb-headset.cpp +++ b/pcsx2/USB/usb-mic/usb-headset.cpp @@ -25,13 +25,14 @@ // Most stuff is based on Qemu 1.7 USB soundcard passthrough code. #include "PrecompiledHeader.h" -#include "USB/qemu-usb/vl.h" +#include "USB/qemu-usb/qusb.h" #include "USB/qemu-usb/desc.h" -#include "USB/shared/inifile_usb.h" -#include - -#include "audio.h" -#include "usb-headset.h" +#include "USB/qemu-usb/USBinternal.h" +#include "USB/usb-mic/audio.h" +#include "USB/usb-mic/usb-headset.h" +#include "USB/usb-mic/audiodev.h" +#include "USB/USB.h" +#include "StateWrapper.h" #define BUFFER_FRAMES 200 @@ -50,9 +51,8 @@ namespace usb_mic typedef struct HeadsetState { USBDevice dev; - AudioDevice* audsrc; - AudioDevice* audsink; - AudioDeviceProxyBase* audsrcproxy; + std::unique_ptr audsrc; + std::unique_ptr audsink; struct freeze { @@ -79,7 +79,7 @@ namespace usb_mic bool mute; uint8_t vol[2]; } mixer; //TODO - } f; //freezable + } f; //freezable std::vector in_buffer; std::vector out_buffer; @@ -88,367 +88,351 @@ namespace usb_mic USBDescDevice desc_dev; } HeadsetState; - std::list HeadsetDevice::ListAPIs() - { - return RegisterAudioDevice::instance().Names(); - } - const TCHAR* HeadsetDevice::LongAPIName(const std::string& name) - { - return RegisterAudioDevice::instance().Proxy(name)->Name(); - } - static const uint8_t headset_dev_descriptor[] = { - /* bLength */ 0x12, //(18) - /* bDescriptorType */ 0x01, //(1) + /* bLength */ 0x12, //(18) + /* bDescriptorType */ 0x01, //(1) /* bcdUSB */ WBVAL(0x0110), //(272) - /* bDeviceClass */ 0x00, //(0) - /* bDeviceSubClass */ 0x00, //(0) - /* bDeviceProtocol */ 0x00, //(0) - /* bMaxPacketSize0 */ 0x40, //(64) + /* bDeviceClass */ 0x00, //(0) + /* bDeviceSubClass */ 0x00, //(0) + /* bDeviceProtocol */ 0x00, //(0) + /* bMaxPacketSize0 */ 0x40, //(64) /* idVendor */ WBVAL(0x046d), //Logitech /* idProduct */ WBVAL(0x0a01), //"USB headset" from usb.ids /* bcdDevice */ WBVAL(0x1012), //(10.12) - /* iManufacturer */ 0x01, //(1) - /* iProduct */ 0x02, //(2) - /* iSerialNumber */ 0x00, //(0) unused - /* bNumConfigurations */ 0x01, //(1) + /* iManufacturer */ 0x01, //(1) + /* iProduct */ 0x02, //(2) + /* iSerialNumber */ 0x00, //(0) unused + /* bNumConfigurations */ 0x01, //(1) }; static const uint8_t headset_config_descriptor[] = { /* Configuration 1 */ - USB_CONFIGURATION_DESC_SIZE, /* bLength */ + USB_CONFIGURATION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ - WBVAL(318), /* wTotalLength */ - 0x03, /* bNumInterfaces */ - 0x01, /* bConfigurationValue */ - 0x00, /* iConfiguration */ - USB_CONFIG_BUS_POWERED, /* bmAttributes */ - USB_CONFIG_POWER_MA(100), /* bMaxPower */ + WBVAL(318), /* wTotalLength */ + 0x03, /* bNumInterfaces */ + 0x01, /* bConfigurationValue */ + 0x00, /* iConfiguration */ + USB_CONFIG_BUS_POWERED, /* bmAttributes */ + USB_CONFIG_POWER_MA(100), /* bMaxPower */ /* Interface 0, Alternate Setting 0, Audio Control */ - USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ + USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x00, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x00, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ - AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + 0x00, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Control Interface */ AUDIO_CONTROL_INTERFACE_DESC_SZ(2), /* bLength : 8+n */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ - WBVAL(0x0100), /* 1.00 */ /* bcdADC */ - WBVAL(0x0075), /* wTotalLength : this + following unit/terminal sizes */ - 0x02, /* bInCollection */ - 0x01, /* baInterfaceNr(0) */ - 0x02, /* baInterfaceNr(1) */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ + WBVAL(0x0100), /* 1.00 */ /* bcdADC */ + WBVAL(0x0075), /* wTotalLength : this + following unit/terminal sizes */ + 0x02, /* bInCollection */ + 0x01, /* baInterfaceNr(0) */ + 0x02, /* baInterfaceNr(1) */ /* Audio Input Terminal */ - AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength : 12 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ - 0x0d, /* bTerminalID */ + AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength : 12 */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ + 0x0d, /* bTerminalID */ WBVAL(AUDIO_TERMINAL_MICROPHONE), /* wTerminalType */ - 0x00, /* bAssocTerminal */ - 0x01, /* bNrChannels */ - WBVAL(AUDIO_CHANNEL_L), /* wChannelConfig */ - 0x00, /* iChannelNames */ - 0x00, /* iTerminal */ + 0x00, /* bAssocTerminal */ + 0x01, /* bNrChannels */ + WBVAL(AUDIO_CHANNEL_L), /* wChannelConfig */ + 0x00, /* iChannelNames */ + 0x00, /* iTerminal */ /* Audio Feature Unit 6 */ AUDIO_FEATURE_UNIT_DESC_SZ(1, 1), /* bLength : f(ch,n) = 7 + (ch+1)*n */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ - 0x06, /* bUnitID */ - 0x0d, /* bSourceID */ - 0x01, /* bControlSize */ - 0x03, /* bmaControls(0) */ /* mute / volume */ - 0x00, /* bmaControls(1) */ - 0x00, /* iFeature */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ + 0x06, /* bUnitID */ + 0x0d, /* bSourceID */ + 0x01, /* bControlSize */ + 0x03, /* bmaControls(0) */ /* mute / volume */ + 0x00, /* bmaControls(1) */ + 0x00, /* iFeature */ /* Audio Input Terminal */ - AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength : 12 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ - 0x0c, /* bTerminalID */ - WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ - 0x00, /* bAssocTerminal */ - 0x02, /* bNrChannels */ + AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength : 12 */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ + 0x0c, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ + 0x00, /* bAssocTerminal */ + 0x02, /* bNrChannels */ WBVAL((AUDIO_CHANNEL_L | AUDIO_CHANNEL_R)), /* wChannelConfig */ - 0x00, /* iChannelNames */ - 0x00, /* iTerminal */ + 0x00, /* iChannelNames */ + 0x00, /* iTerminal */ AUDIO_MIXER_UNIT_DESC_SZ(2, 1), //0x0A+p+n //0x0d - AUDIO_INTERFACE_DESCRIPTOR_TYPE, - AUDIO_CONTROL_MIXER_UNIT, - 0x09, /* bUnitID */ - 0x02, /* bNrInPins */ - 0x0c, /* baSourceID( 0) */ - 0x06, /* baSourceID( 1) */ - 0x02, /* bNrChannels */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, AUDIO_CONTROL_MIXER_UNIT, 0x09, /* bUnitID */ + 0x02, /* bNrInPins */ + 0x0c, /* baSourceID( 0) */ + 0x06, /* baSourceID( 1) */ + 0x02, /* bNrChannels */ WBVAL((AUDIO_CHANNEL_L | AUDIO_CHANNEL_R)), /* wChannelConfig */ - 0, /* iChannelNames */ - 0x00, /* bmControls */ - 0, /* iMixer */ + 0, /* iChannelNames */ + 0x00, /* bmControls */ + 0, /* iMixer */ /* Audio Feature Unit 1 */ AUDIO_FEATURE_UNIT_DESC_SZ(2, 1), /* bLength : f(ch,n) = 7 + (ch+1)*n */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ - 0x01, /* bUnitID */ - 0x09, /* bSourceID */ - 0x01, /* bControlSize */ - 0x01, /* bmaControls(0) */ /* mute */ - 0x02, /* bmaControls(1) */ /* volume */ - 0x02, /* bmaControls(2) */ /* volume */ - 0x00, /* iFeature */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ + 0x01, /* bUnitID */ + 0x09, /* bSourceID */ + 0x01, /* bControlSize */ + 0x01, /* bmaControls(0) */ /* mute */ + 0x02, /* bmaControls(1) */ /* volume */ + 0x02, /* bmaControls(2) */ /* volume */ + 0x00, /* iFeature */ /* Audio Output Terminal */ AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength : 9 */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ - 0x0e, /* bTerminalID */ - WBVAL(AUDIO_TERMINAL_SPEAKER), /* wTerminalType */ - 0x00, /* bAssocTerminal */ - 0x01, /* bSourceID */ - 0x00, /* iTerminal */ + AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ + 0x0e, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_SPEAKER), /* wTerminalType */ + 0x00, /* bAssocTerminal */ + 0x01, /* bSourceID */ + 0x00, /* iTerminal */ /* Audio Input Terminal */ - AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength : 12 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ - 0x0b, /* bTerminalID */ + AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength : 12 */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ + 0x0b, /* bTerminalID */ WBVAL(AUDIO_TERMINAL_MICROPHONE), /* wTerminalType */ - 0x00, /* bAssocTerminal */ - 0x01, /* bNrChannels */ - WBVAL(AUDIO_CHANNEL_L), /* wChannelConfig */ - 0x00, /* iChannelNames */ - 0x00, /* iTerminal */ + 0x00, /* bAssocTerminal */ + 0x01, /* bNrChannels */ + WBVAL(AUDIO_CHANNEL_L), /* wChannelConfig */ + 0x00, /* iChannelNames */ + 0x00, /* iTerminal */ /* Audio Feature Unit 2 */ AUDIO_FEATURE_UNIT_DESC_SZ(1, 1), /* bLength : f(ch,n) = 7 + (ch+1)*n */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ - 0x02, /* bUnitID */ - 0x0b, /* bSourceID */ - 0x01, /* bControlSize */ - 0x03, /* bmaControls(0) */ /* mute, volume */ - 0x00, /* bmaControls(1) */ - 0x00, /* iFeature */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ + 0x02, /* bUnitID */ + 0x0b, /* bSourceID */ + 0x01, /* bControlSize */ + 0x03, /* bmaControls(0) */ /* mute, volume */ + 0x00, /* bmaControls(1) */ + 0x00, /* iFeature */ - AUDIO_MIXER_UNIT_DESC_SZ(1, 1), - AUDIO_INTERFACE_DESCRIPTOR_TYPE, - AUDIO_CONTROL_MIXER_UNIT, - 0x07, /* bUnitID */ - 0x01, /* bNrInPins */ - 0x02, /* baSourceID( 0) */ - 0x01, /* bNrChannels */ + AUDIO_MIXER_UNIT_DESC_SZ(1, 1), AUDIO_INTERFACE_DESCRIPTOR_TYPE, AUDIO_CONTROL_MIXER_UNIT, 0x07, /* bUnitID */ + 0x01, /* bNrInPins */ + 0x02, /* baSourceID( 0) */ + 0x01, /* bNrChannels */ WBVAL(AUDIO_CHANNEL_L), /* wChannelConfig */ - 0, /* iChannelNames */ - 0x00, /* bmControls */ - 0, /* iMixer */ + 0, /* iChannelNames */ + 0x00, /* bmControls */ + 0, /* iMixer */ /* Audio Output Terminal */ - AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength : 9 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ - 0x0a, /* bTerminalID */ + AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength : 9 */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ + 0x0a, /* bTerminalID */ WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ - 0x00, /* bAssocTerminal */ - 0x07, /* bSourceID */ - 0x00, /* iTerminal */ + 0x00, /* bAssocTerminal */ + 0x07, /* bSourceID */ + 0x00, /* iTerminal */ /* Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith */ - USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ + USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x01, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x00, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x01, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Interface 1, Alternate Setting 1, Audio Streaming - Operational */ - USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ + USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x01, /* bInterfaceNumber */ - 0x01, /* bAlternateSetting */ - 0x01, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x01, /* bInterfaceNumber */ + 0x01, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Streaming Interface */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength : 7 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ - 0x0c, /* bTerminalLink */ - 0x01, /* bDelay */ - WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x0c, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ /* Audio Type I Format */ - AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength : 8 + (n*3) */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength : 8 + (n*3) */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ - AUDIO_FORMAT_TYPE_I, /* bFormatType */ - 0x02, /* bNrChannels */ - 0x02, /* bSubFrameSize */ - 0x10, /* bBitResolution */ - 0x05, /* bSamFreqType */ - B3VAL(8000), /* tSamFreq 1 */ - B3VAL(11025), /* tSamFreq 2 */ - B3VAL(22050), /* tSamFreq 3 */ - B3VAL(44100), /* tSamFreq 4 */ - B3VAL(48000), /* tSamFreq 5 */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x02, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ /* Endpoint - Standard Descriptor */ - AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength : 9 */ - USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - USB_ENDPOINT_OUT(1), /* bEndpointAddress */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength : 9 */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_OUT(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ADAPTIVE, /* bmAttributes */ - WBVAL(0x00c0), /* wMaxPacketSize */ - 0x01, /* bInterval */ - 0x00, /* bRefresh */ - 0x00, /* bSynchAddress */ + WBVAL(0x00c0), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ /* Endpoint - Audio Streaming */ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength : 7 */ - AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ - 0x01, /* bmAttributes */ - 0x00, /* bLockDelayUnits */ - WBVAL(0x0000), /* wLockDelay */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ /* Interface 1, Alternate Setting 2, Audio Streaming - Operational */ - USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ + USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x01, /* bInterfaceNumber */ - 0x02, /* bAlternateSetting */ - 0x01, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x01, /* bInterfaceNumber */ + 0x02, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Streaming Interface */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength : 7 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ - 0x0c, /* bTerminalLink */ - 0x01, /* bDelay */ - WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x0c, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ /* Audio Type I Format */ - AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength : 8 + (n*3) */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength : 8 + (n*3) */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ - AUDIO_FORMAT_TYPE_I, /* bFormatType */ - 0x01, /* bNrChannels */ - 0x02, /* bSubFrameSize */ - 0x10, /* bBitResolution */ - 0x05, /* bSamFreqType */ - B3VAL(8000), /* tSamFreq 1 */ - B3VAL(11025), /* tSamFreq 2 */ - B3VAL(22050), /* tSamFreq 3 */ - B3VAL(44100), /* tSamFreq 4 */ - B3VAL(48000), /* tSamFreq 5 */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x01, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ /* Endpoint - Standard Descriptor */ - AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength : 9 */ - USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - USB_ENDPOINT_OUT(1), /* bEndpointAddress */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength : 9 */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_OUT(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ADAPTIVE, /* bmAttributes */ - WBVAL(0x0060), /* wMaxPacketSize */ - 0x01, /* bInterval */ - 0x00, /* bRefresh */ - 0x00, /* bSynchAddress */ + WBVAL(0x0060), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ /* Endpoint - Audio Streaming */ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength : 7 */ - AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ - 0x01, /* bmAttributes */ - 0x00, /* bLockDelayUnits */ - WBVAL(0x0000), /* wLockDelay */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ /* Interface 2, Alternate Setting 0 */ - USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ + USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x02, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x00, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x02, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Interface 2, Alternate Setting 1 */ - USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ + USB_INTERFACE_DESC_SIZE, /* bLength : 9 */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x02, /* bInterfaceNumber */ - 0x01, /* bAlternateSetting */ - 0x01, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x02, /* bInterfaceNumber */ + 0x01, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Streaming Interface */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength : 7 */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ - 0x0a, /* bTerminalLink */ - 0x00, /* bDelay */ - WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x0a, /* bTerminalLink */ + 0x00, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ /* Audio Type I Format */ - AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength : 8+(n*3) */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength : 8+(n*3) */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ - AUDIO_FORMAT_TYPE_I, /* bFormatType */ - 0x01, /* bNrChannels */ - 0x02, /* bSubFrameSize */ - 0x10, /* bBitResolution */ - 0x05, /* bSamFreqType */ - B3VAL(8000), /* tSamFreq 1 */ - B3VAL(11025), /* tSamFreq 2 */ - B3VAL(22050), /* tSamFreq 3 */ - B3VAL(44100), /* tSamFreq 4 */ - B3VAL(48000), /* tSamFreq 5 */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x01, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ /* Endpoint - Standard Descriptor */ - AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength : 9 */ - USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - USB_ENDPOINT_IN(4), /* bEndpointAddress */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength : 9 */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_IN(4), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ADAPTIVE, /* bmAttributes */ - WBVAL(0x0060), /* wMaxPacketSize */ - 0x01, /* bInterval */ - 0x00, /* bRefresh */ - 0x00, /* bSynchAddress */ + WBVAL(0x0060), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ /* Endpoint - Audio Streaming */ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength : 7 */ - AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ - 0x01, /* bmAttributes */ - 0x02, /* bLockDelayUnits (PCM samples) */ - WBVAL(0x0001), /* wLockDelay */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x02, /* bLockDelayUnits (PCM samples) */ + WBVAL(0x0001), /* wLockDelay */ /* Terminator */ 0 /* bLength */ }; - static const USBDescStrings desc_strings = { - "", + static const USBDescStrings desc_strings = {"", "Logitech", // Atleast SOCOM II checks this (liblgaud?) - "Logitech USB Headset", - "00000000"}; + "Logitech USB Headset", "00000000"}; static void headset_handle_reset(USBDevice* dev) { @@ -459,17 +443,14 @@ namespace usb_mic /* * Note: we arbitrarily map the volume control range onto -inf..+8 dB */ -#define ATTRIB_ID(cs, attrib, idif) \ - (((cs) << 24) | ((attrib) << 16) | (idif)) +#define ATTRIB_ID(cs, attrib, idif) (((cs) << 24) | ((attrib) << 16) | (idif)) // With current descriptor, if I'm not mistaken, // feature unit 2 (0x0100): headphones // feature unit 5 (0x0600): microphone - static int usb_audio_get_control(HeadsetState* s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t* data) + static int usb_audio_get_control(HeadsetState* s, uint8_t attrib, uint16_t cscn, uint16_t idif, int length, uint8_t* data) { uint8_t cs = cscn >> 8; const uint8_t cn = cscn - 1; /* -1 for the non-present master control */ @@ -570,9 +551,10 @@ namespace usb_mic ret = 2; } break; - case ATTRIB_ID(AUDIO_BASS_BOOST_CONTROL, AUDIO_REQUEST_GET_CUR, 0x0100): //??? SOCOM II when in stereo, but there is no bass control defined in descriptor... - //if (cn < 2) { //asks with cn == 2, meaning both channels? -1 is 'master' - data[0] = 0; //bool + case ATTRIB_ID(AUDIO_BASS_BOOST_CONTROL, AUDIO_REQUEST_GET_CUR, + 0x0100): //??? SOCOM II when in stereo, but there is no bass control defined in descriptor... + //if (cn < 2) { //asks with cn == 2, meaning both channels? -1 is 'master' + data[0] = 0; //bool ret = 1; //} break; @@ -581,9 +563,7 @@ namespace usb_mic return ret; } - static int usb_audio_set_control(HeadsetState* s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t* data) + static int usb_audio_set_control(HeadsetState* s, uint8_t attrib, uint16_t cscn, uint16_t idif, int length, uint8_t* data) { uint8_t cs = cscn >> 8; const uint8_t cn = cscn - 1; /* -1 for the non-present master control */ @@ -655,9 +635,7 @@ namespace usb_mic return ret; } - static int usb_audio_ep_control(HeadsetState* s, uint8_t attrib, - uint16_t cscn, uint16_t ep, - int length, uint8_t* data) + static int usb_audio_ep_control(HeadsetState* s, uint8_t attrib, uint16_t cscn, uint16_t ep, int length, uint8_t* data) { uint8_t cs = cscn >> 8; [[maybe_unused]] const uint8_t cn = cscn - 1; /* -1 for the non-present master control */ @@ -701,10 +679,9 @@ namespace usb_mic return ret; } - static void headset_handle_control(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + static void headset_handle_control(USBDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) { - HeadsetState* s = (HeadsetState*)dev; + HeadsetState* s = USB_CONTAINER_OF(dev, HeadsetState, dev); int ret = 0; @@ -723,8 +700,7 @@ namespace usb_mic case ClassInterfaceRequest | AUDIO_REQUEST_GET_MIN: case ClassInterfaceRequest | AUDIO_REQUEST_GET_MAX: case ClassInterfaceRequest | AUDIO_REQUEST_GET_RES: - ret = usb_audio_get_control(s, request & 0xff, value, index, - length, data); + ret = usb_audio_get_control(s, request & 0xff, value, index, length, data); if (ret < 0) { //if (s->debug) { @@ -739,8 +715,7 @@ namespace usb_mic case ClassInterfaceOutRequest | AUDIO_REQUEST_SET_MIN: case ClassInterfaceOutRequest | AUDIO_REQUEST_SET_MAX: case ClassInterfaceOutRequest | AUDIO_REQUEST_SET_RES: - ret = usb_audio_set_control(s, request & 0xff, value, index, - length, data); + ret = usb_audio_set_control(s, request & 0xff, value, index, length, data); if (ret < 0) { //if (s->debug) { @@ -758,8 +733,7 @@ namespace usb_mic case ClassEndpointOutRequest | AUDIO_REQUEST_SET_MIN: case ClassEndpointOutRequest | AUDIO_REQUEST_SET_MAX: case ClassEndpointOutRequest | AUDIO_REQUEST_SET_RES: - ret = usb_audio_ep_control(s, request & 0xff, value, index, - length, data); + ret = usb_audio_ep_control(s, request & 0xff, value, index, length, data); if (ret < 0) goto fail; break; @@ -779,7 +753,7 @@ namespace usb_mic static void headset_handle_data(USBDevice* dev, USBPacket* p) { - HeadsetState* s = (HeadsetState*)dev; + HeadsetState* s = USB_CONTAINER_OF(dev, HeadsetState, dev); int ret = USB_RET_STALL; uint8_t devep = p->ep->nr; @@ -808,7 +782,7 @@ namespace usb_mic if (s->audsrc->GetFrames(&frames)) { - frames = MIN(frames, maxFrames); + frames = std::min(frames, maxFrames); s->in_buffer.resize(frames * inChns); frames = s->audsrc->GetBuffer(s->in_buffer.data(), frames); } @@ -913,7 +887,7 @@ namespace usb_mic static void headset_handle_destroy(USBDevice* dev) { - HeadsetState* s = (HeadsetState*)dev; + HeadsetState* s = USB_CONTAINER_OF(dev, HeadsetState, dev); if (file) fclose(file); file = NULL; @@ -923,40 +897,33 @@ namespace usb_mic if (s->audsrc) { s->audsrc->Stop(); - delete s->audsrc; - s->audsrc = NULL; + s->audsrc.reset(); s->in_buffer.clear(); } if (s->audsink) { s->audsink->Stop(); - delete s->audsink; - s->audsink = NULL; + s->audsink.reset(); s->out_buffer.clear(); } - s->audsrcproxy->AudioDeinit(); delete s; } static int headset_handle_open(USBDevice* dev) { - HeadsetState* s = (HeadsetState*)dev; + HeadsetState* s = USB_CONTAINER_OF(dev, HeadsetState, dev); if (s) { - if (s->audsrc) - s->audsrc->Start(); - if (s->audsink) - s->audsink->Start(); } return 0; } static void headset_handle_close(USBDevice* dev) { - HeadsetState* s = (HeadsetState*)dev; + HeadsetState* s = USB_CONTAINER_OF(dev, HeadsetState, dev); if (s) { if (s->audsrc) @@ -967,40 +934,25 @@ namespace usb_mic } } - USBDevice* HeadsetDevice::CreateDevice(int port) + USBDevice* HeadsetDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { - std::string api; -#ifdef _WIN32 - std::wstring tmp; - if (!LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp)) - return nullptr; - api = wstr_to_str(tmp); -#else - if (!LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, api)) - return nullptr; -#endif - return HeadsetDevice::CreateDevice(port, api); - } + HeadsetState* s = new HeadsetState(); - USBDevice* HeadsetDevice::CreateDevice(int port, const std::string& api) - { - HeadsetState* s; - AudioDeviceInfo info; + std::string input_devname(USB::GetConfigString(si, port, TypeName(), "input_device")); + std::string output_devname(USB::GetConfigString(si, port, TypeName(), "output_device")); + const s32 input_latency = USB::GetConfigInt(si, port, TypeName(), "input_latency", AudioDevice::DEFAULT_LATENCY); + const s32 output_latency = USB::GetConfigInt(si, port, TypeName(), "output_latency", AudioDevice::DEFAULT_LATENCY); - s = new HeadsetState(); + if (!input_devname.empty()) + s->audsrc = AudioDevice::CreateDevice(port, AUDIODIR_SOURCE, 1, std::move(input_devname), input_latency); + else + s->audsrc = AudioDevice::CreateNoopDevice(port, AUDIODIR_SOURCE, 1); - s->audsrcproxy = RegisterAudioDevice::instance().Proxy(api); - if (!s->audsrcproxy) - { - Console.WriteLn("headset: Invalid audio API: '%s'\n", api.c_str()); - delete s; - return NULL; - } + if (!output_devname.empty()) + s->audsink = AudioDevice::CreateDevice(port, AUDIODIR_SINK, 2, std::move(output_devname), output_latency); + else + s->audsink = AudioDevice::CreateNoopDevice(port, AUDIODIR_SINK, 2); - s->audsrcproxy->AudioInit(); - - s->audsrc = s->audsrcproxy->CreateObject(port, TypeName(), 0, AUDIODIR_SOURCE); - s->audsink = s->audsrcproxy->CreateObject(port, TypeName(), 0, AUDIODIR_SINK); s->f.mode = MIC_MODE_SINGLE; if (!s->audsrc || !s->audsink) @@ -1009,6 +961,9 @@ namespace usb_mic s->in_buffer.reserve(BUFFER_FRAMES * s->audsrc->GetChannels()); s->out_buffer.reserve(BUFFER_FRAMES * s->audsink->GetChannels()); + s->audsrc->Start(); + s->audsink->Start(); + s->desc.full = &s->desc_dev; s->desc.str = desc_strings; if (usb_desc_parse_dev(headset_dev_descriptor, sizeof(headset_dev_descriptor), s->desc, s->desc_dev) < 0) @@ -1022,60 +977,82 @@ namespace usb_mic s->dev.klass.handle_control = headset_handle_control; s->dev.klass.handle_data = headset_handle_data; s->dev.klass.unrealize = headset_handle_destroy; - s->dev.klass.open = headset_handle_open; - s->dev.klass.close = headset_handle_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = desc_strings[2]; // set defaults s->f.out.vol[0] = 240; /* 0 dB */ s->f.out.vol[1] = 240; /* 0 dB */ - s->f.in.vol = 240; /* 0 dB */ + s->f.in.vol = 240; /* 0 dB */ s->f.out.srate = 48000; s->f.in.srate = 48000; usb_desc_init(&s->dev); usb_ep_init(&s->dev); - headset_handle_reset((USBDevice*)s); + headset_handle_reset(&s->dev); - return (USBDevice*)s; + return &s->dev; fail: - headset_handle_destroy((USBDevice*)s); - return NULL; + headset_handle_destroy(&s->dev); + return nullptr; } - int HeadsetDevice::Configure(int port, const std::string& api, void* data) + const char* HeadsetDevice::TypeName() const { - auto proxy = RegisterAudioDevice::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; + return "headset"; } - int HeadsetDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* HeadsetDevice::Name() const { - HeadsetState* s = (HeadsetState*)dev; - if (!s) - return 0; - switch (mode) + return "Logitech USB Headset"; + } + + bool HeadsetDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + HeadsetState* s = USB_CONTAINER_OF(dev, HeadsetState, dev); + if (!sw.DoMarker("HeadsetDevice")) + return false; + + sw.Do(&s->f.intf); + sw.Do(&s->f.mode); + sw.Do(&s->f.out.mute); + sw.DoPODArray(s->f.out.vol, std::size(s->f.out.vol)); + sw.Do(&s->f.out.srate); + sw.Do(&s->f.in.mute); + sw.Do(&s->f.in.vol); + sw.Do(&s->f.in.srate); + sw.Do(&s->f.mixer.mute); + sw.DoPODArray(s->f.mixer.vol, std::size(s->f.mixer.vol)); + + if (sw.IsReading() && !sw.HasError()) { - case FreezeAction::Load: - s->f = *(HeadsetState::freeze*)data; - if (s->audsrc) - s->audsrc->SetResampling(s->f.in.srate); - if (s->audsink) - s->audsink->SetResampling(s->f.out.srate); - return sizeof(HeadsetState::freeze); - case FreezeAction::Save: - *(HeadsetState::freeze*)data = s->f; - return sizeof(HeadsetState::freeze); - case FreezeAction::Size: - return sizeof(HeadsetState::freeze); - default: - break; + if (s->audsrc) + s->audsrc->SetResampling(s->f.in.srate); + if (s->audsink) + s->audsink->SetResampling(s->f.out.srate); } - return 0; + + return !sw.HasError(); } + void HeadsetDevice::UpdateSettings(USBDevice* dev, SettingsInterface& si) const + { + // TODO: Update device + } + + gsl::span HeadsetDevice::Settings(u32 subtype) const + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::StringList, "input_device_name", "Input Device", "Selects the device to read audio from.", "", nullptr, + nullptr, nullptr, nullptr, nullptr, &AudioDevice::GetInputDeviceList}, + {SettingInfo::Type::StringList, "output_device_name", "Output Device", "Selects the device to output audio to.", "", nullptr, + nullptr, nullptr, nullptr, nullptr, &AudioDevice::GetOutputDeviceList}, + {SettingInfo::Type::Integer, "input_latency", "Input Latency", "Specifies the latency to the host input device.", AudioDevice::DEFAULT_LATENCY_STR, "0", + "1000", "1", "%dms", nullptr, nullptr, 1.0f}, + {SettingInfo::Type::Integer, "output_latency", "Output Latency", "Specifies the latency to the host output device.", AudioDevice::DEFAULT_LATENCY_STR, "0", + "1000", "1", "%dms", nullptr, nullptr, 1.0f}, + }; + return info; + } } // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/usb-headset.h b/pcsx2/USB/usb-mic/usb-headset.h index 310ae5e8df..73deea5384 100644 --- a/pcsx2/USB/usb-mic/usb-headset.h +++ b/pcsx2/USB/usb-mic/usb-headset.h @@ -14,33 +14,19 @@ */ #include "USB/deviceproxy.h" -#include "audiodeviceproxy.h" namespace usb_mic { - - class HeadsetDevice + class HeadsetDevice final : public DeviceProxy { public: - virtual ~HeadsetDevice() {} - static USBDevice* CreateDevice(int port); - static USBDevice* CreateDevice(int port, const std::string& api); - static const char* TypeName() - { - return "headset"; - } - static const TCHAR* Name() - { - return TEXT("Logitech USB Headset"); - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* TypeName() const override; + const char* Name() const override; + + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override; + gsl::span Settings(u32 subtype) const override; }; } // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/usb-mic-logitech.cpp b/pcsx2/USB/usb-mic/usb-mic-logitech.cpp index 1b8eff2b6f..eb90498369 100644 --- a/pcsx2/USB/usb-mic/usb-mic-logitech.cpp +++ b/pcsx2/USB/usb-mic/usb-mic-logitech.cpp @@ -17,11 +17,10 @@ #include "usb-mic-singstar.h" #include "audio.h" #include "USB/qemu-usb/desc.h" -#include "USB/shared/inifile_usb.h" +#include "USB/qemu-usb/USBinternal.h" namespace usb_mic { - static const uint8_t logitech_mic_dev_descriptor[] = { /* bLength */ 0x12, //(18) /* bDescriptorType */ 0x01, //(1) @@ -232,32 +231,21 @@ namespace usb_mic }; //Minified state - typedef struct SINGSTARMICMINIState + struct SINGSTARMICMINIState { USBDevice dev; USBDesc desc; USBDescDevice desc_dev; - } SINGSTARMICMINIState; + }; - USBDevice* LogitechMicDevice::CreateDevice(int port) + USBDevice* LogitechMicDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { - std::string api; -#ifdef _WIN32 - std::wstring tmp; - if (!LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp)) - return nullptr; - api = wstr_to_str(tmp); -#else - if (!LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, api)) - return nullptr; -#endif - - USBDevice* dev = SingstarDevice::CreateDevice(port, api); + USBDevice* dev = SingstarDevice::CreateDevice(si, port, subtype, false, LogitechMicDevice::TypeName()); if (!dev) return nullptr; - SINGSTARMICMINIState* s = (SINGSTARMICMINIState*)dev; + SINGSTARMICMINIState* s = USB_CONTAINER_OF(dev, SINGSTARMICMINIState, dev); s->desc = {}; s->desc_dev = {}; diff --git a/pcsx2/USB/usb-mic/usb-mic-singstar.cpp b/pcsx2/USB/usb-mic/usb-mic-singstar.cpp index c6ac877fdf..d54571539d 100644 --- a/pcsx2/USB/usb-mic/usb-mic-singstar.cpp +++ b/pcsx2/USB/usb-mic/usb-mic-singstar.cpp @@ -25,16 +25,19 @@ // Most stuff is based on Qemu 1.7 USB soundcard passthrough code. #include "PrecompiledHeader.h" -#include "USB/qemu-usb/vl.h" +#include "USB/qemu-usb/qusb.h" #include "USB/qemu-usb/desc.h" -#include "usb-mic-singstar.h" -#include "USB/shared/inifile_usb.h" -#include +#include "USB/qemu-usb/USBinternal.h" +#include "USB/usb-mic/usb-mic-singstar.h" +#include "USB/usb-mic/audiodev.h" +#include "USB/usb-mic/audio.h" +#include "USB/USB.h" +#include "Host.h" +#include "StateWrapper.h" +#include "fmt/format.h" static FILE* file = NULL; -#include "audio.h" - #define BUFFER_FRAMES 200 /* @@ -58,18 +61,17 @@ namespace usb_mic enum usb_audio_altset : int8_t { ALTSET_OFF = 0x00, /* No endpoint */ - ALTSET_ON = 0x01, /* Single endpoint */ + ALTSET_ON = 0x01, /* Single endpoint */ }; - typedef struct SINGSTARMICState + struct SINGSTARMICState { USBDevice dev; USBDesc desc; USBDescDevice desc_dev; - AudioDevice* audsrc[2]; - AudioDeviceProxyBase* audsrcproxy; + std::unique_ptr audsrc[2]; struct freeze { @@ -81,13 +83,13 @@ namespace usb_mic bool mute; uint8_t vol[2]; uint32_t srate[2]; //TODO can it have different rates? - } f; //freezable + } f; //freezable /* properties */ uint32_t debug; std::vector buffer[2]; //uint8_t fifo[2][200]; //on-chip 400byte fifo - } SINGSTARMICState; + }; static const USBDescStrings desc_strings = { "", @@ -98,203 +100,203 @@ namespace usb_mic /* descriptor dumped from a real singstar MIC adapter */ static const uint8_t singstar_mic_dev_descriptor[] = { - /* bLength */ 0x12, //(18) - /* bDescriptorType */ 0x01, //(1) + /* bLength */ 0x12, //(18) + /* bDescriptorType */ 0x01, //(1) /* bcdUSB */ WBVAL(0x0110), //(272) - /* bDeviceClass */ 0x00, //(0) - /* bDeviceSubClass */ 0x00, //(0) - /* bDeviceProtocol */ 0x00, //(0) - /* bMaxPacketSize0 */ 0x08, //(8) + /* bDeviceClass */ 0x00, //(0) + /* bDeviceSubClass */ 0x00, //(0) + /* bDeviceProtocol */ 0x00, //(0) + /* bMaxPacketSize0 */ 0x08, //(8) /* idVendor */ WBVAL(0x1415), //(5141) /* idProduct */ WBVAL(0x0000), //(0) /* bcdDevice */ WBVAL(0x0001), //(1) - /* iManufacturer */ 0x01, //(1) - /* iProduct */ 0x02, //(2) - /* iSerialNumber */ 0x00, //(0) unused - /* bNumConfigurations */ 0x01, //(1) + /* iManufacturer */ 0x01, //(1) + /* iProduct */ 0x02, //(2) + /* iSerialNumber */ 0x00, //(0) unused + /* bNumConfigurations */ 0x01, //(1) }; static const uint8_t singstar_mic_config_descriptor[] = { /* Configuration 1 */ - 0x09, /* bLength */ + 0x09, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ - WBVAL(0x00b1), /* wTotalLength */ - 0x02, /* bNumInterfaces */ - 0x01, /* bConfigurationValue */ - 0x00, /* iConfiguration */ - USB_CONFIG_BUS_POWERED, /* bmAttributes */ - USB_CONFIG_POWER_MA(90), /* bMaxPower */ + WBVAL(0x00b1), /* wTotalLength */ + 0x02, /* bNumInterfaces */ + 0x01, /* bConfigurationValue */ + 0x00, /* iConfiguration */ + USB_CONFIG_BUS_POWERED, /* bmAttributes */ + USB_CONFIG_POWER_MA(90), /* bMaxPower */ /* Interface 0, Alternate Setting 0, Audio Control */ - USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x00, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x00, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ - AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + 0x00, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ + AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Control Interface */ AUDIO_CONTROL_INTERFACE_DESC_SZ(1), /* bLength */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ - WBVAL(0x0100), /* 1.00 */ /* bcdADC */ - WBVAL(0x0028), /* wTotalLength */ - 0x01, /* bInCollection */ - 0x01, /* baInterfaceNr */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ + WBVAL(0x0100), /* 1.00 */ /* bcdADC */ + WBVAL(0x0028), /* wTotalLength */ + 0x01, /* bInCollection */ + 0x01, /* baInterfaceNr */ /* Audio Input Terminal */ - AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ - 0x01, /* bTerminalID */ - WBVAL(AUDIO_TERMINAL_MICROPHONE), /* wTerminalType */ - 0x02, /* bAssocTerminal */ - 0x02, /* bNrChannels */ + AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ + 0x01, /* bTerminalID */ + WBVAL(AUDIO_TERMINAL_MICROPHONE), /* wTerminalType */ + 0x02, /* bAssocTerminal */ + 0x02, /* bNrChannels */ WBVAL(AUDIO_CHANNEL_L | AUDIO_CHANNEL_R), /* wChannelConfig */ - 0x00, /* iChannelNames */ - 0x00, /* iTerminal */ + 0x00, /* iChannelNames */ + 0x00, /* iTerminal */ /* Audio Output Terminal */ - AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ - 0x02, /* bTerminalID */ + AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ + 0x02, /* bTerminalID */ WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ - 0x01, /* bAssocTerminal */ - 0x03, /* bSourceID */ - 0x00, /* iTerminal */ + 0x01, /* bAssocTerminal */ + 0x03, /* bSourceID */ + 0x00, /* iTerminal */ /* Audio Feature Unit */ AUDIO_FEATURE_UNIT_DESC_SZ(2, 1), /* bLength */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ - 0x03, /* bUnitID */ - 0x01, /* bSourceID */ - 0x01, /* bControlSize */ - 0x01, /* bmaControls(0) */ - 0x02, /* bmaControls(1) */ - 0x02, /* bmaControls(2) */ - 0x00, /* iTerminal */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ + 0x03, /* bUnitID */ + 0x01, /* bSourceID */ + 0x01, /* bControlSize */ + 0x01, /* bmaControls(0) */ + 0x02, /* bmaControls(1) */ + 0x02, /* bmaControls(2) */ + 0x00, /* iTerminal */ /* Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith */ - USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x01, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x00, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x01, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Interface 1, Alternate Setting 1, Audio Streaming - Operational */ - USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x01, /* bInterfaceNumber */ - 0x01, /* bAlternateSetting */ - 0x01, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x01, /* bInterfaceNumber */ + 0x01, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Streaming Interface */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ - 0x02, /* bTerminalLink */ - 0x01, /* bDelay */ - WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x02, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ /* Audio Type I Format */ - AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ - AUDIO_FORMAT_TYPE_I, /* bFormatType */ - 0x01, /* bNrChannels */ - 0x02, /* bSubFrameSize */ - 0x10, /* bBitResolution */ - 0x05, /* bSamFreqType */ - B3VAL(8000), /* tSamFreq 1 */ - B3VAL(11025), /* tSamFreq 2 */ - B3VAL(22050), /* tSamFreq 3 */ - B3VAL(44100), /* tSamFreq 4 */ - B3VAL(48000), /* tSamFreq 5 */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x01, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ /* Endpoint - Standard Descriptor */ - AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ - USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - USB_ENDPOINT_IN(1), /* bEndpointAddress */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_IN(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ASYNCHRONOUS, /* bmAttributes */ - WBVAL(0x0064), /* wMaxPacketSize */ - 0x01, /* bInterval */ - 0x00, /* bRefresh */ - 0x00, /* bSynchAddress */ + WBVAL(0x0064), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ /* Endpoint - Audio Streaming */ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ - AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ - 0x01, /* bmAttributes */ - 0x00, /* bLockDelayUnits */ - WBVAL(0x0000), /* wLockDelay */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ /* Interface 1, Alternate Setting 2, Audio Streaming - ? */ - USB_INTERFACE_DESC_SIZE, /* bLength */ + USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - 0x01, /* bInterfaceNumber */ - 0x02, /* bAlternateSetting */ - 0x01, /* bNumEndpoints */ - USB_CLASS_AUDIO, /* bInterfaceClass */ + 0x01, /* bInterfaceNumber */ + 0x02, /* bAlternateSetting */ + 0x01, /* bNumEndpoints */ + USB_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ - AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ - 0x00, /* iInterface */ + AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ + 0x00, /* iInterface */ /* Audio Streaming Interface */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ - AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ - 0x02, /* bTerminalLink */ - 0x01, /* bDelay */ - WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ + AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ + 0x02, /* bTerminalLink */ + 0x01, /* bDelay */ + WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ /* Audio Type I Format */ - AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength */ + AUDIO_FORMAT_TYPE_I_DESC_SZ(5), /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ - AUDIO_FORMAT_TYPE_I, /* bFormatType */ - 0x02, /* bNrChannels */ - 0x02, /* bSubFrameSize */ - 0x10, /* bBitResolution */ - 0x05, /* bSamFreqType */ - B3VAL(8000), /* tSamFreq 1 */ - B3VAL(11025), /* tSamFreq 2 */ - B3VAL(22050), /* tSamFreq 3 */ - B3VAL(44100), /* tSamFreq 4 */ - B3VAL(48000), /* tSamFreq 5 */ + AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ + AUDIO_FORMAT_TYPE_I, /* bFormatType */ + 0x02, /* bNrChannels */ + 0x02, /* bSubFrameSize */ + 0x10, /* bBitResolution */ + 0x05, /* bSamFreqType */ + B3VAL(8000), /* tSamFreq 1 */ + B3VAL(11025), /* tSamFreq 2 */ + B3VAL(22050), /* tSamFreq 3 */ + B3VAL(44100), /* tSamFreq 4 */ + B3VAL(48000), /* tSamFreq 5 */ /* Endpoint - Standard Descriptor */ - AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ - USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - USB_ENDPOINT_IN(1), /* bEndpointAddress */ + AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + USB_ENDPOINT_IN(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ASYNCHRONOUS, /* bmAttributes */ - WBVAL(0x00c8), /* wMaxPacketSize */ - 0x01, /* bInterval */ - 0x00, /* bRefresh */ - 0x00, /* bSynchAddress */ + WBVAL(0x00c8), /* wMaxPacketSize */ + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ /* Endpoint - Audio Streaming */ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ - AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ - AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ - 0x01, /* bmAttributes */ - 0x00, /* bLockDelayUnits */ - WBVAL(0x0000), /* wLockDelay */ + AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ + 0x01, /* bmAttributes */ + 0x00, /* bLockDelayUnits */ + WBVAL(0x0000), /* wLockDelay */ /* Terminator */ 0 /* bLength */ @@ -310,14 +312,11 @@ namespace usb_mic /* * Note: we arbitrarily map the volume control range onto -inf..+8 dB */ -#define ATTRIB_ID(cs, attrib, idif) \ - (((cs) << 24) | ((attrib) << 16) | (idif)) +#define ATTRIB_ID(cs, attrib, idif) (((cs) << 24) | ((attrib) << 16) | (idif)) //0x0300 - feature bUnitID 0x03 - static int usb_audio_get_control(SINGSTARMICState* s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t* data) + static int usb_audio_get_control(SINGSTARMICState* s, uint8_t attrib, uint16_t cscn, uint16_t idif, int length, uint8_t* data) { uint8_t cs = cscn >> 8; uint8_t cn = cscn - 1; /* -1 for the non-present master control */ @@ -375,9 +374,7 @@ namespace usb_mic return ret; } - static int usb_audio_set_control(SINGSTARMICState* s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t* data) + static int usb_audio_set_control(SINGSTARMICState* s, uint8_t attrib, uint16_t cscn, uint16_t idif, int length, uint8_t* data) { uint8_t cs = cscn >> 8; uint8_t cn = cscn - 1; /* -1 for the non-present master control */ @@ -424,9 +421,7 @@ namespace usb_mic return ret; } - static int usb_audio_ep_control(SINGSTARMICState* s, uint8_t attrib, - uint16_t cscn, uint16_t ep, - int length, uint8_t* data) + static int usb_audio_ep_control(SINGSTARMICState* s, uint8_t attrib, uint16_t cscn, uint16_t ep, int length, uint8_t* data) { uint8_t cs = cscn >> 8; uint8_t cn = cscn - 1; /* -1 for the non-present master control */ @@ -452,7 +447,6 @@ namespace usb_mic if (s->audsrc[1]) s->audsrc[1]->SetResampling(s->f.srate[1]); - } else if (cn < 2) { @@ -474,10 +468,9 @@ namespace usb_mic return ret; } - static void singstar_mic_set_interface(USBDevice* dev, int intf, - int alt_old, int alt_new) + static void singstar_mic_set_interface(USBDevice* dev, int intf, int alt_old, int alt_new) { - SINGSTARMICState* s = (SINGSTARMICState*)dev; + SINGSTARMICState* s = USB_CONTAINER_OF(dev, SINGSTARMICState, dev); s->f.intf = alt_new; #if defined(_DEBUG) /* close previous debug audio output file */ @@ -489,10 +482,9 @@ namespace usb_mic #endif } - static void singstar_mic_handle_control(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + static void singstar_mic_handle_control(USBDevice* dev, USBPacket* p, int request, int value, int index, int length, uint8_t* data) { - SINGSTARMICState* s = (SINGSTARMICState*)dev; + SINGSTARMICState* s = USB_CONTAINER_OF(dev, SINGSTARMICState, dev); int ret = 0; @@ -511,8 +503,7 @@ namespace usb_mic case ClassInterfaceRequest | AUDIO_REQUEST_GET_MIN: case ClassInterfaceRequest | AUDIO_REQUEST_GET_MAX: case ClassInterfaceRequest | AUDIO_REQUEST_GET_RES: - ret = usb_audio_get_control(s, request & 0xff, value, index, - length, data); + ret = usb_audio_get_control(s, request & 0xff, value, index, length, data); if (ret < 0) { //if (s->debug) { @@ -527,8 +518,7 @@ namespace usb_mic case ClassInterfaceOutRequest | AUDIO_REQUEST_SET_MIN: case ClassInterfaceOutRequest | AUDIO_REQUEST_SET_MAX: case ClassInterfaceOutRequest | AUDIO_REQUEST_SET_RES: - ret = usb_audio_set_control(s, request & 0xff, value, index, - length, data); + ret = usb_audio_set_control(s, request & 0xff, value, index, length, data); if (ret < 0) { //if (s->debug) { @@ -546,8 +536,7 @@ namespace usb_mic case ClassEndpointOutRequest | AUDIO_REQUEST_SET_MIN: case ClassEndpointOutRequest | AUDIO_REQUEST_SET_MAX: case ClassEndpointOutRequest | AUDIO_REQUEST_SET_RES: - ret = usb_audio_ep_control(s, request & 0xff, value, index, - length, data); + ret = usb_audio_ep_control(s, request & 0xff, value, index, length, data); if (ret < 0) goto fail; break; @@ -567,7 +556,7 @@ namespace usb_mic static void singstar_mic_handle_data(USBDevice* dev, USBPacket* p) { - SINGSTARMICState* s = (SINGSTARMICState*)dev; + SINGSTARMICState* s = USB_CONTAINER_OF(dev, SINGSTARMICState, dev); int ret = 0; uint8_t devep = p->ep->nr; @@ -606,10 +595,9 @@ namespace usb_mic for (int i = 0; i < 2; i++) { frames = max_frames; - if (s->audsrc[i] && - s->audsrc[i]->GetFrames(&frames)) + if (s->audsrc[i] && s->audsrc[i]->GetFrames(&frames)) { - frames = MIN(max_frames, frames); //max 50 frames usually + frames = std::min(max_frames, frames); //max 50 frames usually out_frames[i] = s->audsrc[i]->GetBuffer(s->buffer[i].data(), frames); } } @@ -669,7 +657,7 @@ namespace usb_mic { uint32_t cn1 = s->audsrc[0]->GetChannels(); uint32_t cn2 = s->audsrc[1]->GetChannels(); - uint32_t minLen = MIN(out_frames[0], out_frames[1]); + uint32_t minLen = std::min(out_frames[0], out_frames[1]); src1 = s->buffer[0].data(); src2 = s->buffer[1].data(); @@ -721,7 +709,7 @@ namespace usb_mic static void singstar_mic_handle_destroy(USBDevice* dev) { - SINGSTARMICState* s = (SINGSTARMICState*)dev; + SINGSTARMICState* s = USB_CONTAINER_OF(dev, SINGSTARMICState, dev); if (file) fclose(file); file = NULL; @@ -733,81 +721,53 @@ namespace usb_mic if (s->audsrc[i]) { s->audsrc[i]->Stop(); - delete s->audsrc[i]; - s->audsrc[i] = NULL; + s->audsrc[i].reset(); s->buffer[i].clear(); } } - s->audsrcproxy->AudioDeinit(); + delete s; } - static int singstar_mic_handle_open(USBDevice* dev) + USBDevice* SingstarDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { - SINGSTARMICState* s = (SINGSTARMICState*)dev; - if (s) - { - for (int i = 0; i < 2; i++) - if (s->audsrc[i]) - s->audsrc[i]->Start(); - } - return 0; + return CreateDevice(si, port, subtype, true, SingstarDevice::TypeName()); } - static void singstar_mic_handle_close(USBDevice* dev) + USBDevice* SingstarDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype, bool dual_mic, const char* devtype) const { - SINGSTARMICState* s = (SINGSTARMICState*)dev; - if (s) + SINGSTARMICState* s = new SINGSTARMICState(); + + if (dual_mic) { - for (int i = 0; i < 2; i++) - if (s->audsrc[i]) - s->audsrc[i]->Stop(); + std::string dev0(USB::GetConfigString(si, port, devtype, "player1_device_name")); + std::string dev1(USB::GetConfigString(si, port, devtype, "player2_device_name")); + const s32 latency = USB::GetConfigInt(si, port, devtype, "input_latency", AudioDevice::DEFAULT_LATENCY); + + if (!dev0.empty()) + s->audsrc[0] = AudioDevice::CreateDevice(port, AUDIODIR_SOURCE, 1, std::move(dev0), latency); + if (!dev1.empty()) + s->audsrc[1] = AudioDevice::CreateDevice(port, AUDIODIR_SOURCE, 1, std::move(dev1), latency); } - } - - //USBDevice *singstar_mic_init(int port, TSTDSTRING *devs) - USBDevice* SingstarDevice::CreateDevice(int port) - { - std::string api; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, SingstarDevice::TypeName(), N_DEVICE_API, tmp); - api = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, SingstarDevice::TypeName(), N_DEVICE_API, api); -#endif - return SingstarDevice::CreateDevice(port, api); - } - USBDevice* SingstarDevice::CreateDevice(int port, const std::string& api) - { - SINGSTARMICState* s; - AudioDeviceInfo info; - - s = new SINGSTARMICState(); - - s->audsrcproxy = RegisterAudioDevice::instance().Proxy(api); - if (!s->audsrcproxy) + else { - Console.WriteLn("singstar: Invalid audio API: '%s'\n", api.c_str()); - delete s; - return NULL; + std::string dev0(USB::GetConfigString(si, port, devtype, "input_device_name")); + const s32 latency0 = USB::GetConfigInt(si, port, devtype, "input_latency", AudioDevice::DEFAULT_LATENCY); + if (!dev0.empty()) + s->audsrc[0] = AudioDevice::CreateDevice(port, AUDIODIR_SOURCE, 1, std::move(dev0), latency0); } - s->audsrcproxy->AudioInit(); - - s->audsrc[0] = s->audsrcproxy->CreateObject(port, TypeName(), 0, AUDIODIR_SOURCE); - s->audsrc[1] = s->audsrcproxy->CreateObject(port, TypeName(), 1, AUDIODIR_SOURCE); - if (!s->audsrc[0] && !s->audsrc[1]) + { + Host::AddOSDMessage("USB-Mic: Neither player 1 nor 2 is connected.", Host::OSD_ERROR_DURATION); goto fail; + } - if (s->audsrc[0] && s->audsrc[1] && s->audsrc[0]->Compare(s->audsrc[1])) + if (s->audsrc[0] && s->audsrc[1] && s->audsrc[0]->Compare(s->audsrc[1].get())) { s->f.mode = MIC_MODE_SHARED; // And don't capture the same source twice - s->audsrc[1]->Stop(); - delete s->audsrc[1]; - s->audsrc[1] = nullptr; + s->audsrc[1].reset(); } else if (!s->audsrc[0] || !s->audsrc[1]) s->f.mode = MIC_MODE_SINGLE; @@ -815,8 +775,17 @@ namespace usb_mic s->f.mode = MIC_MODE_SEPARATE; for (int i = 0; i < 2; i++) + { if (s->audsrc[i]) + { s->buffer[i].resize(BUFFER_FRAMES * s->audsrc[i]->GetChannels()); + if (!s->audsrc[i]->Start()) + { + Host::AddOSDMessage(fmt::format("USB-Mic: Failed to start player {} audio stream.", i + 1), Host::OSD_ERROR_DURATION); + goto fail; + } + } + } s->desc.full = &s->desc_dev; s->desc.str = desc_strings; @@ -832,8 +801,6 @@ namespace usb_mic s->dev.klass.handle_data = singstar_mic_handle_data; s->dev.klass.set_interface = singstar_mic_set_interface; s->dev.klass.unrealize = singstar_mic_handle_destroy; - s->dev.klass.open = singstar_mic_handle_open; - s->dev.klass.close = singstar_mic_handle_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = desc_strings[2]; @@ -845,46 +812,131 @@ namespace usb_mic usb_desc_init(&s->dev); usb_ep_init(&s->dev); - singstar_mic_handle_reset((USBDevice*)s); + singstar_mic_handle_reset(&s->dev); - return (USBDevice*)s; + return &s->dev; fail: - singstar_mic_handle_destroy((USBDevice*)s); - return NULL; + singstar_mic_handle_destroy(&s->dev); + return nullptr; } - int SingstarDevice::Configure(int port, const std::string& api, void* data) + const char* SingstarDevice::Name() const { - auto proxy = RegisterAudioDevice::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; + return "Singstar"; } - int SingstarDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* SingstarDevice::TypeName() const { - SINGSTARMICState* s = (SINGSTARMICState*)dev; - if (!s) - return 0; - switch (mode) + return "singstar"; + } + + bool SingstarDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + SINGSTARMICState* s = USB_CONTAINER_OF(dev, SINGSTARMICState, dev); + if (!sw.DoMarker("SINGSTARMICState")) + return false; + + sw.Do(&s->f.intf); + sw.Do(&s->f.mode); + sw.Do(&s->f.altset); + sw.Do(&s->f.mute); + sw.DoPODArray(&s->f.vol, std::size(s->f.vol)); + sw.DoPODArray(s->f.srate, std::size(s->f.srate)); + + if (sw.IsReading() && !sw.HasError()) { - case FreezeAction::Load: - s->f = *(SINGSTARMICState::freeze*)data; - if (s->audsrc[0]) - s->audsrc[0]->SetResampling(s->f.srate[0]); - if (s->audsrc[1]) - s->audsrc[1]->SetResampling(s->f.srate[1]); - return sizeof(SINGSTARMICState::freeze); - case FreezeAction::Save: - *(SINGSTARMICState::freeze*)data = s->f; - return sizeof(SINGSTARMICState::freeze); - case FreezeAction::Size: - return sizeof(SINGSTARMICState::freeze); - default: - break; + for (u32 i = 0; i < 2; i++) + { + if (s->audsrc[i]) + s->audsrc[i]->SetResampling(s->f.srate[i]); + } } - return 0; + + return !sw.HasError(); } + void SingstarDevice::UpdateSettings(USBDevice* dev, SettingsInterface& si) const + { + // TODO: Reload devices. + } + + gsl::span SingstarDevice::Settings(u32 subtype) const + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::StringList, "player1_device_name", "Player 1 Device", "Selects the input for the first player.", "", + nullptr, nullptr, nullptr, nullptr, nullptr, &AudioDevice::GetInputDeviceList}, + {SettingInfo::Type::StringList, "player2_device_name", "Player 2 Device", "Selects the input for the second player.", "", + nullptr, nullptr, nullptr, nullptr, nullptr, &AudioDevice::GetInputDeviceList}, + {SettingInfo::Type::Integer, "input_latency", "Input Latency", "Specifies the latency to the host input device.", + AudioDevice::DEFAULT_LATENCY_STR, "0", "1000", "1", "%dms", nullptr, nullptr, 1.0f}, + }; + return info; + } + + const char* LogitechMicDevice::TypeName() const + { + return "logitech_usbmic"; + } + + const char* LogitechMicDevice::Name() const + { + return "Logitech USB Mic"; + } + + gsl::span LogitechMicDevice::Settings(u32 subtype) const + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::StringList, "input_device_name", "Input Device", "Selects the device to read audio from.", "", nullptr, + nullptr, nullptr, nullptr, nullptr, &AudioDevice::GetInputDeviceList}, + {SettingInfo::Type::Integer, "input_latency", "Input Latency", "Specifies the latency to the host input device.", + AudioDevice::DEFAULT_LATENCY_STR, "0", "1000", "1", "%dms", nullptr, nullptr, 1.0f}, + }; + return info; + } } // namespace usb_mic + +#include "USB/usb-mic/audiodev-noop.h" + +std::unique_ptr AudioDevice::CreateNoopDevice(u32 port, AudioDir dir, u32 channels) +{ + return std::make_unique(port, dir, channels); +} + +#ifdef SPU2X_CUBEB +#include "USB/usb-mic/audiodev-cubeb.h" + +std::unique_ptr AudioDevice::CreateDevice(u32 port, AudioDir dir, u32 channels, std::string devname, s32 latency) +{ + return std::make_unique(port, dir, channels, std::move(devname), latency); +} + +std::vector> AudioDevice::GetInputDeviceList() +{ + return usb_mic::audiodev_cubeb::CubebAudioDevice::GetDeviceList(true); +} + +std::vector> AudioDevice::GetOutputDeviceList() +{ + return usb_mic::audiodev_cubeb::CubebAudioDevice::GetDeviceList(false); +} + +#else + +std::unique_ptr AudioDevice::CreateDevice(u32 port, AudioDir dir, u32 channels, std::string devname, s32 latency) +{ + Console.Warning("Cubeb is unavailable, creating a noop audio device."); + return CreateNoopDevice(port, dir, channels); +} + +std::vector AudioDevice::GetInputDeviceList() +{ + return {}; +} + +std::vector AudioDevice::GetOutputDeviceList() +{ + return {}; +} + +#endif \ No newline at end of file diff --git a/pcsx2/USB/usb-mic/usb-mic-singstar.h b/pcsx2/USB/usb-mic/usb-mic-singstar.h index 986ca72407..3a3664af95 100644 --- a/pcsx2/USB/usb-mic/usb-mic-singstar.h +++ b/pcsx2/USB/usb-mic/usb-mic-singstar.h @@ -13,62 +13,30 @@ * If not, see . */ -#ifndef USBMICSINGSTAR_H -#define USBMICSINGSTAR_H +#pragma once #include "USB/deviceproxy.h" -#include "audiodeviceproxy.h" - -struct USBDevice; +#include "USB/usb-mic/audiodev.h" namespace usb_mic { - class SingstarDevice + class SingstarDevice : public DeviceProxy { public: - virtual ~SingstarDevice() {} - static USBDevice* CreateDevice(int port); - static USBDevice* CreateDevice(int port, const std::string& api); - static const TCHAR* Name() - { - return TEXT("Singstar"); - } - static const char* TypeName() - { - return "singstar"; - } - static std::list ListAPIs() - { - return RegisterAudioDevice::instance().Names(); - } - static const TCHAR* LongAPIName(const std::string& name) - { - auto proxy = RegisterAudioDevice::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype, bool dual_mic, const char* devtype) const; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* Name() const override; + const char* TypeName() const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override; + gsl::span Settings(u32 subtype) const override; }; - class LogitechMicDevice : public SingstarDevice + class LogitechMicDevice final : public SingstarDevice { public: - virtual ~LogitechMicDevice() {} - static USBDevice* CreateDevice(int port); - static const char* TypeName() - { - return "logitech_usbmic"; - } - static const TCHAR* Name() - { - return TEXT("Logitech USB Mic"); - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* TypeName() const override; + const char* Name() const override; + gsl::span Settings(u32 subtype) const override; }; - } // namespace usb_mic -#endif diff --git a/pcsx2/USB/usb-msd/usb-msd-gtk.cpp b/pcsx2/USB/usb-msd/usb-msd-gtk.cpp deleted file mode 100644 index 171359db37..0000000000 --- a/pcsx2/USB/usb-msd/usb-msd-gtk.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "usb-msd.h" -#include "USB/linux/ini.h" -#include "USB/configuration.h" -#include "USB/gtk.h" -#include "common/Console.h" - -namespace usb_msd -{ - - static void entryChanged(GtkWidget* widget, gpointer data) - { -#ifndef NDEBUG - const gchar* text = gtk_entry_get_text(GTK_ENTRY(widget)); - Console.Warning("Entry text:%s\n", text); -#endif - } - - static void fileChooser(GtkWidget* widget, gpointer data) - { - GtkWidget *dialog, *entry = NULL; - - entry = (GtkWidget*)data; - dialog = gtk_file_chooser_dialog_new("Open File", - NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - - //XXX check access? Dialog seems to default to "Recently used" etc. - //Or set to empty string anyway? Then it seems to default to some sort of "working dir" - if (access(gtk_entry_get_text(GTK_ENTRY(entry)), F_OK) == 0) - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), gtk_entry_get_text(GTK_ENTRY(entry))); - - if (entry && gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) - { - char* filename; - - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - Console.Warning("%s\n", filename); - gtk_entry_set_text(GTK_ENTRY(entry), filename); - g_free(filename); - } - - gtk_widget_destroy(dialog); - } - - int MsdDevice::Configure(int port, const std::string& api, void* data) - { - GtkWidget *ro_frame, *ro_label, *rs_hbox, *vbox; - - GtkWidget* dlg = gtk_dialog_new_with_buttons( - "Mass Storage Settings", GTK_WINDOW(data), GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE); - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - - ro_frame = gtk_frame_new(NULL); - gtk_box_pack_start(GTK_BOX(dlg_area_box), ro_frame, TRUE, FALSE, 5); - - ro_label = gtk_label_new("Select USB image:"); - gtk_frame_set_label_widget(GTK_FRAME(ro_frame), ro_label); - gtk_label_set_use_markup(GTK_LABEL(ro_label), TRUE); - - vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(ro_frame), vbox); - - rs_hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), rs_hbox, FALSE, TRUE, 0); - - GtkWidget* entry = gtk_entry_new(); - gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_PATH); //TODO max length - - std::string var; - if (LoadSetting(TypeName(), port, APINAME, N_CONFIG_PATH, var)) - gtk_entry_set_text(GTK_ENTRY(entry), var.c_str()); - - g_signal_connect(entry, "changed", G_CALLBACK(entryChanged), NULL); - - GtkWidget* button = gtk_button_new_with_label("Browse"); - gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_icon_name("gtk-open", GTK_ICON_SIZE_BUTTON)); - g_signal_connect(button, "clicked", G_CALLBACK(fileChooser), entry); - - gtk_box_pack_start(GTK_BOX(rs_hbox), entry, TRUE, TRUE, 5); - gtk_box_pack_start(GTK_BOX(rs_hbox), button, FALSE, FALSE, 5); - - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - std::string path = gtk_entry_get_text(GTK_ENTRY(entry)); - gtk_widget_destroy(dlg); - - // Wait for all gtk events to be consumed ... - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); - - if (result == GTK_RESPONSE_OK) - { - if (SaveSetting(TypeName(), port, APINAME, N_CONFIG_PATH, path)) - return RESULT_OK; - else - return RESULT_FAILED; - } - - return RESULT_CANCELED; - } - -} // namespace usb_msd diff --git a/pcsx2/USB/usb-msd/usb-msd-win32.cpp b/pcsx2/USB/usb-msd/usb-msd-win32.cpp deleted file mode 100644 index 39e7e5647c..0000000000 --- a/pcsx2/USB/usb-msd/usb-msd-win32.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" - -#include "common/RedtapeWindows.h" - -#include - -#include "usb-msd.h" -#include "USB/Win32/Config_usb.h" -#include "USB/Win32/resource_usb.h" - -namespace usb_msd -{ - - static OPENFILENAMEW ofn; - BOOL CALLBACK MsdDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - int port; - static wchar_t buff[4096] = {0}; - - switch (uMsg) - { - case WM_INITDIALOG: - { - memset(buff, 0, sizeof(buff)); - port = (int)lParam; - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); - - std::wstring var; - if (LoadSetting(MsdDevice::TypeName(), port, APINAME, N_CONFIG_PATH, var)) - wcsncpy_s(buff, sizeof(buff), var.c_str(), countof(buff)); - SetWindowTextW(GetDlgItem(hW, IDC_EDIT1_USB), buff); - return TRUE; - } - case WM_CREATE: - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); - break; - case WM_COMMAND: - - if (HIWORD(wParam) == BN_CLICKED) - { - switch (LOWORD(wParam)) - { - case IDC_BUTTON1_USB: - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = hW; - ofn.lpstrTitle = L"USB image file"; - ofn.lpstrFile = buff; - ofn.nMaxFile = countof(buff); - ofn.lpstrFilter = L"All\0*.*\0"; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = NULL; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = NULL; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; - - if (GetOpenFileName(&ofn)) - { - SetWindowText(GetDlgItem(hW, IDC_EDIT1_USB), ofn.lpstrFile); - } - break; - case IDOK: - { - INT_PTR res = RESULT_OK; - GetWindowTextW(GetDlgItem(hW, IDC_EDIT1_USB), buff, countof(buff)); - port = (int)GetWindowLongPtr(hW, GWLP_USERDATA); - if (!SaveSetting(MsdDevice::TypeName(), port, APINAME, N_CONFIG_PATH, buff)) - res = RESULT_FAILED; - //strcpy_s(conf.usb_img, ofn.lpstrFile); - EndDialog(hW, res); - return TRUE; - } - case IDCANCEL: - EndDialog(hW, FALSE); - return TRUE; - } - } - } - return FALSE; - } - - int MsdDevice::Configure(int port, const std::string& api, void* data) - { - Win32Handles handles = *(Win32Handles*)data; - return DialogBoxParam(handles.hInst, - MAKEINTRESOURCE(IDD_DLGMSD_USB), - handles.hWnd, - (DLGPROC)MsdDlgProc, port); - } - -} // namespace usb_msd diff --git a/pcsx2/USB/usb-msd/usb-msd.cpp b/pcsx2/USB/usb-msd/usb-msd.cpp index 110248e8bd..61007b424f 100644 --- a/pcsx2/USB/usb-msd/usb-msd.cpp +++ b/pcsx2/USB/usb-msd/usb-msd.cpp @@ -8,12 +8,17 @@ */ #include "PrecompiledHeader.h" -#include -#include -#include -#include "USB/qemu-usb/vl.h" +#include +#include +#include +#include "USB/qemu-usb/qusb.h" #include "USB/qemu-usb/desc.h" -#include "usb-msd.h" +#include "USB/qemu-usb/USBinternal.h" +#include "USB/usb-msd/usb-msd.h" +#include "USB/USB.h" +#include "common/FileSystem.h" +#include "Host.h" +#include "StateWrapper.h" #define le32_to_cpu(x) (x) #define cpu_to_le32(x) (x) @@ -48,10 +53,10 @@ namespace usb_msd enum USBMSDMode : int8_t { - USB_MSDM_CBW, /* Command Block. */ + USB_MSDM_CBW, /* Command Block. */ USB_MSDM_DATAOUT, /* Tranfer data to device. */ - USB_MSDM_DATAIN, /* Transfer data from device. */ - USB_MSDM_CSW /* Command Status. */ + USB_MSDM_DATAIN, /* Transfer data from device. */ + USB_MSDM_CSW /* Command Status. */ }; typedef struct ReqState @@ -75,17 +80,18 @@ namespace usb_msd uint32_t file_op_tag; // read from file or buf int32_t result; - uint32_t off; //buffer offset + uint32_t off; //buffer offset uint8_t buf[4096]; //random length right now uint8_t sense_buf[18]; uint8_t last_cmd; ReqState req; //TODO how to detect if image is different - uint32_t hash; - } f; //freezable + uint64_t mtime; + } f = {}; //freezable - FILE* file; + FILE* file = 0; + int64_t file_size = 0; //char fn[MAX_PATH+1]; //TODO Could use with open/close, //but error recovery currently can't deal with file suddenly //becoming not accessible @@ -97,8 +103,8 @@ namespace usb_msd } MSDState; static const uint8_t qemu_msd_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ 0x10, 0x00, /* u16 bcdUSB; v1.0 */ 0x00, /* u8 bDeviceClass; */ @@ -114,24 +120,24 @@ namespace usb_msd 0x01, /* u8 iManufacturer; */ 0x02, /* u8 iProduct; */ 0x03, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ + 0x01 /* u8 bNumConfigurations; */ }; static const uint8_t qemu_msd_config_descriptor[] = { /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ 0x20, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xc0, /* u8 bmAttributes; Bit 7: must be set, 6: Self-powered, 5: Remote wakeup, 4..0: resvd */ - 0x00, /* u8 MaxPower; */ + 0x00, /* u8 MaxPower; */ /* one interface */ 0x09, /* u8 if_bLength; */ @@ -145,20 +151,20 @@ namespace usb_msd 0x00, /* u8 if_iInterface; */ /* Bulk-In endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x02, /* u8 ep_bmAttributes; Bulk */ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00, /* u8 ep_bInterval; */ + 0x00, /* u8 ep_bInterval; */ /* Bulk-Out endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ + 0x02, /* u8 ep_bmAttributes; Bulk */ 0x00, 0x02, /* u16 ep_wMaxPacketSize; */ - 0x00 /* u8 ep_bInterval; */ + 0x00 /* u8 ep_bInterval; */ }; enum @@ -381,11 +387,11 @@ namespace usb_msd NO_SENSE, 0x00, 0x00}; /* LUN not ready, Manual intervention required */ - [[maybe_unused]]const struct SCSISense sense_code_LUN_NOT_READY = { + [[maybe_unused]] const struct SCSISense sense_code_LUN_NOT_READY = { NOT_READY, 0x04, 0x03}; /* LUN not ready, Medium not present */ - [[maybe_unused]]const struct SCSISense sense_code_NO_MEDIUM = { + [[maybe_unused]] const struct SCSISense sense_code_NO_MEDIUM = { NOT_READY, 0x3a, 0x00}; const struct SCSISense sense_code_UNKNOWN_ERROR = { @@ -411,47 +417,25 @@ namespace usb_msd // .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 //}; - static int64_t get_file_size(FILE* file) - { - int fd; - -#if defined(_WIN32) - struct _stat64 buf; - fd = _fileno(file); - if (_fstat64(fd, &buf) != 0) - return -1; - return buf.st_size; -#elif defined(__GNUC__) - struct stat64 buf; - fd = fileno(file); - if (fstat64(fd, &buf) != 0) - return -1; - return buf.st_size; -#else -#error Unknown platform -#endif - } - static void usb_msd_handle_reset(USBDevice* dev) { - MSDState* s = (MSDState*)dev; + MSDState* s = USB_CONTAINER_OF(dev, MSDState, dev); s->f.mode = USB_MSDM_CBW; } #ifndef bswap32 -#define bswap32(x) ( \ - (((x) >> 24) & 0xff) | \ - (((x) >> 8) & 0xff00) | \ +#define bswap32(x) ( \ + (((x) >> 24) & 0xff) | \ + (((x) >> 8) & 0xff00) | \ (((x) << 8) & 0xff0000) | \ (((x) << 24) & 0xff000000)) #define bswap16(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00)) #endif - static void set_sense(void* opaque, SCSISense sense) + static void set_sense(MSDState* s, SCSISense sense) { - MSDState* s = (MSDState*)opaque; memset(s->f.sense_buf, 0, sizeof(s->f.sense_buf)); //SENSE request s->f.sense_buf[0] = 0x70 | 0x80; //0x70 - current sense, 0x80 - set Valid bit @@ -463,8 +447,8 @@ namespace usb_msd //s->f.sense_buf[5] = 0x00; //s->f.sense_buf[6] = 0x00; //LSB s->f.sense_buf[7] = sense.asc ? 0x0a : 0x00; //Additional sense length (10 bytes if any) - s->f.sense_buf[12] = sense.asc; //Additional sense code - s->f.sense_buf[13] = sense.ascq; //Additional sense code qualifier + s->f.sense_buf[12] = sense.asc; //Additional sense code + s->f.sense_buf[13] = sense.ascq; //Additional sense code qualifier } static void usb_msd_send_status(MSDState* s, USBPacket* p) @@ -472,7 +456,7 @@ namespace usb_msd size_t len; assert(s->f.csw.sig == cpu_to_le32(0x53425355)); - len = MIN(sizeof(s->f.csw), p->iov.size); + len = std::min(sizeof(s->f.csw), p->iov.size); usb_packet_copy(p, &s->f.csw, len); memset(&s->f.csw, 0, sizeof(s->f.csw)); } @@ -604,10 +588,8 @@ namespace usb_msd usb_msd_command_complete(s, s->f.result); } - static void send_command(void* opaque, struct usb_msd_cbw* cbw) + static void send_command(MSDState* s, struct usb_msd_cbw* cbw) { - MSDState* s = (MSDState*)opaque; - int64_t lba; uint32_t xfer_len; s->f.last_cmd = cbw->cmd[0]; @@ -627,29 +609,27 @@ namespace usb_msd break; case REQUEST_SENSE: //device shall keep old sense data memcpy(s->f.buf, s->f.sense_buf, - /* XXX the UFI device shall return only the number of bytes requested, as is */ - cbw->cmd[4] < sizeof(s->f.sense_buf) ? (size_t)cbw->cmd[4] : sizeof(s->f.sense_buf)); + /* XXX the UFI device shall return only the number of bytes requested, as is */ + cbw->cmd[4] < sizeof(s->f.sense_buf) ? (size_t)cbw->cmd[4] : sizeof(s->f.sense_buf)); break; case INQUIRY: memset(s->f.buf, 0, sizeof(s->f.buf)); - s->f.buf[0] = 0; //SCSI Peripheral Device Type: 0x0 - direct access device, 0x1f - unknown/no device + s->f.buf[0] = 0; //SCSI Peripheral Device Type: 0x0 - direct access device, 0x1f - unknown/no device s->f.buf[1] = 1 << 7; //removable - s->f.buf[3] = 1; //UFI response data format + s->f.buf[3] = 1; //UFI response data format //inq data len can be zero - strncpy((char*)&s->f.buf[8], "QEMU", 8); //8 bytes vendor + strncpy((char*)&s->f.buf[8], "QEMU", 8); //8 bytes vendor strncpy((char*)&s->f.buf[16], "USB Drive", 16); //16 bytes product - strncpy((char*)&s->f.buf[32], "1.00", 4); //4 bytes product revision + strncpy((char*)&s->f.buf[32], "1.00", 4); //4 bytes product revision break; case READ_CAPACITY_10: - int64_t fsize, lbas; + int64_t lbas; uint32_t *last_lba, *blk_len; memset(s->f.buf, 0, sizeof(s->f.buf)); - fsize = get_file_size(s->file); - - if (fsize == -1) //TODO + if (s->file_size == 0) //TODO { s->f.result = COMMAND_FAILED; set_sense(s, SENSE_CODE(UNKNOWN_ERROR)); @@ -661,7 +641,7 @@ namespace usb_msd //right? *blk_len = LBA_BLOCK_SIZE; //descriptor is currently max 64 bytes for bulk though - lbas = fsize / LBA_BLOCK_SIZE; + lbas = s->file_size / LBA_BLOCK_SIZE; if (lbas > 0xFFFFFFFF) { s->f.result = COMMAND_FAILED; @@ -691,12 +671,11 @@ namespace usb_msd if (xfer_len == 0) // nothing to do break; - if (fseeko64(s->file, lba * LBA_BLOCK_SIZE, SEEK_SET)) + if (FileSystem::FSeek64(s->file, lba * LBA_BLOCK_SIZE, SEEK_SET) != 0) { s->f.result = COMMAND_FAILED; //TODO use errno - int64_t fsize = get_file_size(s->file); - if ((lba + xfer_len) * LBA_BLOCK_SIZE > fsize) + if ((lba + xfer_len) * LBA_BLOCK_SIZE > s->file_size) set_sense(s, SENSE_CODE(OUT_OF_RANGE)); else set_sense(s, SENSE_CODE(NO_SEEK_COMPLETE)); @@ -725,12 +704,11 @@ namespace usb_msd if (xfer_len == 0) //nothing to do break; - if (fseeko64(s->file, lba * LBA_BLOCK_SIZE, SEEK_SET)) + if (FileSystem::FSeek64(s->file, lba * LBA_BLOCK_SIZE, SEEK_SET) != 0) { s->f.result = COMMAND_FAILED; //TODO use errno - int64_t fsize = get_file_size(s->file); - if ((lba + xfer_len) * LBA_BLOCK_SIZE > fsize) + if ((lba + xfer_len) * LBA_BLOCK_SIZE > s->file_size) set_sense(s, SENSE_CODE(OUT_OF_RANGE)); else set_sense(s, SENSE_CODE(NO_SEEK_COMPLETE)); @@ -748,9 +726,9 @@ namespace usb_msd } static void usb_msd_handle_control(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + int index, int length, uint8_t* data) { - MSDState* s = (MSDState*)dev; + MSDState* s = USB_CONTAINER_OF(dev, MSDState, dev); int ret = 0; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -779,7 +757,7 @@ namespace usb_msd static void usb_msd_cancel_io(USBDevice* dev, USBPacket* p) { - MSDState* s = (MSDState*)(dev); + MSDState* s = USB_CONTAINER_OF(dev, MSDState, dev); assert(s->packet == p); s->packet = NULL; @@ -792,7 +770,7 @@ namespace usb_msd static void usb_msd_handle_data(USBDevice* dev, USBPacket* p) { - MSDState* s = (MSDState*)dev; + MSDState* s = USB_CONTAINER_OF(dev, MSDState, dev); struct usb_msd_cbw cbw; uint8_t devep = p->ep->nr; @@ -820,7 +798,7 @@ namespace usb_msd if (le32_to_cpu(cbw.sig) != 0x43425355) { Console.Warning("usb-msd: Bad signature %08x\n", - le32_to_cpu(cbw.sig)); + le32_to_cpu(cbw.sig)); goto fail; } if (cbw.lun != 0) @@ -980,7 +958,7 @@ namespace usb_msd static void usb_msd_handle_destroy(USBDevice* dev) { - MSDState* s = (MSDState*)dev; + MSDState* s = USB_CONTAINER_OF(dev, MSDState, dev); if (s && s->file) { fclose(s->file); @@ -989,31 +967,23 @@ namespace usb_msd delete s; } - USBDevice* MsdDevice::CreateDevice(int port) + USBDevice* MsdDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { MSDState* s = new MSDState(); - //CONFIGVARIANT varApi(N_DEVICE_API, CONFIG_TYPE_CHAR); - //LoadSetting(port, DEVICENAME, varApi); - std::string api = *MsdDevice::ListAPIs().begin(); - - TSTDSTRING var; - - if (!LoadSetting(TypeName(), port, api, N_CONFIG_PATH, var)) + std::string path(USB::GetConfigString(si, port, TypeName(), "ImagePath")); + if (path.empty() || !(s->file = FileSystem::OpenCFile(path.c_str(), "r+b"))) { - Console.Warning("usb-msd: Could not load settings\n"); - delete s; - return NULL; - } - - s->file = wfopen(var.c_str(), TEXT("r+b")); - if (!s->file) - { - Console.WriteLn("usb-msd: Could not open image file '%s'\n", var.c_str()); + Host::AddOSDMessage(fmt::format("usb-msd: Could not open image file '{}'", path), Host::OSD_ERROR_DURATION); goto fail; } - s->f.hash = 0; + FILESYSTEM_STAT_DATA sd; + if (!FileSystem::StatFile(s->file, &sd)) + goto fail; + + s->file_size = sd.Size; + s->f.mtime = sd.ModificationTime; s->f.last_cmd = -1; s->dev.speed = USB_SPEED_FULL; @@ -1036,53 +1006,60 @@ namespace usb_msd usb_desc_init(&s->dev); usb_ep_init(&s->dev); - usb_msd_handle_reset((USBDevice*)s); - return (USBDevice*)s; + usb_msd_handle_reset(&s->dev); + return &s->dev; fail: - usb_msd_handle_destroy((USBDevice*)s); - return NULL; + usb_msd_handle_destroy(&s->dev); + return nullptr; } - const char* MsdDevice::TypeName() + const char* MsdDevice::TypeName() const { - return "msd"; + return "Msd"; } - int MsdDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* MsdDevice::Name() const { - MSDState* s = (MSDState*)dev; - MSDState::freeze* tmp; + return "Mass Storage Device"; + } - if (!s) - return 0; - switch (mode) + bool MsdDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + MSDState* s = USB_CONTAINER_OF(dev, MSDState, dev); + + // use mtime to check when the image has been changed... definitely far from ideal, + // but hashing the file every time we save state is kinda slow. and this isn't a + // heavily used feature... + FILESYSTEM_STAT_DATA sd; + if (s->file && FileSystem::StatFile(s->file, &sd)) + s->f.mtime = static_cast(sd.ModificationTime); + + const u64 old_mtime = s->f.mtime; + sw.DoPOD(&s->f); + + // resetting port to try to avoid possible data corruption + if (sw.IsReading() && old_mtime != s->f.mtime) { - case FreezeAction::Load: - //if (s->f.req) free (s->f.req); - - tmp = (MSDState::freeze*)data; - s->f = *tmp; - //ReqState *req = (ReqState *)((char*)data + sizeof(MSDState::freeze)); - //s->f.req = qemu_mallocz (sizeof(ReqState)); - //*s->f.req = *req; - - //TODO resetting port to try to avoid possible data corruption - //if (s->f.mode == USB_MSDM_DATAOUT) - usb_reattach(dev->port); - return sizeof(MSDState::freeze); // + sizeof(ReqState); - - case FreezeAction::Save: - tmp = (MSDState::freeze*)data; - *tmp = s->f; - return sizeof(MSDState::freeze); - - case FreezeAction::Size: - return sizeof(MSDState::freeze); // + sizeof(ReqState); - default: - break; + Host::AddOSDMessage("Modification time to USB mass storage image changed, reattaching.", + Host::OSD_ERROR_DURATION); + usb_reattach(dev->port); } - return 0; + + return !sw.HasError(); + } + + void MsdDevice::UpdateSettings(USBDevice* dev, SettingsInterface& si) const + { + // TODO: Handle changes to path. + } + + gsl::span MsdDevice::Settings(u32 subtype) const + { + static constexpr const SettingInfo settings[] = { + {SettingInfo::Type::Path, "ImagePath", "Image Path", "Sets the path to image which will back the virtual mass storage device."}, + }; + return settings; } } // namespace usb_msd diff --git a/pcsx2/USB/usb-msd/usb-msd.h b/pcsx2/USB/usb-msd/usb-msd.h index a32200589b..5331846fa4 100644 --- a/pcsx2/USB/usb-msd/usb-msd.h +++ b/pcsx2/USB/usb-msd/usb-msd.h @@ -13,40 +13,20 @@ * If not, see . */ -#ifndef USBMSD_H -#define USBMSD_H +#pragma once + #include "USB/deviceproxy.h" namespace usb_msd { - - static const char* APINAME = "cstdio"; - - class MsdDevice + class MsdDevice final : public DeviceProxy { public: - virtual ~MsdDevice() {} - static USBDevice* CreateDevice(int port); - static const char* TypeName(); - static const TCHAR* Name() - { - return TEXT("Mass storage device"); - } - static std::list ListAPIs() - { - return std::list{APINAME}; - } - static const TCHAR* LongAPIName(const std::string& name) - { - return TEXT("cstdio"); - } - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* TypeName() const override; + const char* Name() const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override; + gsl::span Settings(u32 subtype) const override; }; - } // namespace usb_msd -#endif diff --git a/pcsx2/USB/usb-pad/api_init_linux.cpp b/pcsx2/USB/usb-pad/api_init_linux.cpp deleted file mode 100644 index 07ca03fe06..0000000000 --- a/pcsx2/USB/usb-pad/api_init_linux.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "padproxy.h" -#include "evdev/evdev.h" - -void usb_pad::RegisterPad::Register() -{ - auto& inst = RegisterPad::instance(); - inst.Add("evdev", new PadProxy()); -} diff --git a/pcsx2/USB/usb-pad/api_init_win32_pad.cpp b/pcsx2/USB/usb-pad/api_init_win32_pad.cpp deleted file mode 100644 index 64c64ffe0e..0000000000 --- a/pcsx2/USB/usb-pad/api_init_win32_pad.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "padproxy.h" -#include "raw/usb-pad-raw.h" -#include "dx/usb-pad-dx.h" - -void usb_pad::RegisterPad::Register() -{ - auto& inst = RegisterPad::instance(); - inst.Add(raw::APINAME, new PadProxy()); - inst.Add(dx::APINAME, new PadProxy()); -} diff --git a/pcsx2/USB/usb-pad/bitjuggling.cpp b/pcsx2/USB/usb-pad/bitjuggling.cpp deleted file mode 100644 index 6b0a9bcb57..0000000000 --- a/pcsx2/USB/usb-pad/bitjuggling.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "USB/qemu-usb/vl.h" - -#include -#include -#include - -// -// http://stackoverflow.com/questions/3534535/whats-a-time-efficient-algorithm-to-copy-unaligned-bit-arrays -// - -#define PREPARE_FIRST_COPY() \ - do \ - { \ - if (src_len >= (CHAR_BIT - dst_offset_modulo)) \ - { \ - *dst &= reverse_mask[dst_offset_modulo]; \ - src_len -= CHAR_BIT - dst_offset_modulo; \ - } \ - else \ - { \ - *dst &= reverse_mask[dst_offset_modulo] | reverse_mask_xor[dst_offset_modulo + src_len + 1]; \ - c &= reverse_mask[dst_offset_modulo + src_len]; \ - src_len = 0; \ - } \ - } while (0) - -//But copies bits in reverse? -void bitarray_copy(const uint8_t* src_org, int src_offset, int src_len, - uint8_t* dst_org, int dst_offset) -{ - static const unsigned char mask[] = - {0x55, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; - static const unsigned char reverse_mask[] = - {0x55, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; - static const unsigned char reverse_mask_xor[] = - {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00}; - - if (src_len) - { - const unsigned char* src; - unsigned char* dst; - int src_offset_modulo, - dst_offset_modulo; - - src = src_org + (src_offset / CHAR_BIT); - dst = dst_org + (dst_offset / CHAR_BIT); - - src_offset_modulo = src_offset % CHAR_BIT; - dst_offset_modulo = dst_offset % CHAR_BIT; - - if (src_offset_modulo == dst_offset_modulo) - { - int byte_len; - int src_len_modulo; - if (src_offset_modulo) - { - unsigned char c; - - c = reverse_mask_xor[dst_offset_modulo] & *src++; - - PREPARE_FIRST_COPY(); - *dst++ |= c; - } - - byte_len = src_len / CHAR_BIT; - src_len_modulo = src_len % CHAR_BIT; - - if (byte_len) - { - memcpy(dst, src, byte_len); - src += byte_len; - dst += byte_len; - } - if (src_len_modulo) - { - *dst &= reverse_mask_xor[src_len_modulo]; - *dst |= reverse_mask[src_len_modulo] & *src; - } - } - else - { - int bit_diff_ls, - bit_diff_rs; - int byte_len; - int src_len_modulo; - unsigned char c; - /* - * Begin: Line things up on destination. - */ - if (src_offset_modulo > dst_offset_modulo) - { - bit_diff_ls = src_offset_modulo - dst_offset_modulo; - bit_diff_rs = CHAR_BIT - bit_diff_ls; - - c = *src++ << bit_diff_ls; - c |= *src >> bit_diff_rs; - c &= reverse_mask_xor[dst_offset_modulo]; - } - else - { - bit_diff_rs = dst_offset_modulo - src_offset_modulo; - bit_diff_ls = CHAR_BIT - bit_diff_rs; - - c = *src >> bit_diff_rs & - reverse_mask_xor[dst_offset_modulo]; - } - PREPARE_FIRST_COPY(); - *dst++ |= c; - - /* - * Middle: copy with only shifting the source. - */ - byte_len = src_len / CHAR_BIT; - - while (--byte_len >= 0) - { - c = *src++ << bit_diff_ls; - c |= *src >> bit_diff_rs; - *dst++ = c; - } - - /* - * End: copy the remaing bits; - */ - src_len_modulo = src_len % CHAR_BIT; - if (src_len_modulo) - { - c = *src++ << bit_diff_ls; - c |= *src >> bit_diff_rs; - c &= reverse_mask[src_len_modulo]; - - *dst &= reverse_mask_xor[src_len_modulo]; - *dst |= c; - } - } - } -} diff --git a/pcsx2/USB/usb-pad/dx/dinput-config.cpp b/pcsx2/USB/usb-pad/dx/dinput-config.cpp deleted file mode 100644 index b3f846cb20..0000000000 --- a/pcsx2/USB/usb-pad/dx/dinput-config.cpp +++ /dev/null @@ -1,1479 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" - -#pragma warning(push) -// floats to int -#pragma warning(disable : 4244) - -#include "dx.h" - -#include -#include -#include -#include -#include "versionproxy.h" - -#include "usb-pad-dx.h" -#include "USB/shared/inifile_usb.h" - -namespace usb_pad -{ - namespace dx - { - - static int32_t useRamp = 0; - - bool listening = false; - DWORD listenend = 0; - DWORD listennext = 0; - DWORD listentimeout = 10000; - DWORD listeninterval = 500; - - extern BYTE diks[256]; // DirectInput keyboard state buffer - extern DIMOUSESTATE2 dims2; // DirectInput mouse state structure - - std::vector jso; // DInput joystick old state, only for config - std::vector jsi; // DInput joystick initial state, only for config - - int32_t GAINZ[2][1]; - int32_t FFMULTI[2][1]; - int32_t INVERTFORCES[2]{}; - - // FFB test - bool ffbTestRunning = false; - unsigned int ffbTestStage = 0; - - bool dialogOpen = false; - - HWND hKey; - HWND hWnd; - TCHAR text[1024]; - ControlID CID = CID_COUNT; //keep track of last assigned control - - HFONT hFont; - HDC hDC; - PAINTSTRUCT Ps; - RECT rect; - static WNDPROC pFnPrevFunc; - LONG filtercontrol = 0; - float TESTV = 0; - float TESTVF = 0; - DWORD m_dwScalingTime; - DWORD m_dwDrawingTime; - DWORD m_dwCreationTime; - DWORD m_dwMemory; - DWORD m_dwOption; - DWORD m_dwTime; - HBITMAP m_hOldAABitmap; - HBITMAP m_hAABitmap; - HDC m_hAADC; - HBITMAP m_hOldMemBitmap; - HBITMAP m_hMemBitmap; - HDC m_hMemDC; - - - //label enum - DWORD LABELS[CID_COUNT] = { - IDC_LABEL0, - IDC_LABEL1, - IDC_LABEL2, - IDC_LABEL3, - IDC_LABEL4, - IDC_LABEL5, - IDC_LABEL6, - IDC_LABEL7, - IDC_LABEL8, - IDC_LABEL9, - IDC_LABEL10, - IDC_LABEL11, - IDC_LABEL12, - IDC_LABEL13, - IDC_LABEL14, - IDC_LABEL15, - IDC_LABEL16, - IDC_LABEL17, - IDC_LABEL18, - IDC_LABEL19, - IDC_LABEL20, - IDC_LABEL21, - IDC_LABEL22, - IDC_LABEL23, - IDC_LABEL24, - IDC_LABEL25, - IDC_LABEL26, - IDC_LABEL27, - IDC_LABEL28, - IDC_LABEL29, - IDC_LABEL30, - }; - - struct DXDlgSettings - { - int port; - const char* dev_type; - }; - - void SetControlLabel(int cid, const InputMapped& im) - { - if (cid >= CID_COUNT) - return; - - if (im.type == MT_AXIS) - swprintf_s(text, L"Axis %zi/%i/%s/%i", im.index, im.mapped, im.INVERTED ? L"i" : L"n", im.HALF); - else if (im.type == MT_BUTTON) - swprintf_s(text, L"Button %zi/%i", im.index, im.mapped); - else - swprintf_s(text, L"Unmapped"); - - SetWindowText(GetDlgItem(hWnd, LABELS[cid]), text); - } - - //config only - void ListenUpdate() - { - for (size_t i = 0; i < g_pJoysticks.size(); i++) - { - jso[i] = g_pJoysticks[i]->GetDeviceState(); - } - PollDevices(); - } - - //poll and store all joystick states for comparison (config only) - void ListenAxis() - { - PollDevices(); - for (size_t i = 0; i < g_pJoysticks.size(); i++) - { - if (g_pJoysticks[i]->GetControlType() != CT_JOYSTICK) - continue; - jso[i] = g_pJoysticks[i]->GetDeviceState(); - jsi[i] = jso[i]; - } - - listenend = listentimeout + GetTickCount(); - listennext = GetTickCount(); - listening = true; - } - - //get listen time left in ms (config only) - DWORD GetListenTimeout() - { - return listenend - GetTickCount(); - } - - //compare all device axis for difference (config only) - bool AxisDown(size_t ijoy, InputMapped& im) - { - //TODO mouse axis - if (g_pJoysticks[ijoy]->GetControlType() != CT_JOYSTICK) - return false; - - DIJOYSTATE2 js = g_pJoysticks[ijoy]->GetDeviceState(); - LONG detectrange = 2000; - for (int32_t axisid = 0; axisid < DINPUT_AXES_COUNT; axisid++) - { - LONG diff = 0; - im.index = ijoy; - im.mapped = axisid; - im.type = MappingType::MT_NONE; - - // TODO mind the POV axes, one axis for all directions? - diff = GetAxisValueFromOffset(axisid, js) - GetAxisValueFromOffset(axisid, jso[ijoy]); - if (diff > detectrange) - { - im.HALF = GetAxisValueFromOffset(axisid, jsi[ijoy]); - im.INVERTED = true; - im.type = MappingType::MT_AXIS; - return true; - } - if (diff < -detectrange) - { - im.HALF = GetAxisValueFromOffset(axisid, jsi[ijoy]); - im.INVERTED = false; - im.type = MappingType::MT_AXIS; - return true; - } - } - return false; - } - - //checks all devices for digital button id/index - bool KeyDown(size_t ijoy, InputMapped& im) - { - assert(ijoy < g_pJoysticks.size()); - if (ijoy >= g_pJoysticks.size()) - return false; - - auto joy = g_pJoysticks[ijoy]; - im.index = ijoy; - im.type = MT_NONE; - - int buttons = 0; - - switch (joy->GetControlType()) - { - case CT_JOYSTICK: - buttons = ARRAY_SIZE(DIJOYSTATE2::rgbButtons) + 16 /* POV */; - break; - case CT_KEYBOARD: - buttons = 256; - break; - case CT_MOUSE: - buttons = ARRAY_SIZE(DIMOUSESTATE2::rgbButtons); - break; - default: - break; - } - - for (int b = 0; b < buttons; b++) - { - if (joy->GetButton(b)) - { - im.mapped = b; - im.type = MT_BUTTON; - return true; - } - } - - return false; - } - - //search all axis/buttons (config only) - bool FindControl(LONG port, ControlID cid, InputMapped& im) - { - if (listening == true) - { - if (listenend > GetTickCount()) - { - if (listennext < GetTickCount()) - { - listennext = listeninterval + GetTickCount(); - ListenUpdate(); - - for (size_t i = 0; i < g_pJoysticks.size(); i++) - { - if (AxisDown(i, im)) - { - listening = false; - if (CID_STEERING == cid) - { - CreateFFB(port, g_pJoysticks[im.index]->GetDevice(), im.mapped); - } - AddInputMap(port, cid, im); - return true; - } - else if (KeyDown(i, im)) - { - listening = false; - AddInputMap(port, cid, im); - return true; - } - } - } - } - else - { - GetInputMap(port, cid, im); - SetControlLabel(cid, im); - listening = false; - return false; //timedout - } - } - return false; - } - - void ApplyFilter(int port) - { - filtercontrol = SendMessage(GetDlgItem(hWnd, IDC_COMBO1), CB_GETCURSEL, 0, 0); - - if (filtercontrol == -1) - return; - //slider - auto& im = g_Controls[port][filtercontrol]; - im.LINEAR = SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; - im.OFFSET = SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; - im.DEADZONE = SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; - GAINZ[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_GETPOS, 0, 0); - FFMULTI[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_GETPOS, 0, 0); - - swprintf_s(text, TEXT("LINEARITY: %0.02f"), float(im.LINEAR) / PRECMULTI); - SetWindowText(GetDlgItem(hWnd, IDC_LINEAR), text); - swprintf_s(text, TEXT("OFFSET: %0.02f"), float(im.OFFSET) / PRECMULTI); - SetWindowText(GetDlgItem(hWnd, IDC_OFFSET), text); - swprintf_s(text, TEXT("DEAD-ZONE: %0.02f"), float(im.DEADZONE) / PRECMULTI); - SetWindowText(GetDlgItem(hWnd, IDC_DEADZONE), text); - - GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); - MapWindowPoints(GetDlgItem(hWnd, IDC_PICTURE), hWnd, (POINT*)&rect, 2); - InvalidateRect(hWnd, &rect, TRUE); - } - - void LoadFilter(int port) - { - filtercontrol = SendMessage(GetDlgItem(hWnd, IDC_COMBO1), CB_GETCURSEL, 0, 0); - if (filtercontrol == -1) - return; - auto& im = g_Controls[port][filtercontrol]; - //slider - SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_SETPOS, 1, im.LINEAR + 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_SETPOS, 1, im.OFFSET + 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_SETPOS, 1, im.DEADZONE + 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_SETPOS, 1, GAINZ[port][0]); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_SETPOS, 1, FFMULTI[port][0]); - - ApplyFilter(port); - } - - void DefaultFilters(int port, LONG id) - { - auto& im_l = g_Controls[port][CID_STEERING]; - auto& im_r = g_Controls[port][CID_STEERING_R]; - for (int i = 0; i < 6; i++) - { - auto& c = g_Controls[port][i]; - c.LINEAR = 0; - c.OFFSET = 0; - c.DEADZONE = 0; - } - - switch (id) - { - case 0: - im_l.LINEAR = 0; - im_l.OFFSET = 0; - im_r.LINEAR = 0; - im_r.OFFSET = 0; - break; - case 1: - im_l.LINEAR = 6 * PRECMULTI; - im_l.OFFSET = 0; - im_r.LINEAR = 6 * PRECMULTI; - im_r.OFFSET = 0; - break; - case 2: - im_l.LINEAR = 12 * PRECMULTI; - im_l.OFFSET = 0; - im_r.LINEAR = 12 * PRECMULTI; - im_r.OFFSET = 0; - break; - case 3: - im_l.LINEAR = 18 * PRECMULTI; - im_l.OFFSET = 0; - im_r.LINEAR = 18 * PRECMULTI; - im_r.OFFSET = 0; - break; - case 4: - im_l.LINEAR = 25 * PRECMULTI; - im_l.OFFSET = 0; - im_r.LINEAR = 25 * PRECMULTI; - im_r.OFFSET = 0; - break; - } - - LoadFilter(port); - } - - void ControlTest(int port) //thread: waits for window - { - InputMapped im; - - filtercontrol = SendMessage(GetDlgItem(hWnd, IDC_COMBO1), CB_GETCURSEL, 0, 0); - if (filtercontrol >= 0 && listening == false) - { - PollDevices(); - - if (GetInputMap(port, (ControlID)filtercontrol, im)) - { - TESTV = ReadAxis(im); - TESTVF = FilterControl(ReadAxis(im), im.LINEAR, im.OFFSET, im.DEADZONE); - GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); - MapWindowPoints(GetDlgItem(hWnd, IDC_PICTURE), hWnd, (POINT*)&rect, 2); - InvalidateRect(hWnd, &rect, TRUE); - } - } - else - { - TESTV = 0; - } - - return; - } - - void ListenForControl(int port) - { - InputMapped im({}); - - if (FindControl(port, CID, im)) - { - AddInputMap(port, CID, im); - SetControlLabel(CID, im); - } - else if (listening) - { - swprintf_s(text, L"Listening... %u", GetListenTimeout() / 1000 + 1); - SetWindowText(GetDlgItem(hWnd, LABELS[CID]), text); - } - } - - void StartListen(ControlID controlid) - { - if (listening) - return; - - CID = controlid; - swprintf_s(text, L"Listening..."); - SetWindowText(GetDlgItem(hWnd, LABELS[CID]), text); - ListenAxis(); - } - - void DeleteControl(int port, ControlID controlid) - { - CID = controlid; - RemoveInputMap(port, controlid); - SetWindowText(GetDlgItem(hWnd, LABELS[CID]), TEXT("Unmapped")); - } - - void CreateDrawing(int port, HDC hDrawingDC, int scale) - { - GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); - //MapWindowPoints(GetDlgItem(hWnd,IDC_PICTURE), hWnd, (POINT *) &rect, 2); - - int px = 0; //rect.left; - int py = 0; //rect.top; - int pwidth = rect.right - rect.left; - int pheight = rect.bottom - rect.top; - - HPEN bluepen = CreatePen(PS_SOLID, 4 * scale, COLORREF RGB(79, 97, 117)); - HPEN gridpen = CreatePen(PS_SOLID, 1 * scale, COLORREF RGB(0, 0, 0)); - HPEN blackpen = CreatePen(PS_SOLID, 4 * scale, COLORREF RGB(0, 0, 0)); - HBRUSH hbrush = (HBRUSH)GetStockObject(LTGRAY_BRUSH); - - SelectObject(hDrawingDC, hbrush); - - rect.right *= scale; - rect.bottom *= scale; - - FillRect(hDrawingDC, &rect, hbrush); - - //draw grid - SelectObject(hDrawingDC, gridpen); - float step[2] = {pwidth / 5.f, pheight / 5.f}; - for (int x = 1; x < 5; x++) - { - MoveToEx(hDrawingDC, (step[0] * x + px) * scale, (py), 0); - LineTo(hDrawingDC, (step[0] * x + px) * scale, (pheight + py) * scale); - } - for (int y = 1; y < 5; y++) - { - MoveToEx(hDrawingDC, (px)*scale, (step[1] * y + py) * scale, 0); - LineTo(hDrawingDC, (pwidth + px) * scale, (step[1] * y + py) * scale); - } - - //draw linear line - SelectObject(hDrawingDC, blackpen); - MoveToEx(hDrawingDC, (px)*scale, (pheight + py) * scale, 0); - for (float x = 0; x < 1.0f; x += 0.001f) - { - LineTo(hDrawingDC, (x * pwidth + px) * scale, (-x * pheight + pheight + py) * scale); - } - - filtercontrol = SendMessage(GetDlgItem(hWnd, IDC_COMBO1), CB_GETCURSEL, 0, 0); - if (filtercontrol >= 0) - { - auto& im = g_Controls[port][filtercontrol]; - - //draw nonlinear line - SelectObject(hDrawingDC, bluepen); - MoveToEx(hDrawingDC, (px + 8) * scale, (pheight + py - 8) * scale, 0); - for (float x = 0; x < 1.0f; x += 0.001f) - { - float y1 = FilterControl(x, im.LINEAR, im.OFFSET, im.DEADZONE); - LineTo(hDrawingDC, (x * (pwidth - 16) + px + 8) * scale, (-y1 * (pheight - 16) + (pheight - 8) + py) * scale); - } - LineTo(hDrawingDC, (1.0f * (pwidth - 16) + px + 8) * scale, (-1.0f * (pheight - 16) + (pheight - 8) + py) * scale); - - //draw output - int tx = (TESTV * (pwidth - 16) + px + 8) * scale; - int ty = (-TESTVF * (pheight - 16) + (pheight - 8) + py) * scale; - - Ellipse(hDrawingDC, tx - scale * 10, ty - scale * 10, tx + scale * 10, ty + scale * 10); - } - //cleanup - DeleteObject(bluepen); - DeleteObject(gridpen); - DeleteObject(blackpen); - } - - void CreateAAImage(int port, HDC hAADC, int scale) - { - GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); - MapWindowPoints(GetDlgItem(hWnd, IDC_PICTURE), hWnd, (POINT*)&rect, 2); - - const int pwidth = rect.right - rect.left; - const int pheight = rect.bottom - rect.top; - - DWORD startTime, endTime; - - // Calculate memory requested - m_dwMemory = (scale * pwidth) * (scale * pheight) * 4; - - // Get screen DC - HDC hDC = ::GetDC(NULL); - - // Create temporary DC and bitmap - startTime = GetTickCount(); - HDC hTempDC = ::CreateCompatibleDC(hDC); - HBITMAP hTempBitmap = ::CreateCompatibleBitmap(hDC, scale * pwidth, scale * pheight); - HBITMAP hOldTempBitmap = (HBITMAP)::SelectObject(hTempDC, hTempBitmap); - endTime = GetTickCount(); - m_dwCreationTime = endTime - startTime; - - // Release screen DC - ::ReleaseDC(NULL, hDC); - - // Create drawing - startTime = GetTickCount(); - CreateDrawing(port, hTempDC, scale); - endTime = GetTickCount(); - m_dwDrawingTime = endTime - startTime; - - /* // Copy temporary DC to anti-aliazed DC - startTime = GetTickCount(); - int oldStretchBltMode = ::SetStretchBltMode(hAADC, HALFTONE); - ::StretchBlt(hAADC, 0, 0, 300, 200, hTempDC, 0, 0, scale*300, scale*200, SRCCOPY); - ::SetStretchBltMode(hAADC, oldStretchBltMode); - endTime = GetTickCount(); - m_dwScalingTime = endTime - startTime;*/ - - startTime = GetTickCount(); - - // Get source bits - int srcWidth = scale * pwidth; - int srcPitch = srcWidth * 4; - int srcSize = srcWidth * srcPitch; - BYTE* lpSrcBits = new BYTE[srcSize]; - GetBitmapBits(hTempBitmap, srcSize, lpSrcBits); - - // Get destination bits - int dstWidth = pwidth; - int dstHeight = pheight; - int dstPitch = dstWidth * 4; - int dstSize = dstWidth * dstPitch; - BYTE* lpDstBits = new BYTE[dstSize]; - HBITMAP hAABitmap = (HBITMAP)GetCurrentObject(hAADC, OBJ_BITMAP); - GetBitmapBits(hAABitmap, dstSize, lpDstBits); - - const int gridSize = scale * scale; - int resultRed, resultGreen, resultBlue; - int dstY = 0; - int tmpX, tmpY, tmpOffset; - for (int y = 1; y < dstHeight - 2; y++) - { - int dstX = 0; - const int srcY = (y * scale) * srcPitch; - for (int x = 1; x < dstWidth - 2; x++) - { - const int srcX = (x * scale) * 4; - const int srcOffset = srcY + srcX; - - resultRed = resultGreen = resultBlue = 0; - tmpY = -srcPitch; - for (int i = 0; i < scale; i++) - { - tmpX = -4; - for (int j = 0; j < scale; j++) - { - tmpOffset = tmpY + tmpX; - - resultRed += lpSrcBits[srcOffset + tmpOffset + 2]; - resultGreen += lpSrcBits[srcOffset + tmpOffset + 1]; - resultBlue += lpSrcBits[srcOffset + tmpOffset]; - - tmpX += 4; - } - tmpY += srcPitch; - } - - const int dstOffset = dstY + dstX; - lpDstBits[dstOffset + 2] = (BYTE)(resultRed / gridSize); - lpDstBits[dstOffset + 1] = (BYTE)(resultGreen / gridSize); - lpDstBits[dstOffset] = (BYTE)(resultBlue / gridSize); - dstX += 4; - } - - dstY += dstPitch; - } - SetBitmapBits(hAABitmap, dstSize, lpDstBits); - - // Destroy source bits - delete[] lpSrcBits; - - // Destroy destination bits - delete[] lpDstBits; - - endTime = GetTickCount(); - - m_dwScalingTime = endTime - startTime; - - // Destroy temporary DC and bitmap - if (hTempDC) - { - ::SelectObject(hTempDC, hOldTempBitmap); - ::DeleteDC(hTempDC); - ::DeleteObject(hTempBitmap); - } - } - - void InitialUpdate() - { - GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); - MapWindowPoints(GetDlgItem(hWnd, IDC_PICTURE), hWnd, (POINT*)&rect, 2); - - int px = rect.left; - int py = rect.top; - int pwidth = rect.right - rect.left; - int pheight = rect.bottom - rect.top; - - // Get screen DC - HDC hDC = ::GetDC(NULL); - - // Create memory DC and bitmap - m_hMemDC = ::CreateCompatibleDC(hDC); - m_hMemBitmap = ::CreateCompatibleBitmap(hDC, pwidth, pheight); - m_hOldMemBitmap = (HBITMAP)::SelectObject(m_hMemDC, m_hMemBitmap); - - // Create anti-alias DC and bitmap - m_hAADC = ::CreateCompatibleDC(hDC); - m_hAABitmap = ::CreateCompatibleBitmap(hDC, pwidth, pheight); - m_hOldAABitmap = (HBITMAP)::SelectObject(m_hAADC, m_hAABitmap); - - // Release screen DC - ::ReleaseDC(NULL, hDC); - - // Create drawing - //CreateDrawing(m_hMemDC, 1); - //CreateAAImage(m_hAADC, 1); - } - - void OnPaint(int port) - { - GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); - MapWindowPoints(GetDlgItem(hWnd, IDC_PICTURE), hWnd, (POINT*)&rect, 2); - - int px = rect.left; - int py = rect.top; - if (px <= 0 || py <= 0) - { - return; - } - int pwidth = rect.right - rect.left; - int pheight = rect.bottom - rect.top; - - //hDC = GetDC(hWnd);// - CreateAAImage(port, m_hAADC, 2); - - hDC = BeginPaint(hWnd, &Ps); - - //CreateDrawing(m_hAADC); - // Draw 2x2 anti-aliazed image - ::BitBlt(hDC, px + 2, py + 2, pwidth - 4, pheight - 4, m_hAADC, 0, 0, SRCCOPY); - - EndPaint(hWnd, &Ps); - } - - void EndFFBTest() - { - if (std::exchange(ffbTestRunning, false)) - { - KillTimer(hWnd, 23); - } - } - - INT_PTR CALLBACK StaticProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - (*pFnPrevFunc)(hDlg, uMsg, wParam, lParam); - switch (uMsg) - { - case WM_ERASEBKGND: - return TRUE; - case WM_PAINT: - break; - } - return TRUE; - } - - void InitDialog(int port, const char* dev_type) - { - hFont = CreateFont(18, - 0, - 0, - 0, - FW_BOLD, - 0, - 0, - 0, - ANSI_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - DEFAULT_PITCH | FF_DONTCARE, - TEXT("Tahoma")); - HFONT hFont2 = CreateFont(14, - 0, - 0, - 0, - FW_BOLD, - 0, - 0, - 0, - ANSI_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - DEFAULT_PITCH | FF_DONTCARE, - TEXT("Tahoma")); - - //pFnPrevFunc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hWnd,IDC_PICTURE),GWLP_WNDPROC,(LONG_PTR) StaticProc); - InitDirectInput(hWnd, port); - LoadDInputConfig(port, dev_type); // settings rely on GUIDs so load after device enum - FindFFDevice(port); - - jso.resize(g_pJoysticks.size()); - jsi.resize(g_pJoysticks.size()); - - InitCommonControls(); - - InitialUpdate(); - - SetTimer(hWnd, 22, 40, (TIMERPROC)NULL); - - //StartTest(); - const wchar_t* string[] = {L"STEER LEFT", L"STEER RIGHT", L"THROTTLE", L"BRAKE"}; - - for (int i = 0; i < 4; i++) - SendMessageW(GetDlgItem(hWnd, IDC_COMBO1), CB_ADDSTRING, 0, (LPARAM)string[i]); - - const wchar_t* stringp[] = {L"200 deg", L"360 deg", L"540 deg", L"720 deg", L"900 deg"}; - - for (int i = 0; i < 5; i++) - SendMessageW(GetDlgItem(hWnd, IDC_COMBO3), CB_ADDSTRING, 0, (LPARAM)stringp[i]); - - //slider - SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_SETPOS, 1, 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_SETPOS, 1, 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_SETPOS, 1, 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_SETRANGEMAX, 1, 100 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_SETRANGEMAX, 1, 100 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_SETRANGEMAX, 1, 100 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_SETTICFREQ, 10 * PRECMULTI, 0); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_SETTICFREQ, 10 * PRECMULTI, 0); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_SETTICFREQ, 10 * PRECMULTI, 0); - - SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_SETRANGEMAX, 1, 10000); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_SETTICFREQ, 1000, 0); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_SETPOS, 1, GAINZ[port][0]); - - SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_SETRANGEMAX, 1, 10); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_SETTICFREQ, 1, 0); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_SETPOS, 1, FFMULTI[port][0]); - - SendMessage(GetDlgItem(hWnd, IDC_CHECK1), BM_SETCHECK, INVERTFORCES[port], 0); - SendMessage(GetDlgItem(hWnd, IDC_CHECK3), BM_SETCHECK, useRamp, 0); - //HANDLE hBitmap = LoadImage(NULL,MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP,0,0,LR_DEFAULTSIZE); - //SendMessage(GetDlgItem(hWnd,IDC_PICTURELINK), STM_SETIMAGE, IMAGE_BITMAP, LPARAM(hBitmap)); - - //fonts - SendMessage(GetDlgItem(hWnd, IDC_GROUP1), WM_SETFONT, (WPARAM)hFont, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP2), WM_SETFONT, (WPARAM)hFont, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP3), WM_SETFONT, (WPARAM)hFont, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP4), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP5), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP6), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP7), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP8), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP9), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP10), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP11), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP12), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP13), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP14), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP15), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP16), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP17), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP18), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP19), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP20), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP21), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP22), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP23), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP24), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP25), WM_SETFONT, (WPARAM)hFont2, 1); - SendMessage(GetDlgItem(hWnd, IDC_GROUP26), WM_SETFONT, (WPARAM)hFont2, 1); - - for (int i = 0; i < CID_COUNT; i++) - { - InputMapped im = {}; - GetInputMap(port, (ControlID)i, im); - SetControlLabel(i, im); - } - ShowWindow(hWnd, SW_SHOW); - - dialogOpen = true; - //UpdateWindow( hWnd ); - } - - INT_PTR CALLBACK DxDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - DXDlgSettings* s = nullptr; - //return false; - switch (uMsg) - { - case WM_CREATE: - SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); - break; - - case WM_INITDIALOG: - { - s = (DXDlgSettings*)lParam; - hWnd = hDlg; - SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); - InitDialog(s->port, s->dev_type); - } - break; - case WM_CTLCOLORSTATIC: - { - if ((HWND)lParam == GetDlgItem(hWnd, IDC_GROUP1) || (HWND)lParam == GetDlgItem(hWnd, IDC_GROUP2) || (HWND)lParam == GetDlgItem(hWnd, IDC_GROUP3)) - { - SetTextColor((HDC)wParam, RGB(79, 97, 117)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetStockObject(NULL_BRUSH); - } - if ((HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL1_LBL1) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL2_LBL1) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL3_LBL1) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL4_LBL1)) - { - SetTextColor((HDC)wParam, RGB(255, 0, 0)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetStockObject(NULL_BRUSH); - } - if ((HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL1_LBL2) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL2_LBL2) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL3_LBL2) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL4_LBL2)) - { - SetTextColor((HDC)wParam, RGB(0, 192, 255)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetStockObject(NULL_BRUSH); - } - if ((HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL1_LBL3) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL2_LBL3) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL3_LBL3) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL4_LBL3)) - { - SetTextColor((HDC)wParam, RGB(255, 165, 0)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetStockObject(NULL_BRUSH); - } - if ((HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL1_LBL4) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL2_LBL4) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL3_LBL4) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL4_LBL4)) - { - SetTextColor((HDC)wParam, RGB(0, 140, 0)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetStockObject(NULL_BRUSH); - } - if ((HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL1_LBL5) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL2_LBL5) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL3_LBL5) || (HWND)lParam == GetDlgItem(hWnd, IDC_BZ_CTL4_LBL5)) - { - SetTextColor((HDC)wParam, RGB(204, 204, 0)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetStockObject(NULL_BRUSH); - } - - break; - } - case WM_TIMER: - { - - switch (wParam) - { - case 22: - { - s = (DXDlgSettings*)GetWindowLongPtr(hDlg, GWLP_USERDATA); - if (listening) - ListenForControl(s->port); - ControlTest(s->port); - break; - } - case 23: - { - s = (DXDlgSettings*)GetWindowLongPtr(hDlg, GWLP_USERDATA); - if (!UpdateTestForce(s->port, ffbTestStage++)) - { - EndTestForce(s->port); - EndFFBTest(); - } - break; - } - } - break; - } - - case WM_COMMAND: - s = (DXDlgSettings*)GetWindowLongPtr(hDlg, GWLP_USERDATA); - switch (LOWORD(wParam)) - { - case IDC_COMBO1: - switch (HIWORD(wParam)) - { - case CBN_SELCHANGE: - LoadFilter(s->port); - break; - } - break; - case IDC_COMBO3: - switch (HIWORD(wParam)) - { - case CBN_SELCHANGE: - DefaultFilters(s->port, SendMessage(GetDlgItem(hWnd, IDC_COMBO3), CB_GETCURSEL, 0, 0)); - SendMessage(GetDlgItem(hWnd, IDC_COMBO3), CB_SETCURSEL, -1, 0); - break; - } - break; - case IDC_COMBO4: - switch (HIWORD(wParam)) - { - case LBN_SELCHANGE: - //selectedJoy[0] = SendDlgItemMessage(hWnd, IDC_COMBO4, CB_GETCURSEL, 0, 0); - break; - } - break; - - case IDOK: - { - ApplySettings(s->port); - SaveDInputConfig(s->port, s->dev_type); - SaveConfig(); // Force save to ini file - //Seems to create some dead locks - //SendMessage(hWnd, WM_CLOSE, 0, 0); - //return TRUE; - dialogOpen = false; - EndFFBTest(); - FreeDirectInput(); - EndDialog(hWnd, TRUE); - return TRUE; - } - //break; //Fall through - case IDCANCEL: - { - //Seems to create some dead locks - //SendMessage(hWnd, WM_CLOSE, 0, 0); - dialogOpen = false; - EndFFBTest(); - FreeDirectInput(); - EndDialog(hWnd, FALSE); - return TRUE; - } - break; - case IDC_BUTTON1: - { - if (!ffbTestRunning) - { - ApplySettings(s->port); - if (StartTestForce(s->port)) - { - if (UpdateTestForce(s->port, 0)) - { - // Start a timer to "tick" the FFB test every 500ms - ffbTestStage = 1; - SetTimer(hWnd, 23, 500, nullptr); - ffbTestRunning = true; - } - } - } - } - break; - case IDC_DELALL: - { - for (int i = 0; i < CID_COUNT; i++) - { - DeleteControl(s->port, (ControlID)i); - } - } - break; - - case IDC_ASS0: - { - StartListen(CID_STEERING); - break; - } - case IDC_ASS1: - { - StartListen(CID_STEERING_R); - break; - } - case IDC_ASS2: - { - StartListen(CID_THROTTLE); - break; - } - case IDC_ASS3: - { - StartListen(CID_BRAKE); - break; - } - case IDC_ASS4: - { - StartListen(CID_HATUP); - break; - } - case IDC_ASS5: - { - StartListen(CID_HATDOWN); - break; - } - case IDC_ASS6: - { - StartListen(CID_HATLEFT); - break; - } - case IDC_ASS7: - { - StartListen(CID_HATRIGHT); - break; - } - case IDC_ASS8: - { - StartListen(CID_SQUARE); - break; - } - case IDC_ASS9: - { - StartListen(CID_TRIANGLE); - break; - } - case IDC_ASS10: - { - StartListen(CID_CROSS); - break; - } - case IDC_ASS11: - { - StartListen(CID_CIRCLE); - break; - } - case IDC_ASS12: - { - StartListen(CID_L1); - break; - } - case IDC_ASS13: - { - StartListen(CID_R1); - break; - } - case IDC_ASS14: - { - StartListen(CID_L2); - break; - } - case IDC_ASS15: - { - StartListen(CID_R2); - break; - } - case IDC_ASS16: - { - StartListen(CID_L3); - break; - } - case IDC_ASS17: - { - StartListen(CID_R3); - break; - } - case IDC_ASS18: - { - StartListen(CID_SELECT); - break; - } - case IDC_ASS19: - { - StartListen(CID_START); - break; - } - case IDC_ASS20: - { - StartListen(CID_BUTTON20); - break; - } - case IDC_ASS21: - { - StartListen(CID_BUTTON21); - break; - } - case IDC_ASS22: - { - StartListen(CID_BUTTON22); - break; - } - case IDC_ASS23: - { - StartListen(CID_BUTTON23); - break; - } - case IDC_ASS24: - { - StartListen(CID_BUTTON24); - break; - } - case IDC_ASS25: - { - StartListen(CID_BUTTON25); - break; - } - case IDC_ASS26: - { - StartListen(CID_BUTTON26); - break; - } - case IDC_ASS27: - { - StartListen(CID_BUTTON27); - break; - } - case IDC_ASS28: - { - StartListen(CID_BUTTON28); - break; - } - case IDC_ASS29: - { - StartListen(CID_BUTTON29); - break; - } - case IDC_ASS30: - { - StartListen(CID_BUTTON30); - break; - } - case IDC_DEL0: - { - DeleteControl(s->port, CID_STEERING); - break; - } - case IDC_DEL1: - { - DeleteControl(s->port, CID_STEERING_R); - break; - } - case IDC_DEL2: - { - DeleteControl(s->port, CID_THROTTLE); - break; - } - case IDC_DEL3: - { - DeleteControl(s->port, CID_BRAKE); - break; - } - case IDC_DEL4: - { - DeleteControl(s->port, CID_HATUP); - break; - } - case IDC_DEL5: - { - DeleteControl(s->port, CID_HATDOWN); - break; - } - case IDC_DEL6: - { - DeleteControl(s->port, CID_HATLEFT); - break; - } - case IDC_DEL7: - { - DeleteControl(s->port, CID_HATRIGHT); - break; - } - case IDC_DEL8: - { - DeleteControl(s->port, CID_SQUARE); - break; - } - case IDC_DEL9: - { - DeleteControl(s->port, CID_TRIANGLE); - break; - } - case IDC_DEL10: - { - DeleteControl(s->port, CID_CROSS); - break; - } - case IDC_DEL11: - { - DeleteControl(s->port, CID_CIRCLE); - break; - } - case IDC_DEL12: - { - DeleteControl(s->port, CID_L1); - break; - } - case IDC_DEL13: - { - DeleteControl(s->port, CID_R1); - break; - } - case IDC_DEL14: - { - DeleteControl(s->port, CID_L2); - break; - } - case IDC_DEL15: - { - DeleteControl(s->port, CID_R2); - break; - } - case IDC_DEL16: - { - DeleteControl(s->port, CID_L3); - break; - } - case IDC_DEL17: - { - DeleteControl(s->port, CID_R3); - break; - } - case IDC_DEL18: - { - DeleteControl(s->port, CID_SELECT); - break; - } - case IDC_DEL19: - { - DeleteControl(s->port, CID_START); - break; - } - - - case IDC_PICTURELINK1: - { - ShellExecuteA(NULL, "open", "http://www.ecsimhardware.com", NULL, NULL, SW_SHOWNORMAL); - break; - } - case IDC_PICTURELINK2: - { - ShellExecuteA(NULL, "open", "http://www.ecsimshop.com", NULL, NULL, SW_SHOWNORMAL); - break; - } - case IDC_PICTURELINK3: - { - ShellExecuteA(NULL, "open", "http://www.tocaedit.com", NULL, NULL, SW_SHOWNORMAL); - break; - } - } - break; - - case WM_CLOSE: - { - dialogOpen = false; - EndFFBTest(); - FreeDirectInput(); - EndDialog(hWnd, 0); - } - break; - - case WM_DESTROY: - //PostQuitMessage(0); - return TRUE; - break; - case WM_HSCROLL: - s = (DXDlgSettings*)GetWindowLongPtr(hDlg, GWLP_USERDATA); - ApplyFilter(s->port); - break; - case WM_PAINT: - s = (DXDlgSettings*)GetWindowLongPtr(hDlg, GWLP_USERDATA); - OnPaint(s->port); - break; - } - - return FALSE; - } - - void ApplySettings(int port) - { - INVERTFORCES[port] = SendDlgItemMessage(hWnd, IDC_CHECK1, BM_GETCHECK, 0, 0); - useRamp = !!SendDlgItemMessage(hWnd, IDC_CHECK3, BM_GETCHECK, 0, 0); - GAINZ[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_GETPOS, 0, 0); - FFMULTI[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_GETPOS, 0, 0); - } - - void SaveDInputConfig(int port, const char* dev_type) - { - wchar_t section[256]; - swprintf_s(section, L"%S dinput %d", dev_type, port); - - ClearSection(section); - SaveSetting(section, TEXT("INVERTFORCES"), INVERTFORCES[port]); - -#ifdef _WIN32 - SaveSetting(section, TEXT("# CONTROL n"), str_to_wstr("GUID,MAPPING TYPE,MAPPED TO,INVERTED,HALF,LINEAR,OFFSET,DEADZONE")); -#else - SaveSetting(section, TEXT("# CONTROL n"), "GUID,MAPPING TYPE,MAPPED TO,INVERTED,HALF,LINEAR,OFFSET,DEADZONE"); -#endif - - for (auto& control : g_Controls[port]) - { - int cid = control.first; - const InputMapped& im = control.second; - auto joy = g_pJoysticks[im.index]; - - std::stringstream ss; - ss << joy->GetGUID() << "," << im.type << "," << im.mapped; - //SaveSetting(section, TEXT("ProductName"), joy->Product()); - - if (joy->GetControlType() == CT_JOYSTICK) - { - ss << "," << im.INVERTED - << "," << im.HALF - << "," << im.LINEAR - << "," << im.OFFSET - << "," << im.DEADZONE; - } - - swprintf_s(text, TEXT("CONTROL %i"), cid); -#ifdef _WIN32 - SaveSetting(section, text, str_to_wstr(ss.str())); -#else - SaveSetting(section, text, ss.str()); -#endif - } - - SaveSetting(section, TEXT("GAINZ"), GAINZ[port][0]); - SaveSetting(section, TEXT("FFMULTI"), FFMULTI[port][0]); - //only for config dialog - SaveSetting(section, TEXT("UseRamp"), useRamp); - } - - void LoadDInputConfig(int port, const char* dev_type) - { - wchar_t section[256]; - swprintf_s(section, L"%S dinput %d", dev_type, port); - - LoadSetting(section, TEXT("INVERTFORCES"), INVERTFORCES[port]); - if (!LoadSetting(section, TEXT("GAINZ"), GAINZ[port][0])) - GAINZ[port][0] = DI_FFNOMINALMAX; - - if (!LoadSetting(section, TEXT("FFMULTI"), FFMULTI[port][0])) - FFMULTI[port][0] = 0; - - try - { - swprintf_s(section, TEXT("%" SFMTs " dinput %d"), dev_type, port); - - for (int cid = 0; cid < CID_COUNT; cid++) - { - InputMapped im = {}; - bool found = false; - std::string control, guid, value; - std::stringstream ss; - - swprintf_s(text, TEXT("CONTROL %i"), cid); -#ifdef _WIN32 - std::wstring tmp; - bool res_control = LoadSetting(section, text, tmp); - control = wstr_to_str(tmp); - if (!res_control) - continue; -#else - if (!LoadSetting(section, text, control)) - continue; -#endif - - ss << control; - std::getline(ss, guid, ','); - - for (size_t i = 0; i < g_pJoysticks.size(); i++) - { - std::stringstream ss_guid; - ss_guid << g_pJoysticks[i]->GetGUID(); - if (ss_guid.str() == guid) - { - im.index = i; - found = true; - break; - } - } - - if (!found) - continue; - - std::getline(ss, value, ','); - im.type = (MappingType)std::stoi(value); - - if (im.type == MT_NONE) - { - continue; - } - - std::getline(ss, value, ','); - im.mapped = std::stoi(value); - - if (g_pJoysticks[im.index]->GetControlType() == CT_JOYSTICK) - { - std::getline(ss, value, ','); - im.INVERTED = std::stoi(value); - std::getline(ss, value, ','); - im.HALF = std::stoi(value); - std::getline(ss, value, ','); - im.LINEAR = std::stoi(value); - std::getline(ss, value, ','); - im.OFFSET = std::stoi(value); - std::getline(ss, value, ','); - im.DEADZONE = std::stoi(value); - } - - AddInputMap(port, (ControlID)cid, im); - } - } - catch (std::exception&) - { - } - - LoadSetting(section, TEXT("UseRamp"), useRamp); - } - - int DInputPad::Configure(int port, const char* dev_type, void* data) - { - Win32Handles h = *(Win32Handles*)data; - struct DXDlgSettings s; - s.port = port; - s.dev_type = dev_type; - if (strcmp(dev_type, BuzzDevice::TypeName()) == 0) - { - return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_BUZZ), h.hWnd, DxDialogProc, (LPARAM)&s); - } - if (strcmp(dev_type, KeyboardmaniaDevice::TypeName()) == 0) - { - return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_KEYBOARDMANIA), h.hWnd, DxDialogProc, (LPARAM)&s); - } - if (strcmp(dev_type, GametrakDevice::TypeName()) == 0) - { - return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_GAMETRAK), h.hWnd, DxDialogProc, (LPARAM)&s); - } - if (strcmp(dev_type, RealPlayDevice::TypeName()) == 0) - { - return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_REALPLAY), h.hWnd, DxDialogProc, (LPARAM)&s); - } - return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DIALOG1), h.hWnd, DxDialogProc, (LPARAM)&s); - } - - } // namespace dx -} // namespace usb_pad -#pragma warning(pop) diff --git a/pcsx2/USB/usb-pad/dx/dinput.cpp b/pcsx2/USB/usb-pad/dx/dinput.cpp deleted file mode 100644 index 10c040d9be..0000000000 --- a/pcsx2/USB/usb-pad/dx/dinput.cpp +++ /dev/null @@ -1,1100 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include -#include -#include "dx.h" - -#define SAFE_DELETE(p) \ - { \ - if (p) \ - { \ - delete (p); \ - (p) = NULL; \ - } \ - } -#define SAFE_RELEASE(p) \ - { \ - if (p) \ - { \ - (p)->Release(); \ - (p) = NULL; \ - } \ - } - -//dialog window stuff -extern HWND gsWnd; - -namespace usb_pad -{ - namespace dx - { - - static std::atomic refCount(0); - static bool useRamp = false; - - DWORD pid = 0; - DWORD old = 0; - - static LPDIRECTINPUT8 g_pDI = NULL; - - std::vector g_pJoysticks; - std::map g_Controls[2]; - - static DWORD rgdwAxes[1] = {DIJOFS_X}; //FIXME if steering uses two axes, then this needs DIJOFS_Y too? - static LONG rglDirection[1] = {0}; - - //only two effect (constant force, spring) - static bool FFB[2] = {false, false}; - static LPDIRECTINPUTEFFECT g_pEffectConstant[2] = {0, 0}; - static LPDIRECTINPUTEFFECT g_pEffectSpring[2] = {0, 0}; - static LPDIRECTINPUTEFFECT g_pEffectFriction[2] = {0, 0}; //DFP mode only - static LPDIRECTINPUTEFFECT g_pEffectRamp[2] = {0, 0}; - static LPDIRECTINPUTEFFECT g_pEffectDamper[2] = {0, 0}; - static DWORD g_dwNumForceFeedbackAxis[4] = {0}; - static DIEFFECT eff; - static DIEFFECT effSpring; - static DIEFFECT effFriction; - static DIEFFECT effRamp; - static DIEFFECT effDamper; - static DICONSTANTFORCE cfw; - static DICONDITION cSpring; - static DICONDITION cFriction; - static DIRAMPFORCE cRamp; - static DICONDITION cDamper; - - std::ostream& operator<<(std::ostream& os, REFGUID guid) - { - std::ios_base::fmtflags f(os.flags()); - os << std::uppercase; - os.width(8); - os << std::hex << guid.Data1 << '-'; - - os.width(4); - os << std::hex << guid.Data2 << '-'; - - os.width(4); - os << std::hex << guid.Data3 << '-'; - - os.width(2); - os << std::hex - << static_cast(guid.Data4[0]) - << static_cast(guid.Data4[1]) - << '-' - << static_cast(guid.Data4[2]) - << static_cast(guid.Data4[3]) - << static_cast(guid.Data4[4]) - << static_cast(guid.Data4[5]) - << static_cast(guid.Data4[6]) - << static_cast(guid.Data4[7]); - os.flags(f); - return os; - } - - LONG GetAxisValueFromOffset(int axis, const DIJOYSTATE2& j) - { - constexpr int LVX_OFFSET = 8; // count POVs or not? - switch (axis) - { - case 0: - return j.lX; - break; - case 1: - return j.lY; - break; - case 2: - return j.lZ; - break; - case 3: - return j.lRx; - break; - case 4: - return j.lRy; - break; - case 5: - return j.lRz; - break; - case 6: - return j.rglSlider[0]; - break; - case 7: - return j.rglSlider[1]; - break; - //case 8: return j.rgdwPOV[0]; break; - //case 9: return j.rgdwPOV[1]; break; - //case 10: return j.rgdwPOV[2]; break; - //case 11: return j.rgdwPOV[3]; break; - case LVX_OFFSET + 0: - return j.lVX; - break; /* 'v' as in velocity */ - case LVX_OFFSET + 1: - return j.lVY; - break; - case LVX_OFFSET + 2: - return j.lVZ; - break; - case LVX_OFFSET + 3: - return j.lVRx; - break; - case LVX_OFFSET + 4: - return j.lVRy; - break; - case LVX_OFFSET + 5: - return j.lVRz; - break; - case LVX_OFFSET + 6: - return j.rglVSlider[0]; - break; - case LVX_OFFSET + 7: - return j.rglVSlider[1]; - break; - case LVX_OFFSET + 8: - return j.lAX; - break; /* 'a' as in acceleration */ - case LVX_OFFSET + 9: - return j.lAY; - break; - case LVX_OFFSET + 10: - return j.lAZ; - break; - case LVX_OFFSET + 11: - return j.lARx; - break; - case LVX_OFFSET + 12: - return j.lARy; - break; - case LVX_OFFSET + 13: - return j.lARz; - break; - case LVX_OFFSET + 14: - return j.rglASlider[0]; - break; - case LVX_OFFSET + 15: - return j.rglASlider[1]; - break; - case LVX_OFFSET + 16: - return j.lFX; - break; /* 'f' as in force */ - case LVX_OFFSET + 17: - return j.lFY; - break; - case LVX_OFFSET + 18: - return j.lFZ; - break; - case LVX_OFFSET + 19: - return j.lFRx; - break; /* 'fr' as in rotational force aka torque */ - case LVX_OFFSET + 20: - return j.lFRy; - break; - case LVX_OFFSET + 21: - return j.lFRz; - break; - case LVX_OFFSET + 22: - return j.rglFSlider[0]; - break; - case LVX_OFFSET + 23: - return j.rglFSlider[1]; - break; - } - return 0; - } - - bool JoystickDevice::Poll() - { - HRESULT hr = 0; - if (m_device) - { - hr = m_device->Poll(); - if (FAILED(hr)) - { - hr = m_device->Acquire(); - //return SUCCEEDED(hr); - } - else - { - if (m_type == CT_JOYSTICK) - { - m_device->GetDeviceState(sizeof(m_controls.js2), &m_controls.js2); - } - else if (m_type == CT_MOUSE) - { - m_device->GetDeviceState(sizeof(m_controls.ms2), &m_controls.ms2); - } - else if (m_type == CT_KEYBOARD) - { - m_device->GetDeviceState(sizeof(m_controls.kbd), &m_controls.kbd); - } - return true; - } - } - return false; - } - - bool JoystickDevice::GetButton(int b) - { - if (m_type == CT_JOYSTICK) - { - if (b < ARRAY_SIZE(DIJOYSTATE2::rgbButtons) && m_controls.js2.rgbButtons[b] & 0x80) - return true; - - if (b >= ARRAY_SIZE(DIJOYSTATE2::rgbButtons)) - { - b -= ARRAY_SIZE(DIJOYSTATE2::rgbButtons); - int i = b / 4; - //hat switch cases (4 hat switches with 4 directions possible) surely a better way... but this would allow funky joysticks to work. - switch (b % 4) - { - case 0: - if ((m_controls.js2.rgdwPOV[i] <= 4500 || m_controls.js2.rgdwPOV[i] >= 31500) && m_controls.js2.rgdwPOV[i] != -1) - { - return true; - } - break; - case 1: - if (m_controls.js2.rgdwPOV[i] >= 4500 && m_controls.js2.rgdwPOV[i] <= 13500) - { - return true; - } - break; - case 2: - if (m_controls.js2.rgdwPOV[i] >= 13500 && m_controls.js2.rgdwPOV[i] <= 22500) - { - return true; - } - break; - case 3: - if (m_controls.js2.rgdwPOV[i] >= 22500 && m_controls.js2.rgdwPOV[i] <= 31500) - { - return true; - } - break; - } - } - } - else if (m_type == CT_KEYBOARD) - { - return (b < ARRAY_SIZE(m_controls.kbd) && m_controls.kbd[b] & 0x80); - } - else if (m_type == CT_MOUSE) - { - return (b < ARRAY_SIZE(DIMOUSESTATE2::rgbButtons) && m_controls.ms2.rgbButtons[b] & 0x80); - } - return false; - } - - LONG JoystickDevice::GetAxis(int a) - { - return GetAxisValueFromOffset(a, m_controls.js2); - } - - JoystickDevice::~JoystickDevice() - { - if (m_device) - { - m_device->Unacquire(); - m_device->Release(); - } - } - - void ReleaseFFB(int port) - { - if (g_pEffectConstant[port]) - g_pEffectConstant[port]->Stop(); - if (g_pEffectSpring[port]) - g_pEffectSpring[port]->Stop(); - if (g_pEffectFriction[port]) - g_pEffectFriction[port]->Stop(); - if (g_pEffectRamp[port]) - g_pEffectRamp[port]->Stop(); - if (g_pEffectDamper[port]) - g_pEffectDamper[port]->Stop(); - - SAFE_RELEASE(g_pEffectConstant[port]); - SAFE_RELEASE(g_pEffectSpring[port]); - SAFE_RELEASE(g_pEffectFriction[port]); - SAFE_RELEASE(g_pEffectRamp[port]); - SAFE_RELEASE(g_pEffectDamper[port]); - - FFB[port] = false; - } - - void AddInputMap(int port, int cid, const InputMapped& im) - { - g_Controls[port][cid] = im; - } - - void RemoveInputMap(int port, int cid) - { - g_Controls[port].erase(cid); //FIXME ini doesn't clear old entries duh - // override with MT_NONE instead - //g_Controls[port][cid].type = MT_NONE; - } - - bool GetInputMap(int port, int cid, InputMapped& im) - { - auto it = g_Controls[port].find(cid); - if (it != g_Controls[port].end()) - { - im = it->second; - return true; - } - return false; - } - - void CreateFFB(int port, LPDIRECTINPUTDEVICE8 device, DWORD axis) - { - ReleaseFFB(port); - - if (!device) - return; - - UpdateFFBSettings(port, device); - - rgdwAxes[0] = axis; - //LPDIRECTINPUTDEVICE8 device = joy->GetDevice(); - //create the constant force effect - ZeroMemory(&eff, sizeof(eff)); - ZeroMemory(&effSpring, sizeof(effSpring)); - ZeroMemory(&effFriction, sizeof(effFriction)); - ZeroMemory(&cfw, sizeof(cfw)); - ZeroMemory(&cSpring, sizeof(cSpring)); - ZeroMemory(&cFriction, sizeof(cFriction)); - ZeroMemory(&cRamp, sizeof(cRamp)); - - //constantforce - eff.dwSize = sizeof(eff); - eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; - eff.dwSamplePeriod = 0; - eff.dwGain = DI_FFNOMINALMAX; - eff.dwTriggerButton = DIEB_NOTRIGGER; - eff.dwTriggerRepeatInterval = 0; - eff.cAxes = countof(rgdwAxes); - eff.rgdwAxes = rgdwAxes; //TODO set actual "steering" axis though usually is DIJOFS_X - eff.rglDirection = rglDirection; - eff.dwStartDelay = 0; - eff.dwDuration = INFINITE; - - // copy default values - effSpring = eff; - effFriction = eff; - effRamp = eff; - effDamper = eff; - - cfw.lMagnitude = 0; - - eff.cbTypeSpecificParams = sizeof(cfw); - eff.lpvTypeSpecificParams = &cfw; - { - HRESULT hres = device->CreateEffect(GUID_ConstantForce, &eff, &g_pEffectConstant[port], NULL); - if (FAILED(hres)) - Console.Warning("USB: CreateEffect GUID_ConstantForce error: %x", hres); - } - - cSpring.lNegativeCoefficient = 0; - cSpring.lPositiveCoefficient = 0; - - effSpring.cbTypeSpecificParams = sizeof(cSpring); - effSpring.lpvTypeSpecificParams = &cSpring; - { - HRESULT hres = device->CreateEffect(GUID_Spring, &effSpring, &g_pEffectSpring[port], NULL); - if (FAILED(hres)) - Console.Warning("USB: CreateEffect GUID_Spring error: %x", hres); - } - - effFriction.cbTypeSpecificParams = sizeof(cFriction); - effFriction.lpvTypeSpecificParams = &cFriction; - { - HRESULT hres = device->CreateEffect(GUID_Friction, &effFriction, &g_pEffectFriction[port], NULL); - if (FAILED(hres)) - Console.Warning("USB: CreateEffect GUID_Friction error: %x", hres); - } - - effRamp.cbTypeSpecificParams = sizeof(cRamp); - effRamp.lpvTypeSpecificParams = &cRamp; - { - HRESULT hres = device->CreateEffect(GUID_RampForce, &effRamp, &g_pEffectRamp[port], NULL); - if (FAILED(hres)) - Console.Warning("USB: CreateEffect GUID_RampForce error: %x", hres); - } - - effDamper.cbTypeSpecificParams = sizeof(cDamper); - effDamper.lpvTypeSpecificParams = &cDamper; - { - HRESULT hres = device->CreateEffect(GUID_Damper, &effDamper, &g_pEffectDamper[port], NULL); - if (FAILED(hres)) - Console.Warning("USB: CreateEffect GUID_Damper error: %x", hres); - } - - FFB[port] = true; - - //start the effect - if (g_pEffectConstant[port]) - { - g_pEffectConstant[port]->Start(1, 0); - } - } - - bool UpdateFFBSettings(int port, LPDIRECTINPUTDEVICE8 device) - { - DIPROPDWORD prop { sizeof(prop), sizeof(prop.diph) }; - prop.diph.dwObj = 0; - prop.diph.dwHow = DIPH_DEVICE; - prop.dwData = std::clamp(GAINZ[port][0], 0, DI_FFNOMINALMAX); - return SUCCEEDED(device->SetProperty(DIPROP_FFGAIN, &prop.diph)); - } - - BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, - VOID* pContext) - { - HRESULT hr; - - // Obtain an interface to the enumerated joystick. - LPDIRECTINPUTDEVICE8 joy = nullptr; - hr = g_pDI->CreateDevice(pdidInstance->guidInstance, &joy, NULL); - if (SUCCEEDED(hr) && joy) - g_pJoysticks.push_back(new JoystickDevice(CT_JOYSTICK, joy, pdidInstance->guidInstance, pdidInstance->tszProductName)); - - return DIENUM_CONTINUE; - } - - BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, - VOID* pContext) - { - HRESULT hr; - LPDIRECTINPUTDEVICE8 pWheel = (LPDIRECTINPUTDEVICE8)pContext; - // For axes that are returned, set the DIPROP_RANGE property for the - // enumerated axis in order to scale min/max values. - if (pdidoi->dwType & DIDFT_AXIS) - { - DIPROPRANGE diprg { sizeof(diprg), sizeof(diprg.diph) }; - diprg.diph.dwHow = DIPH_BYID; - diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis - diprg.lMin = 0; - diprg.lMax = 65535; - - // Set the range for the axis (not used, DX defaults 65535 all axis) - if (FAILED(hr = pWheel->SetProperty(DIPROP_RANGE, &diprg.diph))) - return DIENUM_STOP; - - //DIPROPDWORD dipdw; - //dipdw.diph.dwSize = sizeof(DIPROPDWORD); - //dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); - //dipdw.diph.dwHow = DIPH_BYID; //DIPH_DEVICE; - //dipdw.diph.dwObj = pdidoi->dwType; //0; - //dipdw.dwData = DIPROPAXISMODE_ABS; - - //if (FAILED(hr = pWheel->SetProperty(DIPROP_AXISMODE, &dipdw.diph))) - // return DIENUM_CONTINUE; - - //dipdw.dwData = 0; - //if (FAILED(hr = pWheel->SetProperty(DIPROP_DEADZONE, &dipdw.diph))) - // return DIENUM_CONTINUE; - - //dipdw.dwData = DI_FFNOMINALMAX; - //if (FAILED(hr = pWheel->SetProperty(DIPROP_SATURATION, &dipdw.diph))) - // return DIENUM_CONTINUE; - } - - return DIENUM_CONTINUE; - } - - //read all joystick states - void PollDevices() - { - for (auto& joy : g_pJoysticks) - { - joy->Poll(); - } - } - - //the non-linear filter (input/output 0.0-1.0 only) (parameters -50 to +50, expanded with PRECMULTI) - float FilterControl(float input, LONG linear, LONG offset, LONG dead) - { - //ugly, but it works gooood - - //format+shorten variables - float lf = float(linear) / PRECMULTI; - float hs = 0; - if (linear > 0) - hs = 1.0f - ((lf * 2) * 0.01f); - else - hs = 1.0f - (abs(lf * 2) * 0.01f); - - float hs2 = (offset + 50 * PRECMULTI) / PRECMULTI * 0.01f; - float v = input; - float d = float(dead) / PRECMULTI * 0.005f; - - //format and apply deadzone - v = (v * (1.0f + (d * 2.0f))) - d; - - //clamp - if (v < 0.0f) - v = 0.0f; - if (v > 1.0f) - v = 1.0f; - - //clamp negdead - //if (v == -d) v = 0.0; - if (fabs(v + d) < FLT_EPSILON) - v = 0.0f; - - //possibilities - float c1 = v - (1.0f - (pow((1.0f - v), (1.0f / hs)))); - float c2 = v - pow(v, hs); - float c3 = float(v - (1.0 - (pow((1.0 - v), hs)))); - float c4 = ((v - pow(v, (1.0f / hs)))); - float res = 0; - - if (linear < 0) - { - res = v - (((1.0f - hs2) * c3) + (hs2 * c4)); //get negative result - } - else - { - res = v - (((1.0f - hs2) * c1) + (hs2 * c2)); //get positive result - } - - //return our result - return res; - } - - float ReadAxis(const InputMapped& im) - { - assert(im.index < g_pJoysticks.size()); - if (im.index >= g_pJoysticks.size()) - return 0; - - LONG value = 0; - if (im.type == MT_AXIS) - value = g_pJoysticks[im.index]->GetAxis(im.mapped); - else if (im.type == MT_BUTTON && g_pJoysticks[im.index]->GetButton(im.mapped)) // for the lulz - { - value = USHRT_MAX; - } - - - float retval = 0; - - if (im.HALF > 60000) // origin somewhere near top - { - retval = (65535 - value) * (1.0f / 65535); - } - else if (im.HALF > 26000 && im.HALF < 38000) // origin somewhere near center - { - if (im.INVERTED) - retval = (value - 32767) * (1.0f / 32767); - else - retval = (32767 - value) * (1.0f / 32767); - } - else if (im.HALF >= 0 && im.HALF < 4000) // origin somewhere near bottom - { - retval = value * (1.0f / 65535); - } - - if (retval < 0.0f) - retval = 0.0f; - - return retval; - } - - float ReadAxis(int port, int cid) - { - InputMapped im; - if (!GetInputMap(port, cid, im)) - return 0; - return ReadAxis(im); - } - - //using both above functions - float ReadAxisFiltered(int port, int cid) - { - InputMapped im; - if (!GetInputMap(port, cid, im)) - return 0; - return FilterControl(ReadAxis(im), im.LINEAR, im.OFFSET, im.DEADZONE); - } - - void AutoCenter(LPDIRECTINPUTDEVICE8 device, bool onoff) - { - if (!device) - return; - //disable the auto-centering spring. - DIPROPDWORD dipdw { sizeof(dipdw), sizeof(dipdw.diph) }; - dipdw.diph.dwObj = 0; - dipdw.diph.dwHow = DIPH_DEVICE; - dipdw.dwData = onoff ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF; - - device->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph); - } - - void SetRamp(int port, const ramp& var) - { - } - - void SetRampVariable(int port, int forceids, const variable& var) - { - if (!FFB[port]) - return; - - // one main loop is 2ms, too erratic - effRamp.dwDuration = 2000 * (var.t1 + 1) * 25; - - // Force0 only (Force2 is Y axis?) - if (forceids & 1) - { - int force = var.l1; - int dir = (var.d1 & 1 ? 1 : -1); - - if (INVERTFORCES[port]) - { - cRamp.lStart = (127 - force) * DI_FFNOMINALMAX / 127; - int sign = 1; - if (cRamp.lStart < 0) - sign = -1; // pull to force's direction? - cRamp.lEnd = sign * DI_FFNOMINALMAX * dir; - } - else - { - cRamp.lStart = -(127 - force) * DI_FFNOMINALMAX / 127; - //int sign = -1; - //if (cRamp.lStart < 0) sign = 1; // pull to force's direction? - //cRamp.lEnd = sign * DI_FFNOMINALMAX * dir; // or to center? - cRamp.lEnd = -(127 - (force + /* var.t1 **/ var.s1 * dir)) * DI_FFNOMINALMAX / 127; - } - } - - if (g_pEffectRamp[port]) - g_pEffectRamp[port]->SetParameters(&effRamp, - DIEP_TYPESPECIFICPARAMS | DIEP_START | DIEP_DURATION); - } - - void DisableRamp(int port) - { - if (g_pEffectRamp[port]) - g_pEffectRamp[port]->Stop(); - } - - // IDK - void SetSpringSlopeForce(int port, const spring& spring) - { - cSpring.lOffset = 0; - cSpring.lNegativeCoefficient = (spring.s1 & 1 ? -1 : 1) * spring.k1 * 10000 / 15; - cSpring.lPositiveCoefficient = (spring.s2 & 1 ? -1 : 1) * spring.k2 * 10000 / 15; - cSpring.dwNegativeSaturation = spring.dead1 * 10000 / 0xFF; - cSpring.dwPositiveSaturation = spring.dead2 * 10000 / 0xFF; - cSpring.lDeadBand = 0; - - if (g_pEffectSpring[port]) - g_pEffectSpring[port]->SetParameters(&effSpring, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - - void JoystickDeviceFF::SetConstantForce(int level) - { - - //FIXME either this or usb-pad-ff was inverted - if (INVERTFORCES[m_port]) - cfw.lMagnitude = -level * DI_FFNOMINALMAX / SHRT_MAX; - else - cfw.lMagnitude = level * DI_FFNOMINALMAX / SHRT_MAX; - - if (FFMULTI[m_port][0] > 0) - cfw.lMagnitude *= 1 + FFMULTI[m_port][0]; - - if (g_pEffectConstant[m_port]) - { - g_pEffectConstant[m_port]->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START); - - //DWORD flags; - //g_pEffectConstant->GetEffectStatus(&flags); - - //if(!(flags & DIEGES_PLAYING)) - //{ - // InitDI(); - //} - } - } - - void JoystickDeviceFF::SetSpringForce(const parsed_ff_data& ff) - { - cSpring.dwNegativeSaturation = ff.u.condition.left_saturation * DI_FFNOMINALMAX / SHRT_MAX; - cSpring.dwPositiveSaturation = ff.u.condition.right_saturation * DI_FFNOMINALMAX / SHRT_MAX; - - cSpring.lNegativeCoefficient = ff.u.condition.left_coeff * DI_FFNOMINALMAX / SHRT_MAX; - cSpring.lPositiveCoefficient = ff.u.condition.right_coeff * DI_FFNOMINALMAX / SHRT_MAX; - - cSpring.lOffset = ff.u.condition.center * DI_FFNOMINALMAX / SHRT_MAX; - cSpring.lDeadBand = ff.u.condition.deadband * DI_FFNOMINALMAX / USHRT_MAX; - - if (g_pEffectSpring[m_port]) - g_pEffectSpring[m_port]->SetParameters(&effSpring, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - - void JoystickDeviceFF::SetDamperForce(const parsed_ff_data& ff) - { - cDamper.dwNegativeSaturation = ff.u.condition.left_saturation * DI_FFNOMINALMAX / SHRT_MAX; - cDamper.dwPositiveSaturation = ff.u.condition.right_saturation * DI_FFNOMINALMAX / SHRT_MAX; - - cDamper.lNegativeCoefficient = ff.u.condition.left_coeff * DI_FFNOMINALMAX / SHRT_MAX; - cDamper.lPositiveCoefficient = ff.u.condition.right_coeff * DI_FFNOMINALMAX / SHRT_MAX; - - cDamper.lOffset = ff.u.condition.center * DI_FFNOMINALMAX / SHRT_MAX; - cDamper.lDeadBand = ff.u.condition.deadband * DI_FFNOMINALMAX / USHRT_MAX; - - if (g_pEffectDamper[m_port]) - g_pEffectDamper[m_port]->SetParameters(&effDamper, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - - //LG driver converts it into high-precision damper instead, hmm - void JoystickDeviceFF::SetFrictionForce(const parsed_ff_data& ff) - { - cFriction.dwNegativeSaturation = ff.u.condition.left_saturation * DI_FFNOMINALMAX / SHRT_MAX; - cFriction.dwPositiveSaturation = ff.u.condition.right_saturation * DI_FFNOMINALMAX / SHRT_MAX; - - cFriction.lNegativeCoefficient = ff.u.condition.left_coeff * DI_FFNOMINALMAX / SHRT_MAX; - cFriction.lPositiveCoefficient = ff.u.condition.right_coeff * DI_FFNOMINALMAX / SHRT_MAX; - - cFriction.lOffset = ff.u.condition.center * DI_FFNOMINALMAX / SHRT_MAX; - cFriction.lDeadBand = ff.u.condition.deadband * DI_FFNOMINALMAX / USHRT_MAX; - - if (g_pEffectFriction[m_port]) - g_pEffectFriction[m_port]->SetParameters(&effFriction, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - - void JoystickDeviceFF::DisableForce(EffectID force) - { - switch (force) - { - case EFF_CONSTANT: - if (g_pEffectConstant[m_port]) - g_pEffectConstant[m_port]->Stop(); - break; - case EFF_SPRING: - if (g_pEffectSpring[m_port]) - g_pEffectSpring[m_port]->Stop(); - break; - case EFF_DAMPER: - if (g_pEffectDamper[m_port]) - g_pEffectDamper[m_port]->Stop(); - break; - case EFF_FRICTION: - if (g_pEffectFriction[m_port]) - g_pEffectFriction[m_port]->Stop(); - break; - case EFF_RUMBLE: - break; - } - } - - void JoystickDeviceFF::SetAutoCenter(int value) - { - InputMapped im; - LPDIRECTINPUTDEVICE8 dev = nullptr; - if (GetInputMap(m_port, CID_STEERING, im)) - dev = g_pJoysticks[im.index]->GetDevice(); - - AutoCenter(dev, value > 0); - } - - void FreeDirectInput() - { - if (!refCount || --refCount > 0) - return; - - ReleaseFFB(0); - ReleaseFFB(1); - - // Release any DirectInput objects. - for (auto joy : g_pJoysticks) - delete joy; - g_pJoysticks.clear(); - - SAFE_RELEASE(g_pDI); - didDIinit = false; - } - - //initialize all available devices - HRESULT InitDirectInput(HWND hWindow, int port) - { - - HRESULT hr; - LPDIRECTINPUTDEVICE8 pKeyboard = NULL; - LPDIRECTINPUTDEVICE8 pMouse = NULL; - - //release any previous resources - - if (refCount == 0) - { - // Create a DInput object - if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, - IID_IDirectInput8, (VOID**)&g_pDI, NULL))) - return hr; - - //Create Keyboard - g_pDI->CreateDevice(GUID_SysKeyboard, &pKeyboard, NULL); - if (pKeyboard) - { - pKeyboard->SetDataFormat(&c_dfDIKeyboard); - pKeyboard->SetCooperativeLevel(hWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); - pKeyboard->Acquire(); - g_pJoysticks.push_back(new JoystickDevice(CT_KEYBOARD, pKeyboard, GUID_SysKeyboard, TEXT("SysKeyboard"))); - } - - //Create Mouse - g_pDI->CreateDevice(GUID_SysMouse, &pMouse, NULL); - if (pMouse) - { - pMouse->SetDataFormat(&c_dfDIMouse2); - pMouse->SetCooperativeLevel(hWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); - pMouse->Acquire(); - g_pJoysticks.push_back(new JoystickDevice(CT_MOUSE, pMouse, GUID_SysMouse, TEXT("SysMouse"))); - } - - //enumerate attached only - g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY); - - //loop through all attached joysticks - for (size_t i = 0; i < g_pJoysticks.size(); i++) - { - auto joy = g_pJoysticks[i]; - auto device = joy->GetDevice(); - device->SetDataFormat(&c_dfDIJoystick2); - - DIDEVCAPS diCaps { sizeof(diCaps) }; - device->GetCapabilities(&diCaps); - - if (diCaps.dwFlags & DIDC_FORCEFEEDBACK) - { - //Exclusive - device->SetCooperativeLevel(hWindow, DISCL_EXCLUSIVE | DISCL_BACKGROUND); - - /*DIDEVICEINSTANCE instance_; - ZeroMemory(&instance_, sizeof(DIDEVICEINSTANCE)); - instance_.dwSize = sizeof(DIDEVICEINSTANCE); - g_pJoysticks[i]->GetDeviceInfo(&instance_); - std::stringstream str; - str << instance_.guidInstance;*/ - } - else - device->SetCooperativeLevel(hWindow, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); - - device->EnumObjects(EnumObjectsCallback, device, DIDFT_ALL); - device->Acquire(); - } - } - - ++refCount; - - didDIinit = true; - return S_OK; - } - - HWND GetWindowHandle(DWORD tPID) - { - //Get first window handle - HWND res = FindWindow(NULL, NULL); - DWORD mPID = 0; - while (res != 0) - { - if (!GetParent(res)) - { - GetWindowThreadProcessId(res, &mPID); - if (mPID == tPID) - return res; - } - res = GetWindow(res, GW_HWNDNEXT); - } - return NULL; - } - - bool FindFFDevice(int port) - { - InputMapped im; - if (!GetInputMap(port, CID_STEERING, im)) - return false; - - auto device = g_pJoysticks[im.index]->GetDevice(); - DIDEVCAPS diCaps { sizeof(diCaps) }; - device->GetCapabilities(&diCaps); - - //has ffb? - if (!FFB[port] && (diCaps.dwFlags & DIDC_FORCEFEEDBACK)) - { - - //FIXME im.mapped is offset to GetAxisValueFromOffset, compatibility with DIEFFECT::rgdwAxes is questionable after DIJOYSTATE2::rglSlider - CreateFFB(port, device, im.mapped); - - AutoCenter(device, false); //TODO some games set autocenter. Figure out default for ones that don't. - - /*DIDEVICEINSTANCE instance_; - ZeroMemory(&instance_, sizeof(DIDEVICEINSTANCE)); - instance_.dwSize = sizeof(DIDEVICEINSTANCE); - g_pJoysticks[i]->GetDeviceInfo(&instance_); - std::stringstream str; - str << instance_.guidInstance;*/ - } - return FFB[port]; - } - - //use direct input - void InitDI(int port, const char* dev_type) - { - HWND hWin = nullptr; - - if (gsWnd) - { - hWin = gsWnd; - } - else - { - pid = GetCurrentProcessId(); - while (hWin == 0) - { - hWin = GetWindowHandle(pid); - } - } - - // DirectInput needs a top-level window - InitDirectInput(GetAncestor(hWin, GA_ROOT), port); - LoadDInputConfig(port, dev_type); - FindFFDevice(port); - } - - bool GetControl(int port, int id) - { - InputMapped im; - if (!GetInputMap(port, id, im)) - return false; - - assert(im.index < g_pJoysticks.size()); - if (im.index >= g_pJoysticks.size()) - return false; - - auto joy = g_pJoysticks[im.index]; - - if (im.type == MT_AXIS) - { - return ReadAxisFiltered(port, id) >= 0.5f; - } - else if (im.type == MT_BUTTON) - { - return joy->GetButton(im.mapped); - } - return false; - } - - float GetAxisControl(int port, ControlID id) - { - if (id == CID_STEERING) - { - //apply steering, single axis is split to two for filtering - if (ReadAxisFiltered(port, CID_STEERING) > 0.0) - { - return -ReadAxisFiltered(port, CID_STEERING); - } - else - { - if (ReadAxisFiltered(port, CID_STEERING_R) > 0.0) - { - return ReadAxisFiltered(port, CID_STEERING_R); - } - else - { - return 0; - } - } - } - - return ReadAxisFiltered(port, id); - } - - int32_t GetAxisControlUnfiltered(int port, ControlID id) - { - InputMapped im; - if (!GetInputMap(port, id, im)) - return 0; - - assert(im.index < g_pJoysticks.size()); - if (im.index >= g_pJoysticks.size()) - return 0; - - LONG value = 0; - if (im.type == MT_AXIS) - { - value = g_pJoysticks[im.index]->GetAxis(im.mapped); - } - return value; - } - - //set left/right ffb torque - HRESULT SetConstantForce(int port, LONG magnitude) - { - if (!FFB[port] || !g_pEffectConstant[port]) - return DIERR_NOTINITIALIZED; - - if (INVERTFORCES[port]) - cfw.lMagnitude = -magnitude; - else - cfw.lMagnitude = magnitude; - - if (FFMULTI[port][0] > 0) - cfw.lMagnitude *= 1 + FFMULTI[port][0]; - - return g_pEffectConstant[port]->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - - bool StartTestForce(int port) - { - InputMapped im; - LPDIRECTINPUTDEVICE8 dev = nullptr; - if (GetInputMap(port, CID_STEERING, im)) - dev = g_pJoysticks[im.index]->GetDevice(); - - // Gain value may have changed, so update it for the constant force effect - return UpdateFFBSettings(port, dev); - } - - bool UpdateTestForce(int port, unsigned int stage) - { - // FFB test ticks every 500ms and goes as follows: - // Turn right, wait 500ms, turn left, wait 1000ms, turn right, wait 500ms, end - if (stage == 0) - { - return SUCCEEDED(SetConstantForce(port, DI_FFNOMINALMAX / 3)); - } - if (stage == 1) - { - return SUCCEEDED(SetConstantForce(port, -DI_FFNOMINALMAX / 3)); - } - if (stage == 2) - { - // Do nothing, as we wait 1000ms - return true; - } - if (stage == 3) - { - return SUCCEEDED(SetConstantForce(port, DI_FFNOMINALMAX / 3)); - } - return false; - } - - bool EndTestForce(int port) - { - return SUCCEEDED(SetConstantForce(port, 0)); - } - - } // namespace dx -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/dx/dx.h b/pcsx2/USB/usb-pad/dx/dx.h deleted file mode 100644 index 138fc94861..0000000000 --- a/pcsx2/USB/usb-pad/dx/dx.h +++ /dev/null @@ -1,231 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "USB/usb-pad/usb-pad.h" -#include "USB/configuration.h" -#include "USB/usb-pad/lg/lg_ff.h" - -#define DINPUT_AXES_COUNT 32 - -namespace usb_pad -{ - namespace dx - { - //dinput control mappings - - static const DWORD PRECMULTI = 100; //floating point precision multiplier, 100 - two digit precision after comma - - extern int32_t GAINZ[2][1]; - extern int32_t FFMULTI[2][1]; - extern int32_t INVERTFORCES[2]; - - static bool didDIinit = false; //we have a handle - - std::ostream& operator<<(std::ostream& os, REFGUID guid); - - enum ControlID - { - CID_STEERING, - CID_STEERING_R, - CID_THROTTLE, - CID_BRAKE, - CID_HATUP, - CID_HATDOWN, - CID_HATLEFT, - CID_HATRIGHT, - CID_SQUARE, - CID_TRIANGLE, - CID_CROSS, - CID_CIRCLE, - CID_L1, - CID_R1, - CID_L2, - CID_R2, - CID_L3, - CID_R3, - CID_SELECT, - CID_START, - CID_BUTTON20, - CID_BUTTON21, - CID_BUTTON22, - CID_BUTTON23, - CID_BUTTON24, - CID_BUTTON25, - CID_BUTTON26, - CID_BUTTON27, - CID_BUTTON28, - CID_BUTTON29, - CID_BUTTON30, - CID_COUNT, - }; - - // Maybe merge with JoystickDevice - class JoystickDeviceFF : public FFDevice - { - public: - JoystickDeviceFF(int port) - : m_port(port) - { - } - ~JoystickDeviceFF() {} - - void SetConstantForce(int level); - void SetSpringForce(const parsed_ff_data& ff); - void SetDamperForce(const parsed_ff_data& ff); - void SetFrictionForce(const parsed_ff_data& ff); - void SetAutoCenter(int value); - void DisableForce(EffectID force); - - private: - int m_port; - }; - - enum ControlType - { - CT_NONE, - CT_KEYBOARD, - CT_MOUSE, - CT_JOYSTICK, - }; - - enum MappingType - { - MT_NONE = 0, //TODO leave for sanity checking? - MT_AXIS, - MT_BUTTON, - }; - - struct InputMapped - { - size_t index; //index into g_pJoysticks - MappingType type = MT_NONE; - int32_t mapped; //device axis/button - bool INVERTED; - int32_t HALF; - int32_t LINEAR; - int32_t OFFSET; - int32_t DEADZONE; - }; - - class JoystickDevice - { - public: - JoystickDevice(ControlType type, LPDIRECTINPUTDEVICE8 device, GUID guid, TSTDSTRING name) - : m_type(type) - , m_guid(guid) - , m_device(device) - , m_product(name) - { - } - - bool Poll(); - - /*void GetDeviceState(size_t sz, void *ptr) - { - if (sz == sizeof(DIJOYSTATE2) && ptr) - *ptr = m_jstate; - }*/ - - DIJOYSTATE2 GetDeviceState() - { - //assert(m_type == CT_JOYSTICK); - return m_controls.js2; - } - - HRESULT GetDeviceState(DWORD sz, LPVOID ptr) - { - return m_device->GetDeviceState(sz, ptr); - } - - bool GetButton(int b); - LONG GetAxis(int a); - - LPDIRECTINPUTDEVICE8 GetDevice() - { - return m_device; - } - - GUID GetGUID() - { - return m_guid; - } - - const TSTDSTRING& Product() const - { - return m_product; - } - - ControlType GetControlType() { return m_type; } - - ~JoystickDevice(); - - private: - GUID m_guid; - TSTDSTRING m_product; - LPDIRECTINPUTDEVICE8 m_device; - ControlType m_type = CT_NONE; - union - { - DIJOYSTATE2 js2; - DIMOUSESTATE2 ms2; - BYTE kbd[256]; - } m_controls = {}; - }; - - extern std::vector g_pJoysticks; - extern std::map g_Controls[2]; - - void ApplySettings(int port); - void LoadDInputConfig(int port, const char* dev_type); - void SaveDInputConfig(int port, const char* dev_type); - - void InitDI(int port, const char* dev_type); - HRESULT InitDirectInput(HWND hWindow, int port); - void FreeDirectInput(); - void PollDevices(); - float ReadAxis(const InputMapped& im); - float ReadAxis(int port, int axisid); - float FilterControl(float input, LONG linear, LONG offset, LONG dead); - - bool StartTestForce(int port); - bool UpdateTestForce(int port, unsigned int stage); - bool EndTestForce(int port); - - LONG GetAxisValueFromOffset(int axis, const DIJOYSTATE2& j); - bool GetControl(int port, int id); - float GetAxisControl(int port, ControlID id); - int32_t GetAxisControlUnfiltered(int port, ControlID id); - void CreateFFB(int port, LPDIRECTINPUTDEVICE8 device, DWORD axis); - bool FindFFDevice(int port); - bool UpdateFFBSettings(int port, LPDIRECTINPUTDEVICE8 device); - - void AddInputMap(int port, int cid, const InputMapped& im); - void RemoveInputMap(int port, int cid); - bool GetInputMap(int port, int cid, InputMapped& im); - - } // namespace dx -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp b/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp deleted file mode 100644 index e53f2f3575..0000000000 --- a/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "usb-pad-dx.h" -#include "dx.h" -#include - -namespace usb_pad -{ - namespace dx - { - - static bool bdown = false; - static DWORD calibrationtime = 0; - static int calidata = 0; - static bool alternate = false; - static bool calibrating = false; - - DInputPad::~DInputPad() { FreeDirectInput(); } - - int DInputPad::TokenIn(uint8_t* buf, int len) - { - int range = range_max(mType); - - // Setting to unpressed - ZeroMemory(&mWheelData, sizeof(wheel_data_t)); - mWheelData.steering = range >> 1; - mWheelData.clutch = 0xFF; - mWheelData.throttle = 0xFF; - mWheelData.brake = 0xFF; - mWheelData.hatswitch = 0x8; - - PollDevices(); - - if (mType == WT_BUZZ_CONTROLLER) - { - for (int i = 0; i < 20; i++) - { - if (GetControl(mPort, i)) - { - mWheelData.buttons |= 1 << i; - } - } - pad_copy_data(mType, buf, mWheelData); - return 5; - } - else if (mType == WT_GAMETRAK_CONTROLLER) - { - mWheelData.buttons |= GetControl(mPort, CID_HATLEFT) << 4; - mWheelData.clutch = GetAxisControlUnfiltered(mPort, CID_STEERING) >> 4; - mWheelData.throttle = GetAxisControlUnfiltered(mPort, CID_STEERING_R) >> 4; - mWheelData.brake = GetAxisControlUnfiltered(mPort, CID_THROTTLE) >> 4; - mWheelData.hatswitch = GetAxisControlUnfiltered(mPort, CID_BRAKE) >> 4; - mWheelData.hat_horz = GetAxisControlUnfiltered(mPort, CID_HATUP) >> 4; - mWheelData.hat_vert = GetAxisControlUnfiltered(mPort, CID_HATDOWN) >> 4; - pad_copy_data(mType, buf, mWheelData); - return 16; - } - else if (mType >= WT_REALPLAY_RACING && mType <= WT_REALPLAY_POOL) - { - for (int i = 0; i < 8; i++) - { - if (GetControl(mPort, i)) - { - mWheelData.buttons |= 1 << i; - } - } - mWheelData.clutch = GetAxisControlUnfiltered(mPort, CID_SQUARE) >> 4; - mWheelData.throttle = GetAxisControlUnfiltered(mPort, CID_TRIANGLE) >> 4; - mWheelData.brake = GetAxisControlUnfiltered(mPort, CID_CROSS) >> 4; - pad_copy_data(mType, buf, mWheelData); - return 19; - } - else if (mType == WT_KEYBOARDMANIA_CONTROLLER) - { - for (int i = 0; i < 31; i++) - { - if (GetControl(mPort, i)) - { - mWheelData.buttons |= 1 << i; - } - } - pad_copy_data(mType, buf, mWheelData); - return len; - } - - //Allow in both ports but warn in configure dialog that only one DX wheel is supported for now - //if(idx == 0){ - //mWheelData.steering = 8191 + (int)(GetControl(STEERING, false)* 8191.0f) ; - - if (calibrating) - { - //Alternate full extents - if (alternate) - calidata--; - else - calidata++; - - if (calidata > range - 1 || calidata < 1) - alternate = !alternate; //invert - - mWheelData.steering = calidata; //pass fake - - //breakout after 11 seconds - if (GetTickCount() - calibrationtime > 11000) - { - calibrating = false; - mWheelData.steering = range >> 1; - } - } - else - { - mWheelData.steering = (range >> 1) + std::lround(GetAxisControl(mPort, CID_STEERING) * (float)(range >> 1)); - } - - mWheelData.throttle = std::lround(255.f - (GetAxisControl(mPort, CID_THROTTLE) * 255.0f)); - mWheelData.brake = std::lround(255.f - (GetAxisControl(mPort, CID_BRAKE) * 255.0f)); - - if (GetControl(mPort, CID_CROSS)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_CROSS); - if (GetControl(mPort, CID_SQUARE)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_SQUARE); - if (GetControl(mPort, CID_CIRCLE)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_CIRCLE); - if (GetControl(mPort, CID_TRIANGLE)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_TRIANGLE); - if (GetControl(mPort, CID_R1)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_R1); - if (GetControl(mPort, CID_L1)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_L1); - if (GetControl(mPort, CID_R2)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_R2); - if (GetControl(mPort, CID_L2)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_L2); - - if (GetControl(mPort, CID_SELECT)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_SELECT); - if (GetControl(mPort, CID_START)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_START); - if (GetControl(mPort, CID_R3)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_R3); - if (GetControl(mPort, CID_L3)) - mWheelData.buttons |= 1 << convert_wt_btn(mType, PAD_L3); - - //diagonal - if (GetControl(mPort, CID_HATUP) && GetControl(mPort, CID_HATRIGHT)) - mWheelData.hatswitch = 1; - if (GetControl(mPort, CID_HATRIGHT) && GetControl(mPort, CID_HATDOWN)) - mWheelData.hatswitch = 3; - if (GetControl(mPort, CID_HATDOWN) && GetControl(mPort, CID_HATLEFT)) - mWheelData.hatswitch = 5; - if (GetControl(mPort, CID_HATLEFT) && GetControl(mPort, CID_HATUP)) - mWheelData.hatswitch = 7; - - //regular - if (mWheelData.hatswitch == 0x8) - { - if (GetControl(mPort, CID_HATUP)) - mWheelData.hatswitch = 0; - if (GetControl(mPort, CID_HATRIGHT)) - mWheelData.hatswitch = 2; - if (GetControl(mPort, CID_HATDOWN)) - mWheelData.hatswitch = 4; - if (GetControl(mPort, CID_HATLEFT)) - mWheelData.hatswitch = 6; - } - - pad_copy_data(mType, buf, mWheelData); - //} //if(idx ... - return len; - } - - int DInputPad::TokenOut(const uint8_t* data, int len) - { - const ff_data* ffdata = (const ff_data*)data; - bool hires = (mType == WT_DRIVING_FORCE_PRO || mType == WT_DRIVING_FORCE_PRO_1102); - ParseFFData(ffdata, hires); - - return len; - } - - int DInputPad::Open() - { - LoadSetting(mDevType, mPort, APINAME, TEXT("UseRamp"), mUseRamp); - InitDI(mPort, mDevType); - if (!mFFdev) - mFFdev = new JoystickDeviceFF(mPort /*, mUseRamp*/); - return 0; - } - - int DInputPad::Close() - { - FreeDirectInput(); - return 0; - } - - } // namespace dx -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/dx/usb-pad-dx.h b/pcsx2/USB/usb-pad/dx/usb-pad-dx.h deleted file mode 100644 index ecf1821134..0000000000 --- a/pcsx2/USB/usb-pad/dx/usb-pad-dx.h +++ /dev/null @@ -1,53 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "USB/usb-pad/padproxy.h" -#include "USB/Win32/Config_usb.h" - -namespace usb_pad -{ - namespace dx - { - - static const char* APINAME = "dinput"; - - class DInputPad : public Pad - { - public: - DInputPad(int port, const char* dev_type) - : Pad(port, dev_type) - , mUseRamp(0) - { - } - ~DInputPad(); - int Open(); - int Close(); - int TokenIn(uint8_t* buf, int len); - int TokenOut(const uint8_t* data, int len); - int Reset() { return 0; } - - static const TCHAR* Name() - { - return TEXT("DInput"); - } - - static int Configure(int port, const char* dev_type, void* data); - - private: - int32_t mUseRamp; - }; - - } // namespace dx -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/dx/versionproxy.h b/pcsx2/USB/usb-pad/dx/versionproxy.h deleted file mode 100644 index 412ef1f1d3..0000000000 --- a/pcsx2/USB/usb-pad/dx/versionproxy.h +++ /dev/null @@ -1,204 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by versionproxy.rc -// -#define IDC_STATIC -1 -#define IDOK2 2 -#define IDD_DLG_BUZZ 114 -#define IDD_DLG_KEYBOARDMANIA 115 -#define IDD_DLG_GAMETRAK 116 -#define IDD_DLG_REALPLAY 120 -#define IDD_DIALOG1 202 -#define IDC_DEL0 1001 -#define IDC_ASS0 1002 -#define IDC_DEL1 1003 -#define IDC_ASS1 1004 -#define IDC_DEL2 1005 -#define IDC_ASS2 1006 -#define IDC_DEL3 1007 -#define IDC_ASS3 1008 -#define IDC_DEL4 1009 -#define IDC_ASS4 1010 -#define IDC_DEL5 1011 -#define IDC_ASS5 1012 -#define IDC_COMBO4 1013 -#define IDC_LABEL0 1014 -#define IDC_BUTTON13 1015 -#define IDC_DEL8 1015 -#define IDC_BUTTON14 1016 -#define IDC_ASS8 1016 -#define IDC_BUTTON15 1017 -#define IDC_DEL9 1017 -#define IDC_BUTTON16 1018 -#define IDC_ASS9 1018 -#define IDC_BUTTON17 1019 -#define IDC_DEL10 1019 -#define IDC_BUTTON18 1020 -#define IDC_ASS10 1020 -#define IDC_BUTTON19 1021 -#define IDC_DEL11 1021 -#define IDC_BUTTON20 1022 -#define IDC_ASS11 1022 -#define IDC_BUTTON21 1023 -#define IDC_DEL12 1023 -#define IDC_BUTTON22 1024 -#define IDC_ASS12 1024 -#define IDC_BUTTON23 1025 -#define IDC_DEL13 1025 -#define IDC_BUTTON24 1026 -#define IDC_ASS13 1026 -#define IDC_BUTTON25 1027 -#define IDC_DEL14 1027 -#define IDC_BUTTON26 1028 -#define IDC_ASS14 1028 -#define IDC_BUTTON27 1029 -#define IDC_DEL6 1029 -#define IDC_BUTTON28 1030 -#define IDC_ASS6 1030 -#define IDC_BUTTON29 1031 -#define IDC_DEL7 1031 -#define IDC_BUTTON30 1032 -#define IDC_ASS7 1032 -#define IDC_LABEL1 1033 -#define IDC_LABEL2 1034 -#define IDC_LABEL3 1035 -#define IDC_LABEL4 1036 -#define IDC_LABEL5 1037 -#define IDC_LABEL6 1038 -#define IDC_LABEL7 1039 -#define IDC_LABEL8 1040 -#define IDC_LABEL9 1041 -#define IDC_LABEL10 1042 -#define IDC_LABEL11 1043 -#define IDC_LABEL12 1044 -#define IDC_LABEL13 1045 -#define IDC_LABEL14 1046 -#define IDC_LABEL15 1047 -#define IDC_EDIT1 1048 -#define IDC_DEL15 1048 -#define IDC_ASS15 1049 -#define IDC_LABEL16 1050 -#define IDC_DEL16 1051 -#define IDC_ASS16 1052 -#define IDC_GROUP1 1053 -#define IDC_GROUP2 1054 -#define IDC_GROUP3 1055 -#define IDC_EDIT4 1056 -#define IDC_EDIT5 1057 -#define IDC_PICTURE 1058 -#define IDC_SLIDER1 1059 -#define IDC_SLIDER2 1060 -#define IDC_COMBO1 1061 -#define IDC_COMBO2 1062 -#define IDC_SLIDER3 1062 -#define IDC_COMBO3 1063 -#define IDC_ASS17 1064 -#define IDC_LINEAR 1065 -#define IDC_OFFSET 1066 -#define IDC_LABEL18 1067 -#define IDC_GROUP4 1068 -#define IDC_GROUP5 1069 -#define IDC_GROUP6 1070 -#define IDC_GROUP7 1071 -#define IDC_GROUP8 1072 -#define IDC_GROUP9 1073 -#define IDC_GROUP10 1074 -#define IDC_GROUP11 1075 -#define IDC_GROUP12 1076 -#define IDC_GROUP13 1077 -#define IDC_GROUP14 1078 -#define IDC_GROUP15 1079 -#define IDC_GROUP16 1080 -#define IDC_GROUP17 1081 -#define IDC_GROUP18 1082 -#define IDC_GROUP19 1083 -#define IDC_GROUP20 1084 -#define IDC_GROUP21 1085 -#define IDC_DEADZONE 1086 -#define IDC_GROUP22 1087 -#define IDC_CHECK5 1088 -#define IDC_GROUP24 1088 -#define IDC_CHECK6 1089 -#define IDC_DEL18 1089 -#define IDC_CHECK7 1090 -#define IDC_ASS18 1090 -#define IDC_GROUP25 1091 -#define IDC_EDIT2 1092 -#define IDC_LABEL19 1092 -#define IDC_CHECK8 1093 -#define IDC_DEL19 1093 -#define IDC_IPADDRESS2 1094 -#define IDC_ASS19 1094 -#define IDC_EDIT3 1095 -#define IDC_CHECK9 1096 -#define IDC_IPADDRESS3 1097 -#define IDC_GROUP23 1098 -#define IDC_CHECK10 1099 -#define IDC_PICTURELINK1 1101 -#define IDC_PICTURELINK2 1102 -#define IDC_PICTURELINK3 1103 -#define IDC_BUTTON31 1105 -#define IDC_TEXT 1106 -#define IDC_LABEL17 1107 -#define IDC_GROUP26 1108 -#define IDC_DEL17 1109 -#define IDC_BUTTON1 1110 -#define IDC_CHECK1 1111 -#define IDC_CHECK3 1113 -#define IDC_SLIDER4 1114 -#define IDC_BUTTON3 1116 -#define IDC_SLIDER5 1117 -#define IDC_BZ_CTL1_LBL1 1118 -#define IDC_BZ_CTL1_LBL2 1119 -#define IDC_BZ_CTL1_LBL3 1120 -#define IDC_BZ_CTL1_LBL4 1121 -#define IDC_BZ_CTL1_LBL5 1122 -#define IDC_BZ_CTL2_LBL1 1123 -#define IDC_BZ_CTL2_LBL2 1124 -#define IDC_BZ_CTL2_LBL3 1125 -#define IDC_BZ_CTL2_LBL4 1126 -#define IDC_BZ_CTL2_LBL5 1127 -#define IDC_BZ_CTL3_LBL1 1128 -#define IDC_BZ_CTL3_LBL2 1129 -#define IDC_BZ_CTL3_LBL3 1130 -#define IDC_BZ_CTL3_LBL4 1131 -#define IDC_BZ_CTL3_LBL5 1132 -#define IDC_BZ_CTL4_LBL1 1133 -#define IDC_BZ_CTL4_LBL2 1134 -#define IDC_BZ_CTL4_LBL3 1135 -#define IDC_BZ_CTL4_LBL4 1136 -#define IDC_BZ_CTL4_LBL5 1137 -#define IDC_LABEL20 1138 -#define IDC_ASS20 1139 -#define IDC_LABEL21 1140 -#define IDC_ASS21 1141 -#define IDC_LABEL22 1142 -#define IDC_ASS22 1143 -#define IDC_LABEL23 1144 -#define IDC_ASS23 1145 -#define IDC_LABEL24 1146 -#define IDC_ASS24 1147 -#define IDC_LABEL25 1148 -#define IDC_ASS25 1149 -#define IDC_LABEL26 1150 -#define IDC_ASS26 1151 -#define IDC_LABEL27 1152 -#define IDC_ASS27 1153 -#define IDC_LABEL28 1154 -#define IDC_ASS28 1155 -#define IDC_LABEL29 1156 -#define IDC_ASS29 1157 -#define IDC_LABEL30 1158 -#define IDC_ASS30 1159 -#define IDC_DELALL 1160 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 121 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1137 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/pcsx2/USB/usb-pad/dx/versionproxy.rc b/pcsx2/USB/usb-pad/dx/versionproxy.rc deleted file mode 100644 index 2f9cfc4296..0000000000 --- a/pcsx2/USB/usb-pad/dx/versionproxy.rc +++ /dev/null @@ -1,464 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "versionproxy.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winresrc.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "versionproxy.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""WinResrc.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_DIALOG1 DIALOGEX 0, 0, 670, 309 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CTEXT "AXIS",IDC_GROUP1,6,6,186,12 - GROUPBOX "STEER LEFT",IDC_GROUP4,6,18,186,36 - LTEXT "1/1/1/1",IDC_LABEL0,12,42,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS0,78,30,54,18 - PUSHBUTTON "DELETE",IDC_DEL0,132,30,54,18 - GROUPBOX "STEER RIGHT",IDC_GROUP5,6,54,186,36 - LTEXT "1/1/1/1",IDC_LABEL1,12,78,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS1,78,66,54,18 - PUSHBUTTON "DELETE",IDC_DEL1,132,66,54,18 - GROUPBOX "THROTTLE",IDC_GROUP6,6,90,186,36 - LTEXT "1/1/1/1",IDC_LABEL2,12,114,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS2,78,102,54,18 - PUSHBUTTON "DELETE",IDC_DEL2,132,102,54,18 - GROUPBOX "BRAKE",IDC_GROUP7,6,126,186,36 - LTEXT "1/1/1/1",IDC_LABEL3,12,150,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS3,78,138,54,18 - PUSHBUTTON "DELETE",IDC_DEL3,132,138,54,18 - GROUPBOX "HAT UP",IDC_GROUP8,6,162,186,36 - LTEXT "1/1/1/1",IDC_LABEL4,12,186,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS4,78,174,54,18 - PUSHBUTTON "DELETE",IDC_DEL4,132,174,54,18 - GROUPBOX "HAT DOWN",IDC_GROUP9,6,198,186,36 - LTEXT "1/1/1/1",IDC_LABEL5,12,222,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS5,78,210,54,18 - PUSHBUTTON "DELETE",IDC_DEL5,132,210,54,18 - GROUPBOX "HAT LEFT",IDC_GROUP10,6,234,186,36 - LTEXT "1/1/1/1",IDC_LABEL6,12,258,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS6,78,246,54,18 - PUSHBUTTON "DELETE",IDC_DEL6,132,246,54,18 - GROUPBOX "HAT RIGHT",IDC_GROUP11,6,270,186,36 - LTEXT "1/1/1/1",IDC_LABEL7,12,294,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS7,78,282,54,18 - PUSHBUTTON "DELETE",IDC_DEL7,132,282,54,18 - CTEXT "BUTTON",IDC_GROUP2,198,6,240,12 - GROUPBOX "SQUARE / X / GREEN",IDC_GROUP12,198,18,120,48 - LTEXT "1/1/1/1",IDC_LABEL8,204,54,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS8,204,30,54,18 - PUSHBUTTON "DELETE",IDC_DEL8,258,30,54,18 - GROUPBOX "TRIANGLE / Y / YELLOW",IDC_GROUP13,318,18,120,48 - LTEXT "1/1/1/1",IDC_LABEL9,324,54,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS9,324,30,54,18 - PUSHBUTTON "DELETE",IDC_DEL9,378,30,54,18 - GROUPBOX "CROSS / A / BLUE",IDC_GROUP14,198,66,120,48 - LTEXT "1/1/1/1",IDC_LABEL10,204,102,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS10,204,78,54,18 - PUSHBUTTON "DELETE",IDC_DEL10,258,78,54,18 - GROUPBOX "CIRCLE / B / RED",IDC_GROUP15,318,66,120,48 - LTEXT "1/1/1/1",IDC_LABEL11,324,102,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS11,324,78,54,18 - PUSHBUTTON "DELETE",IDC_DEL11,378,78,54,18 - GROUPBOX "L1 / L",IDC_GROUP16,198,114,120,48 - LTEXT "1/1/1/1",IDC_LABEL12,204,150,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS12,204,126,54,18 - PUSHBUTTON "DELETE",IDC_DEL12,258,126,54,18 - GROUPBOX "R1 / R / ORANGE",IDC_GROUP17,318,114,120,48 - LTEXT "1/1/1/1",IDC_LABEL13,324,150,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS13,324,126,54,18 - PUSHBUTTON "DELETE",IDC_DEL13,378,126,54,18 - GROUPBOX "L2",IDC_GROUP18,198,162,120,48 - LTEXT "1/1/1/1",IDC_LABEL14,204,198,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS14,204,174,54,18 - PUSHBUTTON "DELETE",IDC_DEL14,258,174,54,18 - GROUPBOX "R2",IDC_GROUP19,318,162,120,48 - LTEXT "1/1/1/1",IDC_LABEL15,324,198,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS15,324,174,54,18 - PUSHBUTTON "DELETE",IDC_DEL15,378,174,54,18 - GROUPBOX "L3",IDC_GROUP20,198,210,120,48 - LTEXT "1/1/1/1",IDC_LABEL16,204,246,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS16,204,222,54,18 - PUSHBUTTON "DELETE",IDC_DEL16,258,222,54,18 - GROUPBOX "R3",IDC_GROUP22,318,210,120,48 - LTEXT "1/1/1/1",IDC_LABEL17,324,246,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS17,324,222,54,18 - PUSHBUTTON "DELETE",IDC_DEL17,378,222,54,18 - GROUPBOX "SELECT",IDC_GROUP24,198,258,120,48 - LTEXT "1/1/1/1",IDC_LABEL18,204,294,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS18,204,270,54,18 - PUSHBUTTON "DELETE",IDC_DEL18,258,270,54,18 - GROUPBOX "START",IDC_GROUP25,318,258,120,48 - LTEXT "1/1/1/1",IDC_LABEL19,324,294,66,8 - PUSHBUTTON "REASSIGN",IDC_ASS19,324,270,54,18 - PUSHBUTTON "DELETE",IDC_DEL19,378,270,54,18 - CTEXT "GLOBAL OPTIONS",IDC_GROUP3,444,6,222,12 - GROUPBOX "FILTER/TEST",IDC_GROUP21,444,18,222,150 - COMBOBOX IDC_COMBO1,450,30,114,72,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_PICTURE,"Static",SS_OWNERDRAW | WS_BORDER,450,48,114,96 - LTEXT "LINEARITY",IDC_LINEAR,576,30,78,8 - CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,570,42,90,24 - LTEXT "OFFSET",IDC_OFFSET,576,72,78,8 - CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,570,84,90,24 - LTEXT "DEAD-ZONE",IDC_DEADZONE,576,114,78,8 - CONTROL "",IDC_SLIDER3,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,570,126,90,24 - RTEXT "Defaults",IDC_STATIC,450,150,36,12 - COMBOBOX IDC_COMBO3,492,150,72,66,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - GROUPBOX "FORCE FEEDBACK",IDC_GROUP26,444,168,222,108 - LTEXT "Device",IDC_STATIC,450,180,36,12,NOT WS_VISIBLE,WS_EX_RIGHT - COMBOBOX IDC_COMBO4,492,180,102,51,CBS_DROPDOWNLIST | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "TEST ",IDC_BUTTON1,600,180,30,12 - LTEXT "GAIN",IDC_STATIC,486,204,18,8 - CONTROL "",IDC_SLIDER4,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,510,198,102,23 - LTEXT "FORCE MULTI",IDC_STATIC,456,228,48,8 - CONTROL "",IDC_SLIDER5,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,510,223,102,23 - CONTROL "INVERT FORCES",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,450,249,65,12 - CONTROL "USE RAMP FOR VARIABLE (0x8) EFFECT",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,519,250,144,12 - PUSHBUTTON "Reset Defaults",IDC_BUTTON3,594,261,60,12,NOT WS_VISIBLE - DEFPUSHBUTTON "OK",IDOK,498,282,80,20 - PUSHBUTTON "Cancel",IDCANCEL,582,282,80,20 -END - -IDD_DLG_BUZZ DIALOGEX 0, 0, 407, 159 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Buzz device" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Controller#1",IDC_STATIC,5,5,99,135 - LTEXT "Red",IDC_BZ_CTL1_LBL1,10,15,38,8 - LTEXT "Blue",IDC_BZ_CTL1_LBL2,10,40,38,8 - LTEXT "Orange",IDC_BZ_CTL1_LBL3,10,65,38,8 - LTEXT "Green",IDC_BZ_CTL1_LBL4,10,90,38,8 - LTEXT "Yellow",IDC_BZ_CTL1_LBL5,10,115,38,8 - LTEXT "1/1/1/1",IDC_LABEL0,10,27,38,8 - LTEXT "1/1/1/1",IDC_LABEL4,10,52,38,8 - LTEXT "1/1/1/1",IDC_LABEL3,10,77,38,8 - LTEXT "1/1/1/1",IDC_LABEL2,10,102,38,8 - LTEXT "1/1/1/1",IDC_LABEL1,10,127,38,8 - PUSHBUTTON "Add",IDC_ASS0,55,15,20,20 - PUSHBUTTON "Del",IDC_DEL0,80,15,20,20 - PUSHBUTTON "Add",IDC_ASS4,55,40,20,20 - PUSHBUTTON "Del",IDC_DEL4,80,40,20,20 - PUSHBUTTON "Add",IDC_ASS3,55,65,20,20 - PUSHBUTTON "Del",IDC_DEL3,80,65,20,20 - PUSHBUTTON "Add",IDC_ASS2,55,90,20,20 - PUSHBUTTON "Del",IDC_DEL2,80,90,20,20 - PUSHBUTTON "Add",IDC_ASS1,55,115,20,20 - PUSHBUTTON "Del",IDC_DEL1,80,115,20,20 - GROUPBOX "Controller#2",IDC_STATIC,105,5,99,135 - LTEXT "Red",IDC_BZ_CTL2_LBL1,110,15,38,8 - LTEXT "Blue",IDC_BZ_CTL2_LBL2,110,40,38,8 - LTEXT "Orange",IDC_BZ_CTL2_LBL3,110,65,38,8 - LTEXT "Green",IDC_BZ_CTL2_LBL4,110,90,38,8 - LTEXT "Yellow",IDC_BZ_CTL2_LBL5,110,115,38,8 - LTEXT "1/1/1/1",IDC_LABEL5,110,27,38,8 - LTEXT "1/1/1/1",IDC_LABEL9,110,52,38,8 - LTEXT "1/1/1/1",IDC_LABEL8,110,77,38,8 - LTEXT "1/1/1/1",IDC_LABEL7,110,102,38,8 - LTEXT "1/1/1/1",IDC_LABEL6,110,127,38,8 - PUSHBUTTON "Add",IDC_ASS5,155,15,20,20 - PUSHBUTTON "Del",IDC_DEL5,180,15,20,20 - PUSHBUTTON "Add",IDC_ASS9,155,40,20,20 - PUSHBUTTON "Del",IDC_DEL9,180,40,20,20 - PUSHBUTTON "Add",IDC_ASS8,155,65,20,20 - PUSHBUTTON "Del",IDC_DEL8,180,65,20,20 - PUSHBUTTON "Add",IDC_ASS7,155,90,20,20 - PUSHBUTTON "Del",IDC_DEL7,180,90,20,20 - PUSHBUTTON "Add",IDC_ASS6,155,115,20,20 - PUSHBUTTON "Del",IDC_DEL6,180,115,20,20 - GROUPBOX "Controller#3",IDC_STATIC,205,5,99,135 - LTEXT "Red",IDC_BZ_CTL3_LBL1,210,15,38,8 - LTEXT "Blue",IDC_BZ_CTL3_LBL2,210,40,38,8 - LTEXT "Orange",IDC_BZ_CTL3_LBL3,210,65,38,8 - LTEXT "Green",IDC_BZ_CTL3_LBL4,210,90,38,8 - LTEXT "Yellow",IDC_BZ_CTL3_LBL5,210,115,38,8 - LTEXT "1/1/1/1",IDC_LABEL10,210,27,38,8 - LTEXT "1/1/1/1",IDC_LABEL14,210,52,38,8 - LTEXT "1/1/1/1",IDC_LABEL13,210,77,38,8 - LTEXT "1/1/1/1",IDC_LABEL12,210,102,38,8 - LTEXT "1/1/1/1",IDC_LABEL11,210,127,38,8 - PUSHBUTTON "Add",IDC_ASS10,255,15,20,20 - PUSHBUTTON "Del",IDC_DEL10,280,15,20,20 - PUSHBUTTON "Add",IDC_ASS14,255,40,20,20 - PUSHBUTTON "Del",IDC_DEL14,280,40,20,20 - PUSHBUTTON "Add",IDC_ASS13,255,65,20,20 - PUSHBUTTON "Del",IDC_DEL13,280,65,20,20 - PUSHBUTTON "Add",IDC_ASS12,255,90,20,20 - PUSHBUTTON "Del",IDC_DEL12,280,90,20,20 - PUSHBUTTON "Add",IDC_ASS11,255,115,20,20 - PUSHBUTTON "Del",IDC_DEL11,280,115,20,20 - GROUPBOX "Controller#4",IDC_STATIC,305,5,99,135 - LTEXT "Red",IDC_BZ_CTL4_LBL1,310,15,38,8 - LTEXT "Blue",IDC_BZ_CTL4_LBL2,310,40,38,8 - LTEXT "Orange",IDC_BZ_CTL4_LBL3,310,65,38,8 - LTEXT "Green",IDC_BZ_CTL4_LBL4,310,90,38,8 - LTEXT "Yellow",IDC_BZ_CTL4_LBL5,310,115,38,8 - LTEXT "1/1/1/1",IDC_LABEL15,310,27,38,8 - LTEXT "1/1/1/1",IDC_LABEL19,310,52,38,8 - LTEXT "1/1/1/1",IDC_LABEL18,310,77,38,8 - LTEXT "1/1/1/1",IDC_LABEL17,310,102,38,8 - LTEXT "1/1/1/1",IDC_LABEL16,310,127,38,8 - PUSHBUTTON "Add",IDC_ASS15,355,15,20,20 - PUSHBUTTON "Del",IDC_DEL15,380,15,20,20 - PUSHBUTTON "Add",IDC_ASS19,355,40,20,20 - PUSHBUTTON "Del",IDC_DEL19,380,40,20,20 - PUSHBUTTON "Add",IDC_ASS18,355,65,20,20 - PUSHBUTTON "Del",IDC_DEL18,380,65,20,20 - PUSHBUTTON "Add",IDC_ASS17,355,90,20,20 - PUSHBUTTON "Del",IDC_DEL17,380,90,20,20 - PUSHBUTTON "Add",IDC_ASS16,355,115,20,20 - PUSHBUTTON "Del",IDC_DEL16,380,115,20,20 - DEFPUSHBUTTON "OK",IDOK,302,142,50,14 - PUSHBUTTON "Cancel",IDCANCEL,354,142,50,14 -END - -IDD_DLG_KEYBOARDMANIA DIALOGEX 0, 0, 580, 160 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Keyboardmania controller" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Buttons",IDC_STATIC,5,5,140,50 - PUSHBUTTON "Start",IDC_ASS22,90,15,50,15 - PUSHBUTTON "Select",IDC_ASS14,90,35,50,15 - LTEXT "1/1/1/1",IDC_LABEL22,10,17,50,8 - LTEXT "1/1/1/1",IDC_LABEL14,10,37,50,8 - GROUPBOX "Wheel",IDC_STATIC,150,5,140,50 - PUSHBUTTON "Up",IDC_ASS29,235,15,50,15 - PUSHBUTTON "Down",IDC_ASS30,235,35,50,15 - LTEXT "1/1/1/1",IDC_LABEL29,155,17,50,8 - LTEXT "1/1/1/1",IDC_LABEL30,155,37,50,8 - - GROUPBOX "Keys",IDC_STATIC,5,60,570,70 - PUSHBUTTON "C 1",IDC_ASS0,10,95,40,15 - PUSHBUTTON "C# 1",IDC_ASS1,30,80,40,15 - PUSHBUTTON "D 1",IDC_ASS2,50,95,40,15 - PUSHBUTTON "D# 1",IDC_ASS3,70,80,40,15 - PUSHBUTTON "E 1",IDC_ASS4,90,95,40,15 - PUSHBUTTON "F 1",IDC_ASS5,130,95,40,15 - PUSHBUTTON "F# 1",IDC_ASS6,150,80,40,15 - PUSHBUTTON "G 1",IDC_ASS8,170,95,40,15 - PUSHBUTTON "G# 1",IDC_ASS9,190,80,40,15 - PUSHBUTTON "A 1",IDC_ASS10,210,95,40,15 - PUSHBUTTON "A# 1",IDC_ASS11,230,80,40,15 - PUSHBUTTON "B 1",IDC_ASS12,250,95,40,15 - PUSHBUTTON "C 2",IDC_ASS13,290,95,40,15 - PUSHBUTTON "C# 2",IDC_ASS16,310,80,40,15 - PUSHBUTTON "D 2",IDC_ASS17,330,95,40,15 - PUSHBUTTON "D# 2",IDC_ASS18,350,80,40,15 - PUSHBUTTON "E 2",IDC_ASS19,370,95,40,15 - PUSHBUTTON "F 2",IDC_ASS20,410,95,40,15 - PUSHBUTTON "F# 2",IDC_ASS21,430,80,40,15 - PUSHBUTTON "G 2",IDC_ASS24,450,95,40,15 - PUSHBUTTON "G# 2",IDC_ASS25,470,80,40,15 - PUSHBUTTON "A 2",IDC_ASS26,490,95,40,15 - PUSHBUTTON "A# 2",IDC_ASS27,510,80,40,15 - PUSHBUTTON "B 2",IDC_ASS28,530,95,40,15 - LTEXT "1/1/1/1",IDC_LABEL0,10,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL1,30,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL2,50,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL3,70,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL4,90,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL5,130,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL6,150,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL8,170,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL9,190,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL10,210,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL11,230,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL12,250,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL13,290,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL16,310,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL17,330,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL18,350,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL19,370,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL20,410,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL21,430,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL24,450,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL25,470,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL26,490,110,38,8 - LTEXT "1/1/1/1",IDC_LABEL27,510,70,38,8 - LTEXT "1/1/1/1",IDC_LABEL28,530,110,38,8 - - PUSHBUTTON "Reset all",IDC_DELALL,5,140,50,15 - DEFPUSHBUTTON "OK",IDOK,470,140,50,15 - PUSHBUTTON "Cancel",IDCANCEL,525,140,50,15 -END - -IDD_DLG_GAMETRAK DIALOGEX 0, 0, 260, 115 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Gametrak" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Left X",IDC_ASS0,10,40,50,14 - PUSHBUTTON "Left Y",IDC_ASS1,70,40,50,14 - PUSHBUTTON "Left Z",IDC_ASS2,40,10,50,14 - PUSHBUTTON "Right X",IDC_ASS3,140,40,50,14 - PUSHBUTTON "Right Y",IDC_ASS4,200,40,50,14 - PUSHBUTTON "Right Z",IDC_ASS5,170,10,50,14 - PUSHBUTTON "Foot Mat",IDC_ASS6,105,70,50,14 - LTEXT "1/1/1/1",IDC_LABEL0,10,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL1,70,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL2,40,25,55,8 - LTEXT "1/1/1/1",IDC_LABEL3,140,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL4,200,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL5,170,25,55,8 - LTEXT "1/1/1/1",IDC_LABEL6,105,85,55,8 - DEFPUSHBUTTON "OK",IDOK,145,95,50,14 - PUSHBUTTON "Cancel",IDCANCEL,200,95,50,14 -END - -IDD_DLG_REALPLAY DIALOGEX 0, 0, 260, 151 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "RealPlay" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - PUSHBUTTON "Up",IDC_ASS0,40,10,50,14 - PUSHBUTTON "Down",IDC_ASS1,40,70,50,14 - PUSHBUTTON "Left",IDC_ASS2,10,40,50,14 - PUSHBUTTON "Right",IDC_ASS3,70,40,50,14 - PUSHBUTTON "Red",IDC_ASS4,170,10,50,14 - PUSHBUTTON "Green",IDC_ASS5,170,70,50,14 - PUSHBUTTON "Yellow",IDC_ASS6,140,40,50,14 - PUSHBUTTON "Blue",IDC_ASS7,200,40,50,14 - PUSHBUTTON "Accel X",IDC_ASS8,45,100,50,14 - PUSHBUTTON "Accel Y",IDC_ASS9,105,100,50,14 - PUSHBUTTON "Accel Z",IDC_ASS10,165,100,50,14 - LTEXT "1/1/1/1",IDC_LABEL0,40,25,55,8 - LTEXT "1/1/1/1",IDC_LABEL1,40,85,55,8 - LTEXT "1/1/1/1",IDC_LABEL2,10,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL3,70,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL4,170,25,55,8 - LTEXT "1/1/1/1",IDC_LABEL5,170,85,55,8 - LTEXT "1/1/1/1",IDC_LABEL6,140,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL7,200,55,55,8 - LTEXT "1/1/1/1",IDC_LABEL8,45,115,55,8 - LTEXT "1/1/1/1",IDC_LABEL9,105,115,55,8 - LTEXT "1/1/1/1",IDC_LABEL10,165,115,55,8 - DEFPUSHBUTTON "OK",IDOK,145,130,50,14 - PUSHBUTTON "Cancel",IDCANCEL,200,130,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_DIALOG1, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 663 - TOPMARGIN, 7 - BOTTOMMARGIN, 302 - END - - IDD_DLG_BUZZ, DIALOG - BEGIN - END - - IDD_DLG_KEYBOARDMANIA, DIALOG - BEGIN - END - - IDD_DLG_GAMETRAK, DIALOG - BEGIN - END - - IDD_DLG_REALPLAY, DIALOG - BEGIN - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_DIALOG1 AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DLG_BUZZ AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DLG_KEYBOARDMANIA AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DLG_GAMETRAK AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_DLG_REALPLAY AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp b/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp deleted file mode 100644 index 5a055189ef..0000000000 --- a/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "evdev-ff.h" -#include "USB/usb-pad/lg/lg_ff.h" -#include -#include -#include - -namespace usb_pad -{ - namespace evdev - { - -#define BITS_TO_UCHAR(x) \ - (((x) + 8 * sizeof(unsigned char) - 1) / (8 * sizeof(unsigned char))) -#define testBit(bit, array) ((((uint8_t*)(array))[(bit) / 8] >> ((bit) % 8)) & 1) - - EvdevFF::EvdevFF(int fd, bool gain_enabled, int gain, bool ac_managed, int ac_strength) - : mHandle(fd) - , mUseRumble(false) - , mLastValue(0) - , m_gain_enabled(gain_enabled) - , m_gain(gain) - //, m_ac_managed(ac_enabled) - , m_ac_managed(true) - , m_ac_strength(ac_strength) - { - unsigned char features[BITS_TO_UCHAR(FF_MAX)]; - if (ioctl(mHandle, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) - { - } - - int effects = 0; - if (ioctl(mHandle, EVIOCGEFFECTS, &effects) < 0) - { - } - - if (!testBit(FF_CONSTANT, features)) - { - if (testBit(FF_RUMBLE, features)) - mUseRumble = true; - } - - if (!testBit(FF_SPRING, features)) - { - } - - if (!testBit(FF_DAMPER, features)) - { - } - - if (!testBit(FF_GAIN, features)) - { - } - - if (!testBit(FF_AUTOCENTER, features)) - { - } - - memset(&mEffect, 0, sizeof(mEffect)); - - // TODO check features and do FF_RUMBLE instead if gamepad? - // XXX linux status (hid-lg4ff.c) - only constant and autocenter are implemented - mEffect.u.constant.level = 0; /* Strength : 0x2000 == 25 % */ - // Logitech wheels' force vs turn direction: 255 - left, 127/128 - neutral, 0 - right - // left direction - mEffect.direction = 0x4000; - mEffect.trigger.button = 0; - mEffect.trigger.interval = 0; - mEffect.replay.length = 0x7FFFUL; /* mseconds */ - mEffect.replay.delay = 0; - - if (m_gain_enabled) - SetGain(m_gain); - - m_ac_strength = std::min(100, std::max(0, m_ac_strength)); - if (ac_managed) - SetAutoCenter(0); // default to off - else - SetAutoCenter(m_ac_strength); - - m_ac_managed = ac_managed; - } - - EvdevFF::~EvdevFF() - { - for (int i = 0; i < (int)countof(mEffIds); i++) - { - if (mEffIds[i] != -1 && ioctl(mHandle, EVIOCRMFF, mEffIds[i]) == -1) - { - } - } - } - - void EvdevFF::DisableForce(EffectID force) - { - struct input_event play; - play.type = EV_FF; - play.code = mEffIds[force]; - play.value = 0; - if (write(mHandle, (const void*)&play, sizeof(play)) == -1) - { - } - } - - void EvdevFF::SetConstantForce(/*const parsed_ff_data& ff*/ int level) - { - struct input_event play; - play.type = EV_FF; - play.value = 1; - mEffect.u = {}; - - if (!mUseRumble) - { - mEffect.type = FF_CONSTANT; - mEffect.id = mEffIds[EFF_CONSTANT]; - mEffect.u.constant.level = /*ff.u.constant.*/ level; - // mEffect.u.constant.envelope.attack_length = 0;//0x100; - // mEffect.u.constant.envelope.attack_level = 0; - // mEffect.u.constant.envelope.fade_length = 0;//0x100; - // mEffect.u.constant.envelope.fade_level = 0; - - if (ioctl(mHandle, EVIOCSFF, &(mEffect)) < 0) - { - return; - } - play.code = mEffect.id; - mEffIds[EFF_CONSTANT] = mEffect.id; - } - else - { - - mEffect.type = FF_RUMBLE; - mEffect.id = mEffIds[EFF_RUMBLE]; - - mEffect.replay.length = 500; - mEffect.replay.delay = 0; - mEffect.u.rumble.weak_magnitude = 0; - mEffect.u.rumble.strong_magnitude = 0; - - int mag = std::abs(/*ff.u.constant.*/ level); - int diff = std::abs(mag - mLastValue); - - // TODO random limits to cull down on too much rumble - if (diff > 8292 && diff < 32767) - mEffect.u.rumble.weak_magnitude = mag; - if (diff / 8192 > 0) - mEffect.u.rumble.strong_magnitude = mag; - - mLastValue = mag; - - if (ioctl(mHandle, EVIOCSFF, &(mEffect)) < 0) - { - return; - } - play.code = mEffect.id; - mEffIds[EFF_RUMBLE] = mEffect.id; - } - - if (write(mHandle, (const void*)&play, sizeof(play)) == -1) - { - } - } - - void EvdevFF::SetSpringForce(const parsed_ff_data& ff) - { - struct input_event play; - play.type = EV_FF; - play.value = 1; - - mEffect.type = FF_SPRING; - mEffect.id = mEffIds[EFF_SPRING]; - mEffect.u = {}; - mEffect.u.condition[0].left_saturation = ff.u.condition.left_saturation; - mEffect.u.condition[0].right_saturation = ff.u.condition.right_saturation; - mEffect.u.condition[0].left_coeff = ff.u.condition.left_coeff; - mEffect.u.condition[0].right_coeff = ff.u.condition.right_coeff; - mEffect.u.condition[0].center = ff.u.condition.center; - mEffect.u.condition[0].deadband = ff.u.condition.deadband; - - if (ioctl(mHandle, EVIOCSFF, &(mEffect)) < 0) - { - return; - } - - play.code = mEffect.id; - mEffIds[EFF_SPRING] = mEffect.id; - - if (write(mHandle, (const void*)&play, sizeof(play)) == -1) - { - } - } - - void EvdevFF::SetDamperForce(const parsed_ff_data& ff) - { - struct input_event play; - play.type = EV_FF; - play.value = 1; - - mEffect.u = {}; - mEffect.type = FF_DAMPER; - mEffect.id = mEffIds[EFF_DAMPER]; - mEffect.u.condition[0].left_saturation = ff.u.condition.left_saturation; - mEffect.u.condition[0].right_saturation = ff.u.condition.right_saturation; - mEffect.u.condition[0].left_coeff = ff.u.condition.left_coeff; - mEffect.u.condition[0].right_coeff = ff.u.condition.right_coeff; - mEffect.u.condition[0].center = ff.u.condition.center; - mEffect.u.condition[0].deadband = ff.u.condition.deadband; - - - if (ioctl(mHandle, EVIOCSFF, &(mEffect)) < 0) - { - return; - } - - play.code = mEffect.id; - mEffIds[EFF_DAMPER] = mEffect.id; - - if (write(mHandle, (const void*)&play, sizeof(play)) == -1) - { - } - } - - void EvdevFF::SetFrictionForce(const parsed_ff_data& ff) - { - struct input_event play; - play.type = EV_FF; - play.value = 1; - - mEffect.u = {}; - mEffect.type = FF_FRICTION; - mEffect.id = mEffIds[EFF_FRICTION]; - mEffect.u.condition[0].left_saturation = ff.u.condition.left_saturation; - mEffect.u.condition[0].right_saturation = ff.u.condition.right_saturation; - mEffect.u.condition[0].left_coeff = ff.u.condition.left_coeff; - mEffect.u.condition[0].right_coeff = ff.u.condition.right_coeff; - mEffect.u.condition[0].center = ff.u.condition.center; - mEffect.u.condition[0].deadband = ff.u.condition.deadband; - - if (ioctl(mHandle, EVIOCSFF, &(mEffect)) < 0) - { - return; - } - - play.code = mEffect.id; - mEffIds[EFF_FRICTION] = mEffect.id; - - if (write(mHandle, (const void*)&play, sizeof(play)) == -1) - { - } - } - - void EvdevFF::SetAutoCenter(int value) - { - if (!m_ac_managed) - return; - struct input_event ie; - value = value * m_ac_strength / 100; - - ie.type = EV_FF; - ie.code = FF_AUTOCENTER; - ie.value = value * 0xFFFFUL / 100; - - if (write(mHandle, &ie, sizeof(ie)) == -1) - { - } - } - - void EvdevFF::SetGain(int gain /* between 0 and 100 */) - { - struct input_event ie; - - ie.type = EV_FF; - ie.code = FF_GAIN; - ie.value = 0xFFFFUL * gain / 100; - - if (write(mHandle, &ie, sizeof(ie)) == -1) - { - } - } - - } // namespace evdev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/evdev/evdev-ff.h b/pcsx2/USB/usb-pad/evdev/evdev-ff.h deleted file mode 100644 index 082cf988d9..0000000000 --- a/pcsx2/USB/usb-pad/evdev/evdev-ff.h +++ /dev/null @@ -1,56 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef EVDEV_FF_H -#define EVDEV_FF_H - -#include -#include "USB/usb-pad/usb-pad.h" - -namespace usb_pad -{ - namespace evdev - { - - class EvdevFF : public FFDevice - { - public: - EvdevFF(int fd, bool gain_enabled, int gain, bool ac_managed, int ac_strength); - ~EvdevFF(); - - void SetConstantForce(int level); - void SetSpringForce(const parsed_ff_data& ff); - void SetDamperForce(const parsed_ff_data& ff); - void SetFrictionForce(const parsed_ff_data& ff); - void SetAutoCenter(int value); - void SetGain(int gain); - void DisableForce(EffectID force); - - private: - int mHandle; - ff_effect mEffect; - int mEffIds[5] = {-1, -1, -1, -1, -1}; //save ids just in case - - bool mUseRumble; - int mLastValue; - bool m_gain_enabled; - int m_gain; - bool m_ac_managed; - int m_ac_strength; - }; - - } // namespace evdev -} // namespace usb_pad -#endif diff --git a/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp b/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp deleted file mode 100644 index e4d6718966..0000000000 --- a/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp +++ /dev/null @@ -1,754 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "evdev.h" - -#include -#include -#include -#include -#include "USB/gtk.h" - -namespace usb_pad -{ - namespace evdev - { - - using sys_clock = std::chrono::system_clock; - using ms = std::chrono::milliseconds; - -#define EVDEV_DIR "/dev/input/by-id/" - - const std::array key_to_str = { - "RESERVED", /* linux:0 (KEY_RESERVED) */ - "ESC", /* linux:1 (KEY_ESC) */ - "1", /* linux:2 (KEY_1) */ - "2", /* linux:3 (KEY_2) */ - "3", /* linux:4 (KEY_3) */ - "4", /* linux:5 (KEY_4) */ - "5", /* linux:6 (KEY_5) */ - "6", /* linux:7 (KEY_6) */ - "7", /* linux:8 (KEY_7) */ - "8", /* linux:9 (KEY_8) */ - "9", /* linux:10 (KEY_9) */ - "0", /* linux:11 (KEY_0) */ - "MINUS", /* linux:12 (KEY_MINUS) */ - "EQUAL", /* linux:13 (KEY_EQUAL) */ - "BACKSPACE", /* linux:14 (KEY_BACKSPACE) */ - "TAB", /* linux:15 (KEY_TAB) */ - "Q", /* linux:16 (KEY_Q) */ - "W", /* linux:17 (KEY_W) */ - "E", /* linux:18 (KEY_E) */ - "R", /* linux:19 (KEY_R) */ - "T", /* linux:20 (KEY_T) */ - "Y", /* linux:21 (KEY_Y) */ - "U", /* linux:22 (KEY_U) */ - "I", /* linux:23 (KEY_I) */ - "O", /* linux:24 (KEY_O) */ - "P", /* linux:25 (KEY_P) */ - "{", /* linux:26 (KEY_LEFTBRACE) */ - "}", /* linux:27 (KEY_RIGHTBRACE) */ - "ENTER", /* linux:28 (KEY_ENTER) */ - "L-CTRL", /* linux:29 (KEY_LEFTCTRL) */ - "A", /* linux:30 (KEY_A) */ - "S", /* linux:31 (KEY_S) */ - "D", /* linux:32 (KEY_D) */ - "F", /* linux:33 (KEY_F) */ - "G", /* linux:34 (KEY_G) */ - "H", /* linux:35 (KEY_H) */ - "J", /* linux:36 (KEY_J) */ - "K", /* linux:37 (KEY_K) */ - "L", /* linux:38 (KEY_L) */ - ";", /* linux:39 (KEY_SEMICOLON) */ - "'", /* linux:40 (KEY_APOSTROPHE) */ - "~", /* linux:41 (KEY_GRAVE) */ - "L-SHIFT", /* linux:42 (KEY_LEFTSHIFT) */ - "\\", /* linux:43 (KEY_BACKSLASH) */ - "Z", /* linux:44 (KEY_Z) */ - "X", /* linux:45 (KEY_X) */ - "C", /* linux:46 (KEY_C) */ - "V", /* linux:47 (KEY_V) */ - "B", /* linux:48 (KEY_B) */ - "N", /* linux:49 (KEY_N) */ - "M", /* linux:50 (KEY_M) */ - ",", /* linux:51 (KEY_COMMA) */ - ".", /* linux:52 (KEY_DOT) */ - "/", /* linux:53 (KEY_SLASH) */ - "R-SHIFT", /* linux:54 (KEY_RIGHTSHIFT) */ - "KPASTERISK", /* linux:55 (KEY_KPASTERISK) */ - "LEFTALT", /* linux:56 (KEY_LEFTALT) */ - "SPACE", /* linux:57 (KEY_SPACE) */ - "CAPSLOCK", /* linux:58 (KEY_CAPSLOCK) */ - "F1", /* linux:59 (KEY_F1) */ - "F2", /* linux:60 (KEY_F2) */ - "F3", /* linux:61 (KEY_F3) */ - "F4", /* linux:62 (KEY_F4) */ - "F5", /* linux:63 (KEY_F5) */ - "F6", /* linux:64 (KEY_F6) */ - "F7", /* linux:65 (KEY_F7) */ - "F8", /* linux:66 (KEY_F8) */ - "F9", /* linux:67 (KEY_F9) */ - "F10", /* linux:68 (KEY_F10) */ - "NUMLOCK", /* linux:69 (KEY_NUMLOCK) */ - "SCROLLLOCK", /* linux:70 (KEY_SCROLLLOCK) */ - "KP7", /* linux:71 (KEY_KP7) */ - "KP8", /* linux:72 (KEY_KP8) */ - "KP9", /* linux:73 (KEY_KP9) */ - "KPMINUS", /* linux:74 (KEY_KPMINUS) */ - "KP4", /* linux:75 (KEY_KP4) */ - "KP5", /* linux:76 (KEY_KP5) */ - "KP6", /* linux:77 (KEY_KP6) */ - "KPPLUS", /* linux:78 (KEY_KPPLUS) */ - "KP1", /* linux:79 (KEY_KP1) */ - "KP2", /* linux:80 (KEY_KP2) */ - "KP3", /* linux:81 (KEY_KP3) */ - "KP0", /* linux:82 (KEY_KP0) */ - "KPDOT", /* linux:83 (KEY_KPDOT) */ - "84", /* linux:84 (unnamed) */ - "ZENKAKUHANKAKU", /* linux:85 (KEY_ZENKAKUHANKAKU) */ - "102ND", /* linux:86 (KEY_102ND) */ - "F11", /* linux:87 (KEY_F11) */ - "F12", /* linux:88 (KEY_F12) */ - "RO", /* linux:89 (KEY_RO) */ - "KATAKANA", /* linux:90 (KEY_KATAKANA) */ - "HIRAGANA", /* linux:91 (KEY_HIRAGANA) */ - "HENKAN", /* linux:92 (KEY_HENKAN) */ - "KATAKANAHIRAGANA", /* linux:93 (KEY_KATAKANAHIRAGANA) */ - "MUHENKAN", /* linux:94 (KEY_MUHENKAN) */ - "KPJPCOMMA", /* linux:95 (KEY_KPJPCOMMA) */ - "KPENTER", /* linux:96 (KEY_KPENTER) */ - "RIGHTCTRL", /* linux:97 (KEY_RIGHTCTRL) */ - "KPSLASH", /* linux:98 (KEY_KPSLASH) */ - "SYSRQ", /* linux:99 (KEY_SYSRQ) */ - "RIGHTALT", /* linux:100 (KEY_RIGHTALT) */ - "LINEFEED", /* linux:101 (KEY_LINEFEED) */ - "HOME", /* linux:102 (KEY_HOME) */ - "UP", /* linux:103 (KEY_UP) */ - "PAGEUP", /* linux:104 (KEY_PAGEUP) */ - "LEFT", /* linux:105 (KEY_LEFT) */ - "RIGHT", /* linux:106 (KEY_RIGHT) */ - "END", /* linux:107 (KEY_END) */ - "DOWN", /* linux:108 (KEY_DOWN) */ - "PAGEDOWN", /* linux:109 (KEY_PAGEDOWN) */ - "INSERT", /* linux:110 (KEY_INSERT) */ - "DELETE", /* linux:111 (KEY_DELETE) */ - "MACRO", /* linux:112 (KEY_MACRO) */ - "MUTE", /* linux:113 (KEY_MUTE) */ - "VOLUMEDOWN", /* linux:114 (KEY_VOLUMEDOWN) */ - "VOLUMEUP", /* linux:115 (KEY_VOLUMEUP) */ - "POWER", /* linux:116 (KEY_POWER) */ - "KPEQUAL", /* linux:117 (KEY_KPEQUAL) */ - "KPPLUSMINUS", /* linux:118 (KEY_KPPLUSMINUS) */ - "PAUSE", /* linux:119 (KEY_PAUSE) */ - "SCALE", /* linux:120 (KEY_SCALE) */ - "KPCOMMA", /* linux:121 (KEY_KPCOMMA) */ - "HANGEUL", /* linux:122 (KEY_HANGEUL) */ - "HANJA", /* linux:123 (KEY_HANJA) */ - "YEN", /* linux:124 (KEY_YEN) */ - "LEFTMETA", /* linux:125 (KEY_LEFTMETA) */ - "RIGHTMETA", /* linux:126 (KEY_RIGHTMETA) */ - "COMPOSE", /* linux:127 (KEY_COMPOSE) */ - "STOP", /* linux:128 (KEY_STOP) */ - "AGAIN", /* linux:129 (KEY_AGAIN) */ - "PROPS", /* linux:130 (KEY_PROPS) */ - "UNDO", /* linux:131 (KEY_UNDO) */ - "FRONT", /* linux:132 (KEY_FRONT) */ - "COPY", /* linux:133 (KEY_COPY) */ - "OPEN", /* linux:134 (KEY_OPEN) */ - "PASTE", /* linux:135 (KEY_PASTE) */ - "FIND", /* linux:136 (KEY_FIND) */ - "CUT", /* linux:137 (KEY_CUT) */ - "HELP", /* linux:138 (KEY_HELP) */ - "MENU", /* linux:139 (KEY_MENU) */ - "CALC", /* linux:140 (KEY_CALC) */ - "SETUP", /* linux:141 (KEY_SETUP) */ - "SLEEP", /* linux:142 (KEY_SLEEP) */ - "WAKEUP", /* linux:143 (KEY_WAKEUP) */ - "FILE", /* linux:144 (KEY_FILE) */ - "SENDFILE", /* linux:145 (KEY_SENDFILE) */ - "DELETEFILE", /* linux:146 (KEY_DELETEFILE) */ - "XFER", /* linux:147 (KEY_XFER) */ - "PROG1", /* linux:148 (KEY_PROG1) */ - "PROG2", /* linux:149 (KEY_PROG2) */ - "WWW", /* linux:150 (KEY_WWW) */ - "MSDOS", /* linux:151 (KEY_MSDOS) */ - "SCREENLOCK", /* linux:152 (KEY_SCREENLOCK) */ - "DIRECTION", /* linux:153 (KEY_DIRECTION) */ - "CYCLEWINDOWS", /* linux:154 (KEY_CYCLEWINDOWS) */ - "MAIL", /* linux:155 (KEY_MAIL) */ - "BOOKMARKS", /* linux:156 (KEY_BOOKMARKS) */ - "COMPUTER", /* linux:157 (KEY_COMPUTER) */ - "BACK", /* linux:158 (KEY_BACK) */ - "FORWARD", /* linux:159 (KEY_FORWARD) */ - "CLOSECD", /* linux:160 (KEY_CLOSECD) */ - "EJECTCD", /* linux:161 (KEY_EJECTCD) */ - "EJECTCLOSECD", /* linux:162 (KEY_EJECTCLOSECD) */ - "NEXTSONG", /* linux:163 (KEY_NEXTSONG) */ - "PLAYPAUSE", /* linux:164 (KEY_PLAYPAUSE) */ - "PREVIOUSSONG", /* linux:165 (KEY_PREVIOUSSONG) */ - "STOPCD", /* linux:166 (KEY_STOPCD) */ - "RECORD", /* linux:167 (KEY_RECORD) */ - "REWIND", /* linux:168 (KEY_REWIND) */ - "PHONE", /* linux:169 (KEY_PHONE) */ - "ISO", /* linux:170 (KEY_ISO) */ - "CONFIG", /* linux:171 (KEY_CONFIG) */ - "HOMEPAGE", /* linux:172 (KEY_HOMEPAGE) */ - "REFRESH", /* linux:173 (KEY_REFRESH) */ - "EXIT", /* linux:174 (KEY_EXIT) */ - "MOVE", /* linux:175 (KEY_MOVE) */ - "EDIT", /* linux:176 (KEY_EDIT) */ - "SCROLLUP", /* linux:177 (KEY_SCROLLUP) */ - "SCROLLDOWN", /* linux:178 (KEY_SCROLLDOWN) */ - "KPLEFTPAREN", /* linux:179 (KEY_KPLEFTPAREN) */ - "KPRIGHTPAREN", /* linux:180 (KEY_KPRIGHTPAREN) */ - "NEW", /* linux:181 (KEY_NEW) */ - "REDO", /* linux:182 (KEY_REDO) */ - "F13", /* linux:183 (KEY_F13) */ - "F14", /* linux:184 (KEY_F14) */ - "F15", /* linux:185 (KEY_F15) */ - "F16", /* linux:186 (KEY_F16) */ - "F17", /* linux:187 (KEY_F17) */ - "F18", /* linux:188 (KEY_F18) */ - "F19", /* linux:189 (KEY_F19) */ - "F20", /* linux:190 (KEY_F20) */ - "F21", /* linux:191 (KEY_F21) */ - "F22", /* linux:192 (KEY_F22) */ - "F23", /* linux:193 (KEY_F23) */ - "F24", /* linux:194 (KEY_F24) */ - "195", /* linux:195 (unnamed) */ - "196", /* linux:196 (unnamed) */ - "197", /* linux:197 (unnamed) */ - "198", /* linux:198 (unnamed) */ - "199", /* linux:199 (unnamed) */ - "PLAYCD", /* linux:200 (KEY_PLAYCD) */ - "PAUSECD", /* linux:201 (KEY_PAUSECD) */ - "PROG3", /* linux:202 (KEY_PROG3) */ - "PROG4", /* linux:203 (KEY_PROG4) */ - "DASHBOARD", /* linux:204 (KEY_DASHBOARD) */ - "SUSPEND", /* linux:205 (KEY_SUSPEND) */ - "CLOSE", /* linux:206 (KEY_CLOSE) */ - "PLAY", /* linux:207 (KEY_PLAY) */ - "FASTFORWARD", /* linux:208 (KEY_FASTFORWARD) */ - "BASSBOOST", /* linux:209 (KEY_BASSBOOST) */ - "PRINT", /* linux:210 (KEY_PRINT) */ - "HP", /* linux:211 (KEY_HP) */ - "CAMERA", /* linux:212 (KEY_CAMERA) */ - "SOUND", /* linux:213 (KEY_SOUND) */ - "QUESTION", /* linux:214 (KEY_QUESTION) */ - "EMAIL", /* linux:215 (KEY_EMAIL) */ - "CHAT", /* linux:216 (KEY_CHAT) */ - "SEARCH", /* linux:217 (KEY_SEARCH) */ - "CONNECT", /* linux:218 (KEY_CONNECT) */ - "FINANCE", /* linux:219 (KEY_FINANCE) */ - "SPORT", /* linux:220 (KEY_SPORT) */ - "SHOP", /* linux:221 (KEY_SHOP) */ - "ALTERASE", /* linux:222 (KEY_ALTERASE) */ - "CANCEL", /* linux:223 (KEY_CANCEL) */ - "BRIGHTNESSDOWN", /* linux:224 (KEY_BRIGHTNESSDOWN) */ - "BRIGHTNESSUP", /* linux:225 (KEY_BRIGHTNESSUP) */ - "MEDIA", /* linux:226 (KEY_MEDIA) */ - "SWITCHVIDEOMODE", /* linux:227 (KEY_SWITCHVIDEOMODE) */ - "KBDILLUMTOGGLE", /* linux:228 (KEY_KBDILLUMTOGGLE) */ - "KBDILLUMDOWN", /* linux:229 (KEY_KBDILLUMDOWN) */ - "KBDILLUMUP", /* linux:230 (KEY_KBDILLUMUP) */ - "SEND", /* linux:231 (KEY_SEND) */ - "REPLY", /* linux:232 (KEY_REPLY) */ - "FORWARDMAIL", /* linux:233 (KEY_FORWARDMAIL) */ - "SAVE", /* linux:234 (KEY_SAVE) */ - "DOCUMENTS", /* linux:235 (KEY_DOCUMENTS) */ - "BATTERY", /* linux:236 (KEY_BATTERY) */ - "BLUETOOTH", /* linux:237 (KEY_BLUETOOTH) */ - "WLAN", /* linux:238 (KEY_WLAN) */ - "UWB", /* linux:239 (KEY_UWB) */ - "UNKNOWN", /* linux:240 (KEY_UNKNOWN) */ - "VIDEO_NEXT", /* linux:241 (KEY_VIDEO_NEXT) */ - "VIDEO_PREV", /* linux:242 (KEY_VIDEO_PREV) */ - "BRIGHTNESS_CYCLE", /* linux:243 (KEY_BRIGHTNESS_CYCLE) */ - "BRIGHTNESS_ZERO", /* linux:244 (KEY_BRIGHTNESS_ZERO) */ - "DISPLAY_OFF", /* linux:245 (KEY_DISPLAY_OFF) */ - "WIMAX", /* linux:246 (KEY_WIMAX) */ - "RFKILL", /* linux:247 (KEY_RFKILL) */ - "MICMUTE", /* linux:248 (KEY_MICMUTE) */ - "249", /* linux:249 (unnamed) */ - "250", /* linux:250 (unnamed) */ - "251", /* linux:251 (unnamed) */ - "252", /* linux:252 (unnamed) */ - "253", /* linux:253 (unnamed) */ - "254", /* linux:254 (unnamed) */ - "255", /* linux:255 (unnamed) */ - "BTN_0", /* linux:256 (BTN_0) */ - "BTN_1", /* linux:257 (BTN_1) */ - "BTN_2", /* linux:258 (BTN_2) */ - "BTN_3", /* linux:259 (BTN_3) */ - "BTN_4", /* linux:260 (BTN_4) */ - "BTN_5", /* linux:261 (BTN_5) */ - "BTN_6", /* linux:262 (BTN_6) */ - "BTN_7", /* linux:263 (BTN_7) */ - "BTN_8", /* linux:264 (BTN_8) */ - "BTN_9", /* linux:265 (BTN_9) */ - "266", /* linux:266 (unnamed) */ - "267", /* linux:267 (unnamed) */ - "268", /* linux:268 (unnamed) */ - "269", /* linux:269 (unnamed) */ - "270", /* linux:270 (unnamed) */ - "271", /* linux:271 (unnamed) */ - "BTN_LEFT", /* linux:272 (BTN_LEFT) */ - "BTN_RIGHT", /* linux:273 (BTN_RIGHT) */ - "BTN_MIDDLE", /* linux:274 (BTN_MIDDLE) */ - "BTN_SIDE", /* linux:275 (BTN_SIDE) */ - "BTN_EXTRA", /* linux:276 (BTN_EXTRA) */ - "BTN_FORWARD", /* linux:277 (BTN_FORWARD) */ - "BTN_BACK", /* linux:278 (BTN_BACK) */ - "BTN_TASK", /* linux:279 (BTN_TASK) */ - "280", /* linux:280 (unnamed) */ - "281", /* linux:281 (unnamed) */ - "282", /* linux:282 (unnamed) */ - "283", /* linux:283 (unnamed) */ - "284", /* linux:284 (unnamed) */ - "285", /* linux:285 (unnamed) */ - "286", /* linux:286 (unnamed) */ - "287", /* linux:287 (unnamed) */ - "TRIGGER", /* linux:288 (BTN_TRIGGER) */ - "THUMB", /* linux:289 (BTN_THUMB) */ - "THUMB2", /* linux:290 (BTN_THUMB2) */ - "TOP", /* linux:291 (BTN_TOP) */ - "TOP2", /* linux:292 (BTN_TOP2) */ - "PINKIE", /* linux:293 (BTN_PINKIE) */ - "BASE", /* linux:294 (BTN_BASE) */ - "BASE2", /* linux:295 (BTN_BASE2) */ - "BASE3", /* linux:296 (BTN_BASE3) */ - "BASE4", /* linux:297 (BTN_BASE4) */ - "BASE5", /* linux:298 (BTN_BASE5) */ - "BASE6", /* linux:299 (BTN_BASE6) */ - "300", /* linux:300 (unnamed) */ - "301", /* linux:301 (unnamed) */ - "302", /* linux:302 (unnamed) */ - "DEAD", /* linux:303 (BTN_DEAD) */ - "BTN_A", /* linux:304 (BTN_A) */ - "BTN_B", /* linux:305 (BTN_B) */ - "BTN_C", /* linux:306 (BTN_C) */ - "BTN_X", /* linux:307 (BTN_X) */ - "BTN_Y", /* linux:308 (BTN_Y) */ - "BTN_Z", /* linux:309 (BTN_Z) */ - "TL", /* linux:310 (BTN_TL) */ - "TR", /* linux:311 (BTN_TR) */ - "TL2", /* linux:312 (BTN_TL2) */ - "TR2", /* linux:313 (BTN_TR2) */ - "SELECT", /* linux:314 (BTN_SELECT) */ - "START", /* linux:315 (BTN_START) */ - "MODE", /* linux:316 (BTN_MODE) */ - "THUMBL", /* linux:317 (BTN_THUMBL) */ - "THUMBR", /* linux:318 (BTN_THUMBR) */ - "319", /* linux:319 (unnamed) */ - "TOOL_PEN", /* linux:320 (BTN_TOOL_PEN) */ - "TOOL_RUBBER", /* linux:321 (BTN_TOOL_RUBBER) */ - "TOOL_BRUSH", /* linux:322 (BTN_TOOL_BRUSH) */ - "TOOL_PENCIL", /* linux:323 (BTN_TOOL_PENCIL) */ - "TOOL_AIRBRUSH", /* linux:324 (BTN_TOOL_AIRBRUSH) */ - "TOOL_FINGER", /* linux:325 (BTN_TOOL_FINGER) */ - "TOOL_MOUSE", /* linux:326 (BTN_TOOL_MOUSE) */ - "TOOL_LENS", /* linux:327 (BTN_TOOL_LENS) */ - "328", /* linux:328 (unnamed) */ - "329", /* linux:329 (unnamed) */ - "TOUCH", /* linux:330 (BTN_TOUCH) */ - "STYLUS", /* linux:331 (BTN_STYLUS) */ - "STYLUS2", /* linux:332 (BTN_STYLUS2) */ - "TOOL_DOUBLETAP", /* linux:333 (BTN_TOOL_DOUBLETAP) */ - "TOOL_TRIPLETAP", /* linux:334 (BTN_TOOL_TRIPLETAP) */ - "TOOL_QUADTAP", /* linux:335 (BTN_TOOL_QUADTAP) */ - "GEAR_DOWN", /* linux:336 (BTN_GEAR_DOWN) */ - "GEAR_UP", /* linux:337 (BTN_GEAR_UP) */ - "338", /* linux:338 (unnamed) */ - "339", /* linux:339 (unnamed) */ - "340", /* linux:340 (unnamed) */ - "341", /* linux:341 (unnamed) */ - "342", /* linux:342 (unnamed) */ - "343", /* linux:343 (unnamed) */ - "344", /* linux:344 (unnamed) */ - "345", /* linux:345 (unnamed) */ - "346", /* linux:346 (unnamed) */ - "347", /* linux:347 (unnamed) */ - "348", /* linux:348 (unnamed) */ - "349", /* linux:349 (unnamed) */ - "350", /* linux:350 (unnamed) */ - "351", /* linux:351 (unnamed) */ - "OK", /* linux:352 (KEY_OK) */ - "SELECT", /* linux:353 (KEY_SELECT) */ - "GOTO", /* linux:354 (KEY_GOTO) */ - "CLEAR", /* linux:355 (KEY_CLEAR) */ - "POWER2", /* linux:356 (KEY_POWER2) */ - "OPTION", /* linux:357 (KEY_OPTION) */ - "INFO", /* linux:358 (KEY_INFO) */ - "TIME", /* linux:359 (KEY_TIME) */ - "VENDOR", /* linux:360 (KEY_VENDOR) */ - "ARCHIVE", /* linux:361 (KEY_ARCHIVE) */ - "PROGRAM", /* linux:362 (KEY_PROGRAM) */ - "CHANNEL", /* linux:363 (KEY_CHANNEL) */ - "FAVORITES", /* linux:364 (KEY_FAVORITES) */ - "EPG", /* linux:365 (KEY_EPG) */ - "PVR", /* linux:366 (KEY_PVR) */ - "MHP", /* linux:367 (KEY_MHP) */ - "LANGUAGE", /* linux:368 (KEY_LANGUAGE) */ - "TITLE", /* linux:369 (KEY_TITLE) */ - "SUBTITLE", /* linux:370 (KEY_SUBTITLE) */ - "ANGLE", /* linux:371 (KEY_ANGLE) */ - "ZOOM", /* linux:372 (KEY_ZOOM) */ - "MODE", /* linux:373 (KEY_MODE) */ - "KEYBOARD", /* linux:374 (KEY_KEYBOARD) */ - "SCREEN", /* linux:375 (KEY_SCREEN) */ - "PC", /* linux:376 (KEY_PC) */ - "TV", /* linux:377 (KEY_TV) */ - "TV2", /* linux:378 (KEY_TV2) */ - "VCR", /* linux:379 (KEY_VCR) */ - "VCR2", /* linux:380 (KEY_VCR2) */ - "SAT", /* linux:381 (KEY_SAT) */ - "SAT2", /* linux:382 (KEY_SAT2) */ - "CD", /* linux:383 (KEY_CD) */ - "TAPE", /* linux:384 (KEY_TAPE) */ - "RADIO", /* linux:385 (KEY_RADIO) */ - "TUNER", /* linux:386 (KEY_TUNER) */ - "PLAYER", /* linux:387 (KEY_PLAYER) */ - "TEXT", /* linux:388 (KEY_TEXT) */ - "DVD", /* linux:389 (KEY_DVD) */ - "AUX", /* linux:390 (KEY_AUX) */ - "MP3", /* linux:391 (KEY_MP3) */ - "AUDIO", /* linux:392 (KEY_AUDIO) */ - "VIDEO", /* linux:393 (KEY_VIDEO) */ - "DIRECTORY", /* linux:394 (KEY_DIRECTORY) */ - "LIST", /* linux:395 (KEY_LIST) */ - "MEMO", /* linux:396 (KEY_MEMO) */ - "CALENDAR", /* linux:397 (KEY_CALENDAR) */ - "RED", /* linux:398 (KEY_RED) */ - "GREEN", /* linux:399 (KEY_GREEN) */ - "YELLOW", /* linux:400 (KEY_YELLOW) */ - "BLUE", /* linux:401 (KEY_BLUE) */ - "CHANNELUP", /* linux:402 (KEY_CHANNELUP) */ - "CHANNELDOWN", /* linux:403 (KEY_CHANNELDOWN) */ - "FIRST", /* linux:404 (KEY_FIRST) */ - "LAST", /* linux:405 (KEY_LAST) */ - "AB", /* linux:406 (KEY_AB) */ - "NEXT", /* linux:407 (KEY_NEXT) */ - "RESTART", /* linux:408 (KEY_RESTART) */ - "SLOW", /* linux:409 (KEY_SLOW) */ - "SHUFFLE", /* linux:410 (KEY_SHUFFLE) */ - "BREAK", /* linux:411 (KEY_BREAK) */ - "PREVIOUS", /* linux:412 (KEY_PREVIOUS) */ - "DIGITS", /* linux:413 (KEY_DIGITS) */ - "TEEN", /* linux:414 (KEY_TEEN) */ - "TWEN", /* linux:415 (KEY_TWEN) */ - "VIDEOPHONE", /* linux:416 (KEY_VIDEOPHONE) */ - "GAMES", /* linux:417 (KEY_GAMES) */ - "ZOOMIN", /* linux:418 (KEY_ZOOMIN) */ - "ZOOMOUT", /* linux:419 (KEY_ZOOMOUT) */ - "ZOOMRESET", /* linux:420 (KEY_ZOOMRESET) */ - "WORDPROCESSOR", /* linux:421 (KEY_WORDPROCESSOR) */ - "EDITOR", /* linux:422 (KEY_EDITOR) */ - "SPREADSHEET", /* linux:423 (KEY_SPREADSHEET) */ - "GRAPHICSEDITOR", /* linux:424 (KEY_GRAPHICSEDITOR) */ - "PRESENTATION", /* linux:425 (KEY_PRESENTATION) */ - "DATABASE", /* linux:426 (KEY_DATABASE) */ - "NEWS", /* linux:427 (KEY_NEWS) */ - "VOICEMAIL", /* linux:428 (KEY_VOICEMAIL) */ - "ADDRESSBOOK", /* linux:429 (KEY_ADDRESSBOOK) */ - "MESSENGER", /* linux:430 (KEY_MESSENGER) */ - "DISPLAYTOGGLE", /* linux:431 (KEY_DISPLAYTOGGLE) */ - "SPELLCHECK", /* linux:432 (KEY_SPELLCHECK) */ - "LOGOFF", /* linux:433 (KEY_LOGOFF) */ - "DOLLAR", /* linux:434 (KEY_DOLLAR) */ - "EURO", /* linux:435 (KEY_EURO) */ - "FRAMEBACK", /* linux:436 (KEY_FRAMEBACK) */ - "FRAMEFORWARD", /* linux:437 (KEY_FRAMEFORWARD) */ - "CONTEXT_MENU", /* linux:438 (KEY_CONTEXT_MENU) */ - "MEDIA_REPEAT", /* linux:439 (KEY_MEDIA_REPEAT) */ - "440", /* linux:440 (unnamed) */ - "441", /* linux:441 (unnamed) */ - "442", /* linux:442 (unnamed) */ - "443", /* linux:443 (unnamed) */ - "444", /* linux:444 (unnamed) */ - "445", /* linux:445 (unnamed) */ - "446", /* linux:446 (unnamed) */ - "447", /* linux:447 (unnamed) */ - "DEL_EOL", /* linux:448 (KEY_DEL_EOL) */ - "DEL_EOS", /* linux:449 (KEY_DEL_EOS) */ - "INS_LINE", /* linux:450 (KEY_INS_LINE) */ - "DEL_LINE", /* linux:451 (KEY_DEL_LINE) */ - "452", /* linux:452 (unnamed) */ - "453", /* linux:453 (unnamed) */ - "454", /* linux:454 (unnamed) */ - "455", /* linux:455 (unnamed) */ - "456", /* linux:456 (unnamed) */ - "457", /* linux:457 (unnamed) */ - "458", /* linux:458 (unnamed) */ - "459", /* linux:459 (unnamed) */ - "460", /* linux:460 (unnamed) */ - "461", /* linux:461 (unnamed) */ - "462", /* linux:462 (unnamed) */ - "463", /* linux:463 (unnamed) */ - "FN", /* linux:464 (KEY_FN) */ - "FN_ESC", /* linux:465 (KEY_FN_ESC) */ - "FN_F1", /* linux:466 (KEY_FN_F1) */ - "FN_F2", /* linux:467 (KEY_FN_F2) */ - "FN_F3", /* linux:468 (KEY_FN_F3) */ - "FN_F4", /* linux:469 (KEY_FN_F4) */ - "FN_F5", /* linux:470 (KEY_FN_F5) */ - "FN_F6", /* linux:471 (KEY_FN_F6) */ - "FN_F7", /* linux:472 (KEY_FN_F7) */ - "FN_F8", /* linux:473 (KEY_FN_F8) */ - "FN_F9", /* linux:474 (KEY_FN_F9) */ - "FN_F10", /* linux:475 (KEY_FN_F10) */ - "FN_F11", /* linux:476 (KEY_FN_F11) */ - "FN_F12", /* linux:477 (KEY_FN_F12) */ - "FN_1", /* linux:478 (KEY_FN_1) */ - "FN_2", /* linux:479 (KEY_FN_2) */ - "FN_D", /* linux:480 (KEY_FN_D) */ - "FN_E", /* linux:481 (KEY_FN_E) */ - "FN_F", /* linux:482 (KEY_FN_F) */ - "FN_S", /* linux:483 (KEY_FN_S) */ - "FN_B", /* linux:484 (KEY_FN_B) */ - "485", /* linux:485 (unnamed) */ - "486", /* linux:486 (unnamed) */ - "487", /* linux:487 (unnamed) */ - "488", /* linux:488 (unnamed) */ - "489", /* linux:489 (unnamed) */ - "490", /* linux:490 (unnamed) */ - "491", /* linux:491 (unnamed) */ - "492", /* linux:492 (unnamed) */ - "493", /* linux:493 (unnamed) */ - "494", /* linux:494 (unnamed) */ - "495", /* linux:495 (unnamed) */ - "496", /* linux:496 (unnamed) */ - "BRL_DOT1", /* linux:497 (KEY_BRL_DOT1) */ - "BRL_DOT2", /* linux:498 (KEY_BRL_DOT2) */ - "BRL_DOT3", /* linux:499 (KEY_BRL_DOT3) */ - "BRL_DOT4", /* linux:500 (KEY_BRL_DOT4) */ - "BRL_DOT5", /* linux:501 (KEY_BRL_DOT5) */ - "BRL_DOT6", /* linux:502 (KEY_BRL_DOT6) */ - "BRL_DOT7", /* linux:503 (KEY_BRL_DOT7) */ - "BRL_DOT8", /* linux:504 (KEY_BRL_DOT8) */ - "BRL_DOT9", /* linux:505 (KEY_BRL_DOT9) */ - "BRL_DOT10", /* linux:506 (KEY_BRL_DOT10) */ - "507", /* linux:507 (unnamed) */ - "508", /* linux:508 (unnamed) */ - "509", /* linux:509 (unnamed) */ - "510", /* linux:510 (unnamed) */ - "511", /* linux:511 (unnamed) */ - "NUMERIC_0", /* linux:512 (KEY_NUMERIC_0) */ - "NUMERIC_1", /* linux:513 (KEY_NUMERIC_1) */ - "NUMERIC_2", /* linux:514 (KEY_NUMERIC_2) */ - "NUMERIC_3", /* linux:515 (KEY_NUMERIC_3) */ - "NUMERIC_4", /* linux:516 (KEY_NUMERIC_4) */ - "NUMERIC_5", /* linux:517 (KEY_NUMERIC_5) */ - "NUMERIC_6", /* linux:518 (KEY_NUMERIC_6) */ - "NUMERIC_7", /* linux:519 (KEY_NUMERIC_7) */ - "NUMERIC_8", /* linux:520 (KEY_NUMERIC_8) */ - "NUMERIC_9", /* linux:521 (KEY_NUMERIC_9) */ - "NUMERIC_STAR", /* linux:522 (KEY_NUMERIC_STAR) */ - "NUMERIC_POUND", /* linux:523 (KEY_NUMERIC_POUND) */ - "KEY_NUMERIC_A", /* linux:524 (KEY_NUMERIC_A) */ - }; - - static bool GetEventName(const char* dev_type, int map, int event, bool is_button, const char** name) - { - if (!name) - return false; - - if (is_button) - { - if (event < (int)key_to_str.size()) - { - *name = key_to_str[event]; - return true; - } - return false; - } - - // assuming that PS2 axes are always mapped to PC axes - static char axis[256] = {0}; - snprintf(axis, sizeof(axis), "Axis %d", event); - *name = axis; - return true; - } - - static bool PollInput(const std::vector>& fds, std::string& dev_name, bool isaxis, int& value, bool& inverted, int& initial) - { - int event_fd = -1; - ssize_t len; - input_event event; - struct AxisValue - { - int16_t value; - bool initial; - }; - AxisValue axisVal[ABS_MAX + 1]{}; - unsigned long absbit[NBITS(ABS_MAX)]{}; - axis_correct abs_correct[ABS_MAX]{}; - - inverted = false; - - fd_set fdset; - int maxfd = -1; - - FD_ZERO(&fdset); - for (const auto& js : fds) - { - FD_SET(js.second.fd, &fdset); - if (maxfd < js.second.fd) - maxfd = js.second.fd; - } - - // wait to avoid some false positives like mouse movement - std::this_thread::sleep_for(ms(250)); - - // empty event queues - for (const auto& js : fds) - while ((len = read(js.second.fd, &event, sizeof(event))) > 0) - ; - - timeval timeout{}; - timeout.tv_sec = 5; - int result = select(maxfd + 1, &fdset, NULL, NULL, &timeout); - - if (!result) - return false; - - if (result == -1) - { - return false; - } - - for (const auto& js : fds) - { - if (FD_ISSET(js.second.fd, &fdset)) - { - event_fd = js.second.fd; - dev_name = js.first; - break; - } - } - - if (event_fd == -1) - return false; - - if (isaxis && ioctl(event_fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) - { - for (int i = 0; i < ABS_MAX; ++i) - { - if (test_bit(i, absbit)) - { - struct input_absinfo absinfo; - - if (ioctl(event_fd, EVIOCGABS(i), &absinfo) < 0) - { - continue; - } - - //TODO from SDL2, usable here? - CalcAxisCorr(abs_correct[i], absinfo); - } - } - } - - auto last = sys_clock::now(); - //Non-blocking read sets len to -1 and errno to EAGAIN if no new data - while (true) - { - auto dur = std::chrono::duration_cast(sys_clock::now() - last).count(); - if (dur > 5000) - goto error; - - if (!isaxis) - { - event_fd = -1; - for (const auto& js : fds) - { - if (FD_ISSET(js.second.fd, &fdset)) - { - event_fd = js.second.fd; - dev_name = js.first; - - break; - } - } - } - - if (event_fd > -1 && (len = read(event_fd, &event, sizeof(event))) > -1 && (len == sizeof(event))) - { - if (isaxis && event.type == EV_ABS) - { - auto& val = axisVal[event.code]; - - if (!val.initial) - { - //val.value = event.value; - val.value = AxisCorrect(abs_correct[event.code], event.value); - val.initial = true; - } - //else if (std::abs(event.value - val.value) > 1000) - else - { - int ac_val = AxisCorrect(abs_correct[event.code], event.value); - int diff = ac_val - val.value; - if (std::abs(diff) > 2047) - { - value = event.code; - inverted = (diff < 0); - initial = val.value; - break; - } - } - } - else if (!isaxis && event.type == EV_KEY) - { - if (event.value) - { - value = event.code; - break; - } - } - } - else if (errno != EAGAIN) - { - goto error; - } - else - { - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); - std::this_thread::sleep_for(ms(1)); - } - } - - return true; - - error: - return false; - } - - int EvDevPad::Configure(int port, const char* dev_type, void* data) - { - ApiCallbacks apicbs{GetEventName, EnumerateDevices, PollInput}; - int ret = 0; - if (!strcmp(dev_type, BuzzDevice::TypeName())) - ret = GtkBuzzConfigure(port, dev_type, "Evdev Settings", evdev::APINAME, GTK_WINDOW(data), apicbs); - else if (!strcmp(dev_type, KeyboardmaniaDevice::TypeName())) - ret = GtkKeyboardmaniaConfigure(port, dev_type, "Evdev Settings", evdev::APINAME, GTK_WINDOW(data), apicbs); - else - ret = GtkPadConfigure(port, dev_type, "Evdev Settings", evdev::APINAME, GTK_WINDOW(data), apicbs); - return ret; - } - -#undef EVDEV_DIR - } // namespace evdev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/evdev/evdev.cpp b/pcsx2/USB/usb-pad/evdev/evdev.cpp deleted file mode 100644 index f81ca4a509..0000000000 --- a/pcsx2/USB/usb-pad/evdev/evdev.cpp +++ /dev/null @@ -1,677 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "evdev.h" -#include -#include -#include -#include "USB/linux/util.h" - -namespace usb_pad -{ - namespace evdev - { - - // hidraw* to input/event*: - // /sys/class/hidraw/hidraw*/device/input/input*/event*/uevent - -#define NORM(x, n) (((uint32_t)(32768 + x) * n) / 0xFFFF) -#define NORM2(x, n) (((uint32_t)(32768 + x) * n) / 0x7FFF) - - bool str_ends_with(const char* str, const char* suffix) - { - if (str == nullptr || suffix == nullptr) - return false; - - size_t str_len = strlen(str); - size_t suffix_len = strlen(suffix); - - if (suffix_len > str_len) - return false; - - return 0 == strncmp(str + str_len - suffix_len, suffix, suffix_len); - } - - bool FindHidraw(const std::string& evphys, std::string& hid_dev, int* vid, int* pid) - { - int fd; - int res; - char buf[256]; - - std::stringstream str; - struct dirent* dp; - - DIR* dirp = opendir("/dev/"); - if (!dirp) - { - Console.Warning("Error opening /dev/"); - return false; - } - - while ((dp = readdir(dirp))) - { - if (strncmp(dp->d_name, "hidraw", 6) == 0) - { - - str.clear(); - str.str(""); - str << "/dev/" << dp->d_name; - std::string path = str.str(); - fd = open(path.c_str(), O_RDWR | O_NONBLOCK); - - if (fd < 0) - { - Console.Warning("Evdev: Unable to open device: %s", path.c_str()); - continue; - } - - memset(buf, 0x0, sizeof(buf)); - //res = ioctl(fd, HIDIOCGRAWNAME(256), buf); - - res = ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf); - if (res < 0) - Console.Warning("HIDIOCGRAWPHYS"); - - struct hidraw_devinfo info; - memset(&info, 0x0, sizeof(info)); - - if (ioctl(fd, HIDIOCGRAWINFO, &info) < 0) - { - Console.Warning("HIDIOCGRAWINFO"); - } - else - { - if (vid) - *vid = info.vendor; - if (pid) - *pid = info.product; - } - - close(fd); - if (evphys == buf) - { - closedir(dirp); - hid_dev = path; - return true; - } - } - } - closedir(dirp); - return false; - } - -#define EVDEV_DIR "/dev/input/by-id/" - void EnumerateDevices(device_list& list) - { - int fd; - int res; - char buf[256]; - - std::stringstream str; - struct dirent* dp; - - //TODO do some caching? ioctl is "very" slow - static device_list list_cache; - - DIR* dirp = opendir(EVDEV_DIR); - if (!dirp) - { - Console.Warning("Error opening " EVDEV_DIR); - return; - } - - // get rid of unplugged devices - for (int i = 0; i < (int)list_cache.size();) - { - if (!file_exists(list_cache[i].path)) - list_cache.erase(list_cache.begin() + i); - else - i++; - } - - while ((dp = readdir(dirp))) - { - //if (strncmp(dp->d_name, "event", 5) == 0) { - if (str_ends_with(dp->d_name, "event-kbd") || str_ends_with(dp->d_name, "event-mouse") || str_ends_with(dp->d_name, "event-joystick")) - { - - str.clear(); - str.str(""); - str << EVDEV_DIR << dp->d_name; - const std::string path = str.str(); - - auto it = std::find_if(list_cache.begin(), list_cache.end(), - [&path](evdev_device& dev) { - return dev.path == path; - }); - if (it != list_cache.end()) - continue; - - fd = open(path.c_str(), O_RDWR | O_NONBLOCK); - - if (fd < 0) - { - Console.Warning("Evdev: Unable to open device: %s", path.c_str()); - continue; - } - - res = ioctl(fd, EVIOCGNAME(sizeof(buf)), buf); - if (res < 0) - Console.Warning("EVIOCGNAME"); - else - { - evdev_device dev{buf, dp->d_name, path, {}}; - res = ioctl(fd, EVIOCGID, &dev.input_id); - list_cache.push_back(dev); - } - - close(fd); - } - } - - list.assign(list_cache.begin(), list_cache.end()); - closedir(dirp); - } - - void EvDevPad::PollAxesValues(const device_data& device) - { - struct input_absinfo absinfo; - - /* Poll all axis */ - for (int i = ABS_X; i < ABS_MAX; i++) - { - absinfo = {}; - - if ((ioctl(device.cfg.fd, EVIOCGABS(i), &absinfo) >= 0) && - device.abs_correct[i].used) - { - absinfo.value = AxisCorrect(device.abs_correct[i], absinfo.value); - } - SetAxis(device, i, absinfo.value); - } - } - - void EvDevPad::SetAxis(const device_data& device, int event_code, int value) - { - int range = range_max(mType); - int code = device.axis_map[event_code] != (uint8_t)-1 ? device.axis_map[event_code] : -1 /* allow axis to be unmapped */; //event_code; - //value = AxisCorrect(mAbsCorrect[event_code], value); - - switch (code) - { - case 0x80 | JOY_STEERING: - mWheelData.steering = device.cfg.inverted[0] ? range - NORM(value, range) : NORM(value, range); - break; - case 0x80 | JOY_THROTTLE: - mWheelData.throttle = device.cfg.inverted[1] ? NORM(value, 0xFF) : 0xFF - NORM(value, 0xFF); - break; - case 0x80 | JOY_BRAKE: - mWheelData.brake = device.cfg.inverted[2] ? NORM(value, 0xFF) : 0xFF - NORM(value, 0xFF); - break; - - //TODO hatswitch mapping maybe - case ABS_HAT0X: - case ABS_HAT1X: - case ABS_HAT2X: - case ABS_HAT3X: - if (value < 0) //left usually - mWheelData.hat_horz = PAD_HAT_W; - else if (value > 0) //right - mWheelData.hat_horz = PAD_HAT_E; - else - mWheelData.hat_horz = PAD_HAT_COUNT; - break; - case ABS_HAT0Y: - case ABS_HAT1Y: - case ABS_HAT2Y: - case ABS_HAT3Y: - if (value < 0) //up usually - mWheelData.hat_vert = PAD_HAT_N; - else if (value > 0) //down - mWheelData.hat_vert = PAD_HAT_S; - else - mWheelData.hat_vert = PAD_HAT_COUNT; - break; - default: - break; - } - } - - int EvDevPad::TokenIn(uint8_t* buf, int buflen) - { - ssize_t len; - - input_event events[32]; - fd_set fds; - int maxfd; - - FD_ZERO(&fds); - maxfd = -1; - - for (auto& device : mDevices) - { - FD_SET(device.cfg.fd, &fds); - if (maxfd < device.cfg.fd) - maxfd = device.cfg.fd; - } - - struct timeval timeout; - timeout.tv_usec = timeout.tv_sec = 0; // 0 - return from select immediately - int result = select(maxfd + 1, &fds, NULL, NULL, &timeout); - - if (result <= 0) - { - return USB_RET_NAK; // If no new data, NAK it - } - - for (auto& device : mDevices) - { - if (!FD_ISSET(device.cfg.fd, &fds)) - { - continue; - } - - //Non-blocking read sets len to -1 and errno to EAGAIN if no new data - while ((len = read(device.cfg.fd, &events, sizeof(events))) > -1) - { - len /= sizeof(events[0]); - for (int i = 0; i < len; i++) - { - input_event& event = events[i]; - switch (event.type) - { - case EV_ABS: - { - if (mType == WT_BUZZ_CONTROLLER) - break; - - int value = AxisCorrect(device.abs_correct[event.code], event.value); - //if (event.code == 0) - // event.code, device.axis_map[event.code] & ~0x80, event.value, value); - SetAxis(device, event.code, value); - } - break; - case EV_KEY: - { - uint16_t button = device.btn_map[event.code]; - if (button == (uint16_t)-1 || !(button & 0x8000)) - break; - - button = button & ~0x8000; - - if (event.value) - mWheelData.buttons |= 1 << convert_wt_btn(mType, button); //on - else - mWheelData.buttons &= ~(1 << convert_wt_btn(mType, button)); //off - } - break; - case EV_SYN: //TODO useful? - { - switch (event.code) - { - case SYN_DROPPED: - //restore last good state - mWheelData = {}; - PollAxesValues(device); - break; - } - } - break; - default: - break; - } - } - - if (len <= 0) - { - break; - } - } - } - - switch (mWheelData.hat_vert) - { - case PAD_HAT_N: - switch (mWheelData.hat_horz) - { - case PAD_HAT_W: - mWheelData.hatswitch = PAD_HAT_NW; - break; - case PAD_HAT_E: - mWheelData.hatswitch = PAD_HAT_NE; - break; - default: - mWheelData.hatswitch = PAD_HAT_N; - break; - } - break; - case PAD_HAT_S: - switch (mWheelData.hat_horz) - { - case PAD_HAT_W: - mWheelData.hatswitch = PAD_HAT_SW; - break; - case PAD_HAT_E: - mWheelData.hatswitch = PAD_HAT_SE; - break; - default: - mWheelData.hatswitch = PAD_HAT_S; - break; - } - break; - default: - mWheelData.hatswitch = mWheelData.hat_horz; - break; - } - - pad_copy_data(mType, buf, mWheelData); - return buflen; - } - - int EvDevPad::TokenOut(const uint8_t* data, int len) - { - if (mUseRawFF) - { - if (mType <= WT_GT_FORCE) - { - if (data[0] == 0x8 || data[0] == 0xB) - return len; - if (data[0] == 0xF8 && - /* Allow range changes */ - !(data[1] == 0x81 || data[1] == 0x02 || data[1] == 0x03)) - return len; //don't send extended commands - } - - std::array report{0}; - - memcpy(report.data() + 1, data, report.size() - 1); - - if (!mFFData.enqueue(report)) - { - return 0; - } - return len; - } - - if (mType <= WT_GT_FORCE) - { - const ff_data* ffdata = (const ff_data*)data; - bool hires = (mType == WT_DRIVING_FORCE_PRO || mType == WT_DRIVING_FORCE_PRO_1102); - ParseFFData(ffdata, hires); - } - - return len; - } - - int EvDevPad::Open() - { - std::stringstream name; - device_list device_list; - char buf[1024]; - mWheelData = {}; - int32_t b_gain, gain, b_ac, ac; - - unsigned long keybit[NBITS(KEY_MAX)]; - unsigned long absbit[NBITS(ABS_MAX)]; - memset(keybit, 0, sizeof(keybit)); - memset(absbit, 0, sizeof(absbit)); - - // Setting to unpressed - mWheelData.steering = 0x3FF >> 1; - mWheelData.clutch = 0xFF; - mWheelData.throttle = 0xFF; - mWheelData.brake = 0xFF; - mWheelData.hatswitch = 0x8; - mWheelData.hat_horz = 0x8; - mWheelData.hat_vert = 0x8; - //memset(mAxisMap, -1, sizeof(mAxisMap)); - //memset(mBtnMap, -1, sizeof(mBtnMap)); - - //mAxisCount = 0; - //mButtonCount = 0; - //mHandle = -1; - - std::string evphys, hid_dev; - - switch (mType) - { - case WT_GENERIC: - case WT_GT_FORCE: - case WT_DRIVING_FORCE_PRO: - case WT_DRIVING_FORCE_PRO_1102: - { - if (!LoadSetting(mDevType, mPort, APINAME, N_HIDRAW_FF_PT, mUseRawFF)) - mUseRawFF = 0; - } - break; - default: - break; - } - - if (mUseRawFF) - { - // TODO could just use device fd below whose axis is mapped to steering - std::string joypath; - if (!LoadSetting(mDevType, mPort, APINAME, N_JOYSTICK, joypath)) - { - return 1; - } - - if (joypath.empty() || !file_exists(joypath)) - return 1; - - int fd = -1; - if ((fd = open(joypath.c_str(), O_RDWR | O_NONBLOCK)) < 0) - { - return 1; - } - - memset(buf, 0, sizeof(buf)); - if (ioctl(fd, EVIOCGPHYS(sizeof(buf) - 1), buf) > 0) - { - evphys = buf; - - int pid, vid; - if ((mUseRawFF = FindHidraw(evphys, hid_dev, &vid, &pid))) - { - // check if still using hidraw and run the thread - if (mUseRawFF && !mWriterThreadIsRunning) - { - if (mWriterThread.joinable()) - mWriterThread.join(); - mWriterThread = std::thread(&EvDevPad::WriterThread, this); - } - } - } - else - { - Console.Warning("EVIOCGPHYS failed"); - } - close(fd); - } - - EnumerateDevices(device_list); - - for (const auto& it : device_list) - { - bool has_mappings = false; - mDevices.push_back({}); - - struct device_data& device = mDevices.back(); - device.name = it.name; - - if ((device.cfg.fd = open(it.path.c_str(), O_RDWR | O_NONBLOCK)) < 0) - { - continue; - } - - int ret_abs = ioctl(device.cfg.fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit); - int ret_key = ioctl(device.cfg.fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit); - memset(device.axis_map, 0xFF, sizeof(device.axis_map)); - memset(device.btn_map, 0xFF, sizeof(device.btn_map)); - - if ((ret_abs < 0) && (ret_key < 0)) - { - // Probably isn't a evdev joystick - SysMessage("%s: Getting atleast some of the bits failed: %s\n", APINAME, strerror(errno)); - continue; - } - - // device.cfg.controls[0..max_buttons] - mapped buttons - // device.cfg.controls[max_buttons..etc] - mapped axes - int max_buttons = 0; - int max_axes = 0; - switch (mType) - { - case WT_BUZZ_CONTROLLER: - LoadBuzzMappings(mDevType, mPort, it.id, device.cfg); - max_buttons = 20; - break; - case WT_KEYBOARDMANIA_CONTROLLER: - max_buttons = 31; - LoadMappings(mDevType, mPort, it.id, max_buttons, 0, device.cfg); - break; - default: - max_buttons = JOY_STEERING; - max_axes = 3; - LoadMappings(mDevType, mPort, it.id, max_buttons, 3, device.cfg); - if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN_ENABLED, b_gain)) - b_gain = 1; - if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN, gain)) - gain = 100; - if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER_MANAGED, b_ac)) - b_ac = 1; - if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER, ac)) - ac = 100; - break; - } - - // Map hatswitches automatically - //FIXME has_mappings is gonna ignore hatsw only devices - for (int i = ABS_HAT0X; i <= ABS_HAT3Y; ++i) - { - device.axis_map[i] = i; - } - - // SDL2 - for (int i = 0; i < ABS_MAX; ++i) - { - if (test_bit(i, absbit)) - { - struct input_absinfo absinfo; - - if (ioctl(device.cfg.fd, EVIOCGABS(i), &absinfo) < 0) - { - continue; - } - - // convert values into 16 bit range - CalcAxisCorr(device.abs_correct[i], absinfo); - - // FIXME axes as buttons - for (int k = 0; k < max_axes; k++) - { - if (i == device.cfg.controls[k + max_buttons]) - { - has_mappings = true; - device.axis_map[i] = 0x80 | (k + JOY_STEERING); - // TODO Instead of single FF instance, create for every device with X-axis??? - // and then switch between them according to which device was used recently - if (k == 0 && !mFFdev && !mUseRawFF) - { - mFFdev = new EvdevFF(device.cfg.fd, b_gain, gain, b_ac, ac); - } - } - } - } - } - - for (int k = 0; k < max_buttons; k++) - { - auto i = device.cfg.controls[k]; - if (i >= 0 && i <= KEY_MAX) - { - has_mappings = true; - device.btn_map[i] = 0x8000 | k; - } - } - - if (!has_mappings) - { - close(device.cfg.fd); - mDevices.pop_back(); - } - } - - return 0; - } - - int EvDevPad::Close() - { - delete mFFdev; - mFFdev = nullptr; - - if (mHidHandle != -1) - { - if (mType <= WT_GT_FORCE) - { - uint8_t reset[7] = {0}; - reset[0] = 0xF3; //stop forces - if (write(mHidHandle, reset, sizeof(reset)) == -1) - { - } - } - close(mHidHandle); - } - - mHidHandle = -1; - for (auto& it : mDevices) - { - close(it.cfg.fd); - it.cfg.fd = -1; - } - mDevices.clear(); - return 0; - } - - void EvDevPad::WriterThread() - { - std::array buf; - int res; - - mWriterThreadIsRunning = true; - - while (mHidHandle != -1) - { - //if (mFFData.wait_dequeue_timed(buf, std::chrono::milliseconds(1000))) //FIXME SIGABORT :S - if (mFFData.try_dequeue(buf)) - { - res = write(mHidHandle, buf.data(), buf.size()); - if (res < 0) - { - Console.Warning("write"); - } - } - else - { // TODO skip sleep for few while cycles? - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - } - - mWriterThreadIsRunning = false; - } - - } // namespace evdev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/evdev/evdev.h b/pcsx2/USB/usb-pad/evdev/evdev.h deleted file mode 100644 index 8d267c7fd1..0000000000 --- a/pcsx2/USB/usb-pad/evdev/evdev.h +++ /dev/null @@ -1,163 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -#include "evdev-ff.h" -#include "shared.h" -#include "USB/linux/util.h" -#include "USB/readerwriterqueue/readerwriterqueue.h" -#include "common/Console.h" -//#include //gtk.h pulls in? -#include -#include -#include - -namespace usb_pad -{ - namespace evdev - { - -#define test_bit(nr, addr) \ - (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0) -#define NBITS(x) ((((x)-1) / (sizeof(long) * 8)) + 1) - - void EnumerateDevices(device_list& list); - - static constexpr const char* APINAME = "evdev"; - - class EvDevPad : public Pad - { - public: - EvDevPad(int port, const char* dev_type) - : Pad(port, dev_type) - , mWriterThreadIsRunning(false) - { - } - - ~EvDevPad() { Close(); } - int Open(); - int Close(); - int TokenIn(uint8_t* buf, int len); - int TokenOut(const uint8_t* data, int len); - int Reset() { return 0; } - - static const TCHAR* Name() - { - return "Evdev"; - } - - static int Configure(int port, const char* dev_type, void* data); - - protected: - void PollAxesValues(const device_data& device); - void SetAxis(const device_data& device, int code, int value); - void WriterThread(); - - int mHidHandle = -1; - EvdevFF* mEvdevFF = nullptr; - struct wheel_data_t mWheelData - { - }; - std::vector mDevices; - int32_t mUseRawFF = 0; - std::thread mWriterThread; - std::atomic mWriterThreadIsRunning; - moodycamel::BlockingReaderWriterQueue, 32> mFFData; - }; - - template - bool GetEvdevName(const std::string& path, char (&name)[_Size]) - { - int fd = 0; - if ((fd = open(path.c_str(), O_RDONLY)) < 0) - { - Console.Warning("Cannot open %s\n", path.c_str()); - } - else - { - if (ioctl(fd, EVIOCGNAME(_Size), name) < -1) - { - Console.Warning("Cannot get controller's name\n"); - close(fd); - return false; - } - close(fd); - return true; - } - return false; - } - - static void CalcAxisCorr(axis_correct& abs_correct, struct input_absinfo absinfo) - { - int t; - // convert values into 16 bit range - if (absinfo.minimum == absinfo.maximum) - { - abs_correct.used = 0; - } - else - { - abs_correct.used = 1; - abs_correct.coef[0] = - (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat; - abs_correct.coef[1] = - (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat; - t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat); - if (t != 0) - { - abs_correct.coef[2] = - (1 << 28) / t; - } - else - { - abs_correct.coef[2] = 0; - } - } - } - - // SDL2 - // convert values into 16 bit range - static int AxisCorrect(const axis_correct& correct, int value) - { - if (correct.used) - { - value *= 2; - if (value > correct.coef[0]) - { - if (value < correct.coef[1]) - { - return 0; - } - value -= correct.coef[1]; - } - else - { - value -= correct.coef[0]; - } - value *= correct.coef[2]; - value >>= 13; - } - - /* Clamp and return */ - if (value < -32768) - return -32768; - if (value > 32767) - return 32767; - - return value; - } - - } // namespace evdev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp b/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp deleted file mode 100644 index 65ef9edb55..0000000000 --- a/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp +++ /dev/null @@ -1,1117 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "shared.h" -#include "USB/icon_buzz_24.h" -#include "common/Console.h" - -#include -#include -#include -#include - -namespace usb_pad -{ - namespace evdev - { - - using sys_clock = std::chrono::system_clock; - using ms = std::chrono::milliseconds; - - constexpr auto CONTROL = "control"; - constexpr auto CFG = "cfg"; - - // Buttons from 0, axes after buttons - bool LoadMappings(const char* dev_type, int port, const std::string& joyname, u32 max_buttons, u32 max_axes, ConfigMapping& cfg) - { - std::stringstream str; - const bool use_control_names = !strcmp(dev_type, PadDevice::TypeName()); - - if (joyname.empty()) - return false; - - cfg.controls.resize(max_buttons + max_axes); - for (u32 i = 0; i < max_buttons + max_axes; i++) - { - str.clear(); - str.str(""); - if (i < max_buttons) - { - str << "button_"; - if (use_control_names && i < (u32)countof(JoystickMapNames)) - str << JoystickMapNames[i]; - else - str << i; - } - else - { - str << "axis_"; - u32 axis = i - max_buttons; - if (use_control_names && (JOY_STEERING + axis) < (u32)countof(JoystickMapNames)) - str << JoystickMapNames[JOY_STEERING + axis]; - else - str << axis; - } - - const std::string& name = str.str(); - int32_t var; - if (LoadSetting(dev_type, port, joyname, name.c_str(), var)) - cfg.controls[i] = var; - else - cfg.controls[i] = -1; - } - - for (int i = 0; i < 3; i++) - { - str.clear(); - str.str(""); - str << "inverted_"; - if (use_control_names) - str << JoystickMapNames[JOY_STEERING + i]; - else - str << i; - - { - const std::string& name = str.str(); - if (!LoadSetting(dev_type, port, joyname, name.c_str(), cfg.inverted[i])) - cfg.inverted[i] = 0; - } - - str.clear(); - str.str(""); - str << "initial_"; - if (use_control_names) - str << JoystickMapNames[JOY_STEERING + i]; - else - str << i; - - { - const std::string& name = str.str(); - if (!LoadSetting(dev_type, port, joyname, name.c_str(), cfg.initial[i])) - cfg.initial[i] = 0; - } - } - return true; - } - - bool SaveMappings(const char* dev_type, int port, const std::string& joyname, u32 max_buttons, u32 max_axes, const ConfigMapping& cfg) - { - if (joyname.empty() || cfg.controls.size() != max_buttons + max_axes) - return false; - - RemoveSection(dev_type, port, joyname); - std::stringstream str; - const bool use_control_names = !strcmp(dev_type, PadDevice::TypeName()); - bool has_axes = false; - - for (u32 i = 0; i < max_buttons + max_axes; i++) - { - str.clear(); - str.str(""); - if (i < max_buttons) - { - str << "button_"; - if (use_control_names && i < (u32)countof(JoystickMapNames)) - str << JoystickMapNames[i]; - else - str << i; - } - else - { - str << "axis_"; - u32 axis = i - max_buttons; - if (use_control_names && (JOY_STEERING + axis) < (u32)countof(JoystickMapNames)) - str << JoystickMapNames[JOY_STEERING + axis]; - else - str << axis; - } - - const std::string& name = str.str(); - if (cfg.controls[i] >= 0) - { - if (!SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) - return false; - if (i >= max_buttons) - has_axes = true; - } - } - - for (u32 i = 0; i < 3 && has_axes; i++) - { - str.clear(); - str.str(""); - str << "inverted_"; - if (use_control_names) - str << JoystickMapNames[JOY_STEERING + i]; - else - str << i; - - { - const std::string& name = str.str(); - if (!SaveSetting(dev_type, port, joyname, name.c_str(), cfg.inverted[i])) - return false; - } - - str.clear(); - str.str(""); - str << "initial_"; - if (use_control_names) - str << JoystickMapNames[JOY_STEERING + i]; - else - str << i; - - { - const std::string& name = str.str(); - if (!SaveSetting(dev_type, port, joyname, name.c_str(), cfg.initial[i])) - return false; - } - } - return true; - } - - bool LoadBuzzMappings(const char* dev_type, int port, const std::string& joyname, ConfigMapping& cfg) - { - std::stringstream str; - - if (joyname.empty()) - return false; - - int j = 0; - - cfg.controls.resize(countof(buzz_map_names) * 4); - for (auto& i : cfg.controls) - { - str.str(""); - str.clear(); - str << "map_" << buzz_map_names[j % 5] << "_" << (j / 5); - const std::string& name = str.str(); - int32_t var; - if (LoadSetting(dev_type, port, joyname, name.c_str(), var)) - i = var; - else - i = -1; - j++; - } - return true; - } - - bool SaveBuzzMappings(const char* dev_type, int port, const std::string& joyname, const ConfigMapping& cfg) - { - if (joyname.empty()) - return false; - - RemoveSection(dev_type, port, joyname); - std::stringstream str; - - const size_t c = countof(buzz_map_names); - for (size_t i = 0; i < cfg.controls.size(); i++) - { - str.str(""); - str.clear(); - str << "map_" << buzz_map_names[i % c] << "_" << (i / c); - const std::string& name = str.str(); - if (cfg.controls[i] >= 0 && !SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) - return false; - } - return true; - } - - static void refresh_store(ConfigData* cfg) - { - GtkTreeIter iter; - std::string name; - - gtk_list_store_clear(cfg->store); - for (auto& it : cfg->jsconf) - { - for (size_t i = 0; i < it.second.controls.size(); i++) - { - if (it.second.controls[i] < 0) - continue; - - const char* pc_name = "Unknown"; - bool is_button = (i < cfg->max_buttons); - cfg->cb->get_event_name(cfg->dev_type, i, it.second.controls[i], is_button, &pc_name); - - gtk_list_store_append(cfg->store, &iter); - - if (!strcmp(cfg->dev_type, BuzzDevice::TypeName())) - { - std::stringstream ss; - ss << (1 + i / countof(buzz_map_names)); - ss << " "; - ss << buzz_map_names[i % countof(buzz_map_names)]; - name = ss.str(); - } - else if (!strcmp(cfg->dev_type, PadDevice::TypeName())) - name = JoystickMapNames[i]; - else if (!strcmp(cfg->dev_type, KeyboardmaniaDevice::TypeName())) - name = kbdmania_key_labels[i]; - else - { - std::stringstream ss; - if (is_button) - ss << "Button " << i; - else - ss << "Axis " << (i - cfg->max_buttons); - name = ss.str(); - } - - gtk_list_store_set(cfg->store, &iter, - COL_NAME, it.first.c_str(), - COL_PS2, name.c_str(), - COL_PC, pc_name, - COL_BINDING, i, - -1); - } - } - } - - static void joystick_changed(GtkComboBox* widget, gpointer data) - { - gint idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - //int port = reinterpret_cast(data); - ConfigData* cfg = reinterpret_cast(g_object_get_data(G_OBJECT(widget), CFG)); - - if (!cfg) - return; - - if (idx > -1) - cfg->js_iter = (cfg->joysticks.begin() + idx); - } - - static void button_clicked(GtkWidget* widget, gpointer data) - { - u32 control = reinterpret_cast(g_object_get_data(G_OBJECT(widget), CONTROL)); - ConfigData* cfg = reinterpret_cast(g_object_get_data(G_OBJECT(widget), CFG)); - if (!cfg) - return; - - int value, initial = 0; - std::string dev_name; - bool inverted = false; - bool is_axis = (control >= cfg->max_buttons); - - gtk_label_set_text(GTK_LABEL(cfg->label), "Polling for input for 5 seconds..."); - - // let label change its text - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); - - if (cfg->cb->poll(cfg->jsconf, dev_name, is_axis, value, inverted, initial)) - { - auto it = std::find_if(cfg->jsconf.begin(), cfg->jsconf.end(), - [&dev_name](MappingPair& i) -> bool { - return i.first == dev_name; - }); - - if (it != cfg->jsconf.end() && control < (u32)it->second.controls.size()) - { - it->second.controls[control] = value; - if (is_axis && control - cfg->max_buttons < countof(it->second.inverted)) - { - it->second.inverted[control - cfg->max_buttons] = inverted; - it->second.initial[control - cfg->max_buttons] = initial; - } - refresh_store(cfg); - } - } - gtk_label_set_text(GTK_LABEL(cfg->label), ""); - } - - // save references to row paths, automatically updated when store changes - static void view_selected_foreach_func(GtkTreeModel* model, - GtkTreePath* path, GtkTreeIter* iter, gpointer userdata) - { - GList** rr_list = (GList**)userdata; - GtkTreeRowReference* rowref; - rowref = gtk_tree_row_reference_new(model, path); - *rr_list = g_list_append(*rr_list, rowref); - } - - static void view_remove_binding(GtkTreeModel* model, - GtkTreeIter* iter, ConfigData* cfg) - { - gchar* dev_name; - int binding; - - gtk_tree_model_get(model, iter, COL_NAME, &dev_name, COL_BINDING, &binding, -1); - - auto& js = cfg->jsconf; - auto it = std::find_if(js.begin(), js.end(), - [&dev_name](MappingPair i) { - return i.first == dev_name; - }); - if (it != js.end()) - { - it->second.controls[binding] = (uint16_t)-1; - } - gtk_list_store_remove(GTK_LIST_STORE(model), iter); - //refresh_store(cfg); - - g_free(dev_name); - } - - static void clear_binding_clicked(GtkWidget* widget, gpointer data) - { - GtkTreeModel* model = nullptr; - GList* rr_list = nullptr; - GList* node = nullptr; - - ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); - GtkTreeSelection* sel = gtk_tree_view_get_selection(cfg->treeview); - - gtk_tree_selection_selected_foreach(sel, view_selected_foreach_func, &rr_list); - - GList* list = gtk_tree_selection_get_selected_rows(sel, &model); - // remove rows from store pointed to by row references - for (node = g_list_first(rr_list); node != nullptr; node = node->next) - { - GtkTreePath* path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node->data); - if (path) - { - GtkTreeIter iter; - if (gtk_tree_model_get_iter(model, &iter, path)) - { - view_remove_binding(model, &iter, cfg); - } - } - } - - g_list_free_full(rr_list, (GDestroyNotify)gtk_tree_row_reference_free); - g_list_free_full(list, (GDestroyNotify)gtk_tree_path_free); - } - - static void clear_all_clicked(GtkWidget* widget, gpointer data) - { - ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); - for (auto& it : cfg->jsconf) - it.second.controls.assign(it.second.controls.size(), -1); - refresh_store(cfg); - } - - static void checkbox_toggled(GtkToggleButton* widget, gpointer data) - { - gboolean* val = reinterpret_cast(data); - if (val) - { - *val = (bool)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - } - } - - static GtkWidget* make_dialog(GtkWindow* parent, const std::string& title, int w = 1200, int h = 700) - { - auto dlg = gtk_dialog_new_with_buttons( - title.c_str(), parent, GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OK, GTK_RESPONSE_OK, - NULL); - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE); - gtk_window_set_default_size(GTK_WINDOW(dlg), w, h); - return dlg; - } - - static void create_panes(GtkWidget* container, GtkWidget*& left_vbox, GtkWidget*& right_vbox) - { - left_vbox = gtk_vbox_new(FALSE, 5); - right_vbox = gtk_vbox_new(FALSE, 15); - -#if 0 - GtkWidget* paned = gtk_hpaned_new(); - gtk_container_add(GTK_CONTAINER(container), paned); - gtk_paned_add1(GTK_PANED(paned), left_vbox); - - GtkWidget* sc_win = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(sc_win), right_vbox); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sc_win), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_paned_add2(GTK_PANED(paned), sc_win); -#else - GtkWidget* hbox = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(container), hbox); - gtk_box_pack_start(GTK_BOX(hbox), left_vbox, TRUE, TRUE, 5); - gtk_box_pack_start(GTK_BOX(hbox), right_vbox, TRUE, TRUE, 5); -#endif -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_widget_set_vexpand(left_vbox, TRUE); - gtk_widget_set_valign(right_vbox, GTK_ALIGN_START); -#endif - } - - static GtkWidget* make_mappings_treeview(int port, ConfigData& cfg, GtkWidget* container) - { - GtkWidget* button; - auto treeview = gtk_tree_view_new(); - cfg.treeview = GTK_TREE_VIEW(treeview); - auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); - - GtkCellRenderer* render = gtk_cell_renderer_text_new(); - - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), - -1, "Name", render, "text", COL_NAME, NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), - -1, "PS2", render, "text", COL_PS2, NULL); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), - -1, "PC", render, "text", COL_PC, NULL); - - gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(treeview), 0); - - gtk_tree_view_column_set_resizable(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0), TRUE); - gtk_tree_view_column_set_resizable(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1), TRUE); - gtk_tree_view_column_set_resizable(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 2), TRUE); - - gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(cfg.store)); - g_object_unref(GTK_TREE_MODEL(cfg.store)); //treeview has its own ref - - GtkWidget* scwin = gtk_scrolled_window_new(NULL, NULL); - gtk_container_add(GTK_CONTAINER(scwin), treeview); - gtk_widget_set_size_request(GTK_WIDGET(scwin), 200, 100); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_box_pack_start(GTK_BOX(container), scwin, TRUE, TRUE, 5); - - button = gtk_button_new_with_label("Clear binding"); - gtk_box_pack_start(GTK_BOX(container), button, FALSE, FALSE, 5); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(clear_binding_clicked), reinterpret_cast(port)); - - button = gtk_button_new_with_label("Clear All"); - gtk_box_pack_start(GTK_BOX(container), button, FALSE, FALSE, 5); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(clear_all_clicked), reinterpret_cast(port)); - return treeview; - } - - static void load_devices_mappings(ConfigData& cfg, const int port, ApiCallbacks& apicbs) - { - int fd; - apicbs.populate(cfg.joysticks); - - cfg.js_iter = cfg.joysticks.end(); - cfg.label = gtk_label_new(""); - cfg.store = gtk_list_store_new(NUM_COLS, - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); - cfg.cb = &apicbs; - - for (const auto& it : cfg.joysticks) - { - if ((fd = open(it.path.c_str(), O_RDONLY | O_NONBLOCK)) < 0) - { - Console.Warning("USB: failed to open '%s'", it.path.c_str()); - continue; - } - - ConfigMapping c(fd); - LoadMappings(cfg.dev_type, port, it.id, cfg.max_buttons, cfg.max_axes, c); - cfg.jsconf.push_back(std::make_pair(it.id, c)); - } - - refresh_store(&cfg); - } - - int GtkPadConfigure(int port, const char* dev_type, const char* apititle, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs) - { - GtkWidget *right_vbox, *left_vbox; - - ConfigData cfg{}; - cfg.dev_type = dev_type; - cfg.max_axes = 3; - cfg.max_buttons = JOY_STEERING; // 16 - load_devices_mappings(cfg, port, apicbs); - - std::string path; - LoadSetting(dev_type, port, apiname, N_JOYSTICK, path); - - cfg.use_hidraw_ff_pt = false; - bool is_evdev = (strncmp(apiname, "evdev", 5) == 0); - if (is_evdev) - { - LoadSetting(dev_type, port, apiname, N_HIDRAW_FF_PT, cfg.use_hidraw_ff_pt); - } - - // --------------------------- - const std::string title = std::string(port ? "Player One " : "Player Two ") + apititle; - GtkWidget* dlg = make_dialog(parent, title); - - // --------------------------- - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - create_panes(dlg_area_box, left_vbox, right_vbox); - make_mappings_treeview(port, cfg, left_vbox); - - // --------------------------- - - // Remapping - { - GtkWidget* table = gtk_table_new(5, 7, TRUE); - gtk_container_add(GTK_CONTAINER(right_vbox), table); - //GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default - GtkAttachOptions opt = (GtkAttachOptions)(GTK_FILL); - - const char* button_labels[] = { - "L2", - "L1 / L", - "R2", - "R1 / R / Orange", - "Left", - "Up", - "Right", - "Down", - "Square / X / Green", - "Cross / A / Blue", - "Circle / B / Red", - "Triangle / Y / Yellow", - "Select", - "Start", - }; - - const Point button_pos[] = { - {1, 0, JOY_L2}, - {1, 1, JOY_L1}, - {5, 0, JOY_R2}, - {5, 1, JOY_R1}, - {0, 3, JOY_LEFT}, - {1, 2, JOY_UP}, - {2, 3, JOY_RIGHT}, - {1, 4, JOY_DOWN}, - {4, 3, JOY_SQUARE}, - {5, 4, JOY_CROSS}, - {6, 3, JOY_CIRCLE}, - {5, 2, JOY_TRIANGLE}, - {3, 3, JOY_SELECT}, - {3, 2, JOY_START}, - }; - - for (int i = 0; i < (int)countof(button_labels); i++) - { - GtkWidget* button = gtk_button_new_with_label(button_labels[i]); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(button_pos[i].type)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - - gtk_table_attach(GTK_TABLE(table), button, - 0 + button_pos[i].x, 1 + button_pos[i].x, - 0 + button_pos[i].y, 1 + button_pos[i].y, - opt, opt, 5, 1); - } - - GtkWidget* hbox = gtk_hbox_new(false, 5); - gtk_container_add(GTK_CONTAINER(right_vbox), hbox); - - GtkWidget* button = gtk_button_new_with_label("Steering"); - gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(JOY_STEERING)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - - button = gtk_button_new_with_label("Throttle"); - gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(JOY_THROTTLE)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - - button = gtk_button_new_with_label("Brake"); - gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(JOY_BRAKE)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - - gtk_box_pack_start(GTK_BOX(right_vbox), cfg.label, TRUE, TRUE, 5); - } - GtkWidget* ro_frame = gtk_frame_new("Force feedback"); - gtk_box_pack_start(GTK_BOX(right_vbox), ro_frame, TRUE, FALSE, 5); - - //GtkWidget *frame_vbox = gtk_vbox_new (FALSE, 5); - //gtk_container_add (GTK_CONTAINER (ro_frame), frame_vbox); - - const char* labels_buff[][2] = {{"Set gain", "Gain"}, {"Managed by game", "Autocenter strength"}}; - const char* ff_var_name[][2] = {{N_GAIN_ENABLED, N_GAIN}, {N_AUTOCENTER_MANAGED, N_AUTOCENTER}}; - GtkWidget* ff_scales[2]; - int32_t ff_enabled[2]; - - GtkWidget* table = gtk_table_new(3, 2, TRUE); - gtk_container_add(GTK_CONTAINER(ro_frame), table); - gtk_table_set_homogeneous(GTK_TABLE(table), FALSE); - GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default - - for (int i = 0; i < 2; i++) - { - if (LoadSetting(dev_type, port, apiname, ff_var_name[i][0], ff_enabled[i])) - ff_enabled[i] = !!ff_enabled[i]; - else - ff_enabled[i] = 1; - - GtkWidget* chk_btn = gtk_check_button_new_with_label(labels_buff[i][0]); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_btn), (gboolean)ff_enabled[i]); - g_signal_connect(G_OBJECT(chk_btn), "toggled", G_CALLBACK(checkbox_toggled), reinterpret_cast(&ff_enabled[i])); - gtk_table_attach(GTK_TABLE(table), chk_btn, - 2, 3, - 0 + i, 1 + i, - GTK_FILL, GTK_SHRINK, 5, 1); - - GtkWidget* label = gtk_label_new(labels_buff[i][1]); - gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f); - gtk_table_attach(GTK_TABLE(table), label, - 0, 1, - 0 + i, 1 + i, - GTK_FILL, GTK_SHRINK, 5, 1); - - //ff_scales[i] = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 100, 1); - ff_scales[i] = gtk_hscale_new_with_range(0, 100, 1); - for (int v = 0; v <= 100; v += 10) - gtk_scale_add_mark(GTK_SCALE(ff_scales[i]), v, GTK_POS_BOTTOM, nullptr); - gtk_table_attach(GTK_TABLE(table), ff_scales[i], - 1, 2, - 0 + i, 1 + i, - opt, opt, 5, 1); - - int32_t var; - if (LoadSetting(dev_type, port, apiname, ff_var_name[i][1], var)) - { - var = std::min(100, std::max(0, var)); - gtk_range_set_value(GTK_RANGE(ff_scales[i]), var); - } - else - gtk_range_set_value(GTK_RANGE(ff_scales[i]), 100); - } - - if (is_evdev) - { - ro_frame = gtk_frame_new("Logitech wheel force feedback pass-through using hidraw"); - gtk_box_pack_start(GTK_BOX(right_vbox), ro_frame, FALSE, FALSE, 5); - - GtkWidget* frame_vbox = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(ro_frame), frame_vbox); - - GtkWidget* chk_btn = gtk_check_button_new_with_label("Enable"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_btn), (gboolean)cfg.use_hidraw_ff_pt); - //g_object_set_data(G_OBJECT(chk_btn), CFG, &cfg); - g_signal_connect(G_OBJECT(chk_btn), "toggled", G_CALLBACK(checkbox_toggled), reinterpret_cast(&cfg.use_hidraw_ff_pt)); - gtk_box_pack_start(GTK_BOX(frame_vbox), chk_btn, FALSE, FALSE, 5); - - GtkWidget* rs_cb = new_combobox("Device:", frame_vbox, true); - - const std::vector whitelist{PAD_LG_FFB_WHITELIST}; - int idx = 0, sel_idx = 0; - for (auto& it : cfg.joysticks) - { - if (!(it.input_id.vendor == PAD_VID && std::find(whitelist.begin(), whitelist.end(), it.input_id.product) != whitelist.end())) - continue; - - std::stringstream str; - str << it.name; - if (!it.id.empty()) - str << " [" << it.id << "]"; - - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), str.str().c_str()); - if (path == it.path) - sel_idx = idx; - idx++; - } - - g_object_set_data(G_OBJECT(rs_cb), CFG, &cfg); - g_signal_connect(G_OBJECT(rs_cb), "changed", G_CALLBACK(joystick_changed), reinterpret_cast(port)); - gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), sel_idx); - } - // --------------------------- - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - - int ret = RESULT_OK; - if (result == GTK_RESPONSE_OK) - { - if (cfg.js_iter != cfg.joysticks.end()) - { - if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->path)) - ret = RESULT_FAILED; - } - - for (auto& it : cfg.jsconf) - SaveMappings(dev_type, port, it.first, cfg.max_buttons, cfg.max_axes, it.second); - - if (is_evdev) - { - SaveSetting(dev_type, port, apiname, N_HIDRAW_FF_PT, cfg.use_hidraw_ff_pt); - } - for (int i = 0; i < 2; i++) - { - SaveSetting(dev_type, port, apiname, ff_var_name[i][0], ff_enabled[i]); - int val = gtk_range_get_value(GTK_RANGE(ff_scales[i])); - SaveSetting(dev_type, port, apiname, ff_var_name[i][1], val); - } - } - else - ret = RESULT_CANCELED; - - for (auto& it : cfg.jsconf) - close(it.second.fd); - - gtk_widget_destroy(dlg); - return ret; - } - - GtkWidget* make_color_icon(uint32_t rgb) - { - GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 24, 24); - guchar* data = gdk_pixbuf_get_pixels(pixbuf); - - for (size_t i = 0; i < 24 * 24; i++) - { - data[i * 4 + 0] = rgb & 0xFF; - data[i * 4 + 1] = (rgb >> 8) & 0xFF; - data[i * 4 + 2] = (rgb >> 16) & 0xFF; - data[i * 4 + 3] = icon_buzz_24[i]; - } - - GtkWidget* w = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - return w; - } - - int GtkBuzzConfigure(int port, const char* dev_type, const char* apititle, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs) - { - GtkWidget *main_hbox, *right_vbox, *left_vbox; - - int fd; - ConfigData cfg{}; - - apicbs.populate(cfg.joysticks); - - cfg.js_iter = cfg.joysticks.end(); - cfg.label = gtk_label_new(""); - cfg.store = gtk_list_store_new(NUM_COLS, - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); - cfg.cb = &apicbs; - cfg.dev_type = dev_type; - cfg.max_axes = 0; - cfg.max_buttons = 20; - - for (const auto& it : cfg.joysticks) - { - if ((fd = open(it.path.c_str(), O_RDONLY | O_NONBLOCK)) < 0) - { - continue; - } - - ConfigMapping c; - c.fd = fd; - LoadBuzzMappings(cfg.dev_type, port, it.id, c); - cfg.jsconf.push_back(std::make_pair(it.id, c)); - } - - refresh_store(&cfg); - - // --------------------------- - const std::string title = std::string("Buzz ") + apititle; - GtkWidget* dlg = make_dialog(parent, title); - - // --------------------------- - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - - main_hbox = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(dlg_area_box), main_hbox); - - left_vbox = gtk_vbox_new(FALSE, 5); - right_vbox = gtk_vbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(main_hbox), left_vbox, TRUE, TRUE, 5); - gtk_box_pack_start(GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 5); - - make_mappings_treeview(port, cfg, left_vbox); - -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_widget_set_vexpand(left_vbox, TRUE); -#endif - - // --------------------------- - - // Remapping - { - GtkWidget* table = gtk_table_new(5, 4, TRUE); - gtk_container_add(GTK_CONTAINER(right_vbox), table); - GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default - - static const char* button_labels[]{ - "Red", - "Blue", - "Orange", - "Green", - "Yellow", - }; - - static const Buzz buzz_btns[]{ - BUZZ_RED, - BUZZ_BLUE, - BUZZ_ORANGE, - BUZZ_GREEN, - BUZZ_YELLOW, - }; - - static const uint32_t icon_colors[]{ - 0x0000FF, - 0xFF0000, - 0x0080FF, - 0x00FF00, - 0x00FFFF, - }; - - for (int j = 0; j < 4; j++) - { - for (int i = 0; i < (int)countof(button_labels); i++) - { - GtkWidget* button = gtk_button_new_with_label(button_labels[i]); - - GtkWidget* icon = make_color_icon(icon_colors[i]); - gtk_button_set_image(GTK_BUTTON(button), icon); - gtk_button_set_image_position(GTK_BUTTON(button), GTK_POS_LEFT); - - GList* children = gtk_container_get_children(GTK_CONTAINER(button)); - - //Gtk 3.16+ - //gtk_label_set_xalign (GTK_WIDGET(children), 0.0) - - //gtk_misc_set_alignment (GTK_MISC (children->data), 0.0, 0.5); - if (GTK_IS_ALIGNMENT(children->data)) - gtk_alignment_set(GTK_ALIGNMENT(children->data), 0.0f, 0.5f, 0.2f, 0.f); - - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(j * countof(buzz_btns) + buzz_btns[i])); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - - gtk_table_attach(GTK_TABLE(table), button, - j, 1 + j, - i + 1, 2 + i, - opt, opt, 5, 1); - } - - gtk_table_attach(GTK_TABLE(table), gtk_label_new("Player 1"), - 0, 1, 0, 1, opt, opt, 5, 1); - gtk_table_attach(GTK_TABLE(table), gtk_label_new("Player 2"), - 1, 2, 0, 1, opt, opt, 5, 1); - gtk_table_attach(GTK_TABLE(table), gtk_label_new("Player 3"), - 2, 3, 0, 1, opt, opt, 5, 1); - gtk_table_attach(GTK_TABLE(table), gtk_label_new("Player 4"), - 3, 4, 0, 1, opt, opt, 5, 1); - } - - GtkWidget* hbox = gtk_hbox_new(false, 5); - gtk_container_add(GTK_CONTAINER(right_vbox), hbox); - - gtk_box_pack_start(GTK_BOX(right_vbox), cfg.label, TRUE, TRUE, 5); - } - - // --------------------------- - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - - int ret = RESULT_OK; - if (result == GTK_RESPONSE_OK) - { - if (cfg.js_iter != cfg.joysticks.end()) - { - if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->path)) - ret = RESULT_FAILED; - } - - for (auto& it : cfg.jsconf) - SaveBuzzMappings(dev_type, port, it.first, it.second); - } - else - ret = RESULT_CANCELED; - - for (auto& it : cfg.jsconf) - close(it.second.fd); - - gtk_widget_destroy(dlg); - return ret; - } - - int GtkKeyboardmaniaConfigure(int port, const char* dev_type, const char* apititle, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs) - { - GtkWidget *right_vbox, *left_vbox; - - ConfigData cfg{}; - cfg.dev_type = dev_type; - cfg.max_buttons = 31; - load_devices_mappings(cfg, port, apicbs); - - // --------------------------- - const std::string title = std::string("Keyboardmania ") + apititle; - GtkWidget* dlg = make_dialog(parent, title); - - // --------------------------- - GtkWidget* dlg_area_box = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); - create_panes(dlg_area_box, left_vbox, right_vbox); - make_mappings_treeview(port, cfg, left_vbox); - - // --------------------------- - // Remapping - { - GtkWidget* table = gtk_table_new(5, 14, TRUE); - gtk_container_add(GTK_CONTAINER(right_vbox), table); - GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_widget_set_halign(table, GTK_ALIGN_START); -#endif - - struct keys - { - u32 index; - bool sharp; - }; - - constexpr keys keys[]{ - {0, false}, - {1, true}, - {2, false}, - {3, true}, - {4, false}, - {5, false}, - {6, true}, - //{"padding", 7}, - {8, false}, - {9, true}, - {10, false}, - {11, true}, - {12, false}, - {13, false}, - //{"Select", 14}, - //{"padding", 15}, - {16, true}, - {17, false}, - {18, true}, - {19, false}, - {20, false}, - {21, true}, - //{"Start", 22}, - //{"padding", 23}, - {24, false}, - {25, true}, - {26, false}, - {27, true}, - {28, false}, - //{"Up", 29}, - //{"Down", 30}, - }; - - int attached = 0; - int voffset = 0; - for (auto& key : keys) - { - GtkWidget* button = gtk_button_new_with_label(kbdmania_key_labels[key.index]); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(key.index)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - - // split into 2-by-2 rows - if (attached > 6) - { - voffset = 2; - attached = 0; - } - - if (!key.sharp) - { - gtk_table_attach(GTK_TABLE(table), button, - attached * 2, 2 + attached * 2, - 1 + voffset, 2 + voffset, - opt, opt, 5, 1); - attached++; - } - else - gtk_table_attach(GTK_TABLE(table), button, - attached * 2 - 1, attached * 2 + 2 - 1, - 0 + voffset, 1 + voffset, - opt, opt, 5, 1); - } - - GtkWidget *button, *frame_box, *frame; - GtkWidget* frame_container = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(right_vbox), frame_container); -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_widget_set_valign(frame_container, GTK_ALIGN_START); -#endif - - frame = gtk_frame_new("Buttons"); - { - frame_box = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(frame), frame_box); - gtk_box_pack_start(GTK_BOX(frame_container), frame, FALSE, FALSE, 5); - - button = gtk_button_new_with_label("Start"); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(22)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - gtk_box_pack_start(GTK_BOX(frame_box), button, FALSE, FALSE, 5); - - button = gtk_button_new_with_label("Select"); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(14)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - gtk_box_pack_start(GTK_BOX(frame_box), button, FALSE, FALSE, 5); - } - - frame = gtk_frame_new("Wheel"); - { - frame_box = gtk_hbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(frame), frame_box); - gtk_box_pack_start(GTK_BOX(frame_container), frame, FALSE, FALSE, 5); - - button = gtk_button_new_with_label("Up"); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(29)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - gtk_box_pack_start(GTK_BOX(frame_box), button, FALSE, FALSE, 5); - - button = gtk_button_new_with_label("Down"); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast(30)); - g_object_set_data(G_OBJECT(button), CFG, &cfg); - gtk_box_pack_start(GTK_BOX(frame_box), button, FALSE, FALSE, 5); - } - } - - gtk_box_pack_start(GTK_BOX(right_vbox), cfg.label, TRUE, TRUE, 5); - - // --------------------------- - gtk_widget_show_all(dlg); - gint result = gtk_dialog_run(GTK_DIALOG(dlg)); - - int ret = RESULT_OK; - if (result == GTK_RESPONSE_OK) - { - if (cfg.js_iter != cfg.joysticks.end()) - { - if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->path)) - ret = RESULT_FAILED; - } - - for (auto& it : cfg.jsconf) - SaveMappings(dev_type, port, it.first, cfg.max_buttons, 0, it.second); - } - else - ret = RESULT_CANCELED; - - for (auto& it : cfg.jsconf) - close(it.second.fd); - - gtk_widget_destroy(dlg); - return ret; - } - - } // namespace evdev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/evdev/shared.h b/pcsx2/USB/usb-pad/evdev/shared.h deleted file mode 100644 index 3f0fe1ba65..0000000000 --- a/pcsx2/USB/usb-pad/evdev/shared.h +++ /dev/null @@ -1,214 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -#include -#include -#include "common/Pcsx2Types.h" -#include "USB/gtk.h" -#include "USB/usb-pad/padproxy.h" -#include "USB/configuration.h" - -#define N_HIDRAW_FF_PT "hidraw_ff_pt" -#define N_GAIN_ENABLED "gain_enabled" -#define N_GAIN "gain" -#define N_AUTOCENTER "autocenter" -#define N_AUTOCENTER_MANAGED "ac_managed" - -struct evdev_device -{ - std::string name; - std::string id; - std::string path; - struct { - uint16_t bustype; - uint16_t vendor; - uint16_t product; - uint16_t version; - } input_id; -}; - -typedef std::vector device_list; - -namespace usb_pad -{ - namespace evdev - { - - enum - { - COL_NAME = 0, - COL_PS2, - COL_PC, - COL_BINDING, - NUM_COLS - }; - - // Keep in sync with PS2Buttons enum - enum JoystickMap - { - JOY_CROSS = 0, - JOY_SQUARE, - JOY_CIRCLE, - JOY_TRIANGLE, - JOY_R1, - JOY_L1, - JOY_R2, - JOY_L2, - JOY_SELECT, - JOY_START, - JOY_R3, - JOY_L3, //order, afaik not used on any PS2 wheel anyway - JOY_DOWN, - JOY_LEFT, - JOY_UP, - JOY_RIGHT, - JOY_STEERING, - JOY_THROTTLE, - JOY_BRAKE, - JOY_MAPS_COUNT - }; - - constexpr const char* JoystickMapNames[]{ - "cross", - "square", - "circle", - "triangle", - "r1", - "l1", - "r2", - "l2", - "select", - "start", - "r3", - "l3", - "down", - "left", - "up", - "right", - "steering", - "throttle", - "brake", - }; - - constexpr const char* buzz_map_names[]{ - "red", - "yellow", - "green", - "orange", - "blue", - }; - - constexpr const char* kbdmania_key_labels[]{ - "C 1", - "C# 1", - "D 1", - "D# 1", - "E 1", - "F 1", - "F# 1", - "", - "G 1", - "G# 1", - "A 1", - "A# 1", - "B 1", - "C 2", - "Select", - "", - "C# 2", - "D 2", - "D# 2", - "E 2", - "F 2", - "F# 2", - "Start", - "", - "G 2", - "G# 2", - "A 2", - "A# 2", - "B 2", - "Up", - "Down", - }; - - struct Point - { - int x; - int y; - JoystickMap type; - }; - - struct ConfigMapping - { - std::vector controls; - int inverted[3]; - int initial[3]; - int fd = -1; - - ConfigMapping() = default; - ConfigMapping(int fd_) - : fd(fd_) - { - } - }; - - struct ApiCallbacks - { - bool (*get_event_name)(const char* dev_type, int map, int event, bool is_button, const char** name); - void (*populate)(device_list& jsdata); - bool (*poll)(const std::vector>& jsconf, std::string& dev_name, bool isaxis, int& value, bool& inverted, int& initial); - }; - - typedef std::pair MappingPair; - struct ConfigData - { - std::vector jsconf; - device_list joysticks; - device_list::const_iterator js_iter; - GtkWidget* label; - GtkListStore* store; - GtkTreeView* treeview; - ApiCallbacks* cb; - int use_hidraw_ff_pt; - const char* dev_type; - u32 max_axes, max_buttons; - }; - - struct axis_correct - { - int used; - int coef[3]; - }; - - struct device_data - { - ConfigMapping cfg; - std::string name; - uint8_t axis_map[ABS_MAX + 1]; - uint16_t btn_map[KEY_MAX + 1]; - struct axis_correct abs_correct[ABS_MAX]; - }; - - int GtkPadConfigure(int port, const char* dev_type, const char* title, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs); - int GtkBuzzConfigure(int port, const char* dev_type, const char* title, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs); - int GtkKeyboardmaniaConfigure(int port, const char* dev_type, const char* apititle, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs); - bool LoadMappings(const char* dev_type, int port, const std::string& joyname, u32 max_buttons, u32 max_axes, ConfigMapping& cfg); - bool SaveMappings(const char* dev_type, int port, const std::string& joyname, u32 max_buttons, u32 max_axes, const ConfigMapping& cfg); - bool LoadBuzzMappings(const char* dev_type, int port, const std::string& joyname, ConfigMapping& cfg); - bool SaveBuzzMappings(const char* dev_type, int port, const std::string& joyname, const ConfigMapping& cfg); - } // namespace evdev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/padproxy.h b/pcsx2/USB/usb-pad/padproxy.h deleted file mode 100644 index 8e825e32d4..0000000000 --- a/pcsx2/USB/usb-pad/padproxy.h +++ /dev/null @@ -1,88 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef PADPROXY_H -#define PADPROXY_H -#include -#include -#include -#include -#include -#include -#include "usb-pad.h" -#include "USB/helpers.h" -#include "USB/deviceproxy.h" - -namespace usb_pad -{ - - class PadError : public std::runtime_error - { - public: - PadError(const char* msg) - : std::runtime_error(msg) - { - } - virtual ~PadError() {} - }; - - class PadProxyBase : public ProxyBase - { - PadProxyBase(const PadProxyBase&) = delete; - - public: - PadProxyBase() {} - PadProxyBase(const std::string& name); - virtual Pad* CreateObject(int port, const char* dev_type) const = 0; - }; - - template - class PadProxy : public PadProxyBase - { - PadProxy(const PadProxy&) = delete; - - public: - PadProxy() { } - ~PadProxy() { } - Pad* CreateObject(int port, const char* dev_type) const - { - try - { - return new T(port, dev_type); - } - catch (PadError& err) - { - (void)err; - return nullptr; - } - } - virtual const TCHAR* Name() const - { - return T::Name(); - } - virtual int Configure(int port, const char* dev_type, void* data) - { - return T::Configure(port, dev_type, data); - } - }; - - class RegisterPad : public RegisterProxy - { - public: - static void Register(); - }; - -} // namespace usb_pad -#endif diff --git a/pcsx2/USB/usb-pad/raw/raw-config-res.h b/pcsx2/USB/usb-pad/raw/raw-config-res.h deleted file mode 100644 index 608477077c..0000000000 --- a/pcsx2/USB/usb-pad/raw/raw-config-res.h +++ /dev/null @@ -1,51 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by raw-config.rc -// -#define IDD_RAWCONFIG 201 -#define IDC_STATIC -1 -#define IDC_LOGGING 1007 -#define IDC_COMBO1 1008 -#define IDC_COMBO2 1009 -#define IDC_LIST1 1010 -#define IDC_COMBO_FFB 1011 -#define IDC_BUTTON1 1012 -#define IDC_BUTTON2 1013 -#define IDC_BUTTON3 1014 -#define IDC_BUTTON4 1015 -#define IDC_BUTTON5 1016 -#define IDC_BUTTON6 1017 -#define IDC_BUTTON7 1018 -#define IDC_BUTTON8 1019 -#define IDC_BUTTON9 1020 -#define IDC_BUTTON10 1021 -#define IDC_BUTTON11 1022 -#define IDC_BUTTON12 1023 -#define IDC_BUTTON13 1024 -#define IDC_BUTTON14 1025 -#define IDC_BUTTON15 1026 -#define IDC_BUTTON16 1027 -#define IDC_BUTTON17 1028 -#define IDC_BUTTON18 1029 -#define IDC_BUTTON19 1030 -#define IDC_BUTTON20 1031 -#define IDC_BUTTON21 1032 -#define IDC_DFP_PASS 1033 -#define IDC_UNBIND 1034 -#define IDC_STATIC_CAP 1035 -#define IDC_COMBO3 1036 -#define IDC_BUTTON22 1037 -#define IDC_BUILD_DATE 1038 -#define IDC_COMBO_WHEEL_TYPE 1039 -#define IDC_TAB1 1040 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 110 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1039 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/pcsx2/USB/usb-pad/raw/raw-config.cpp b/pcsx2/USB/usb-pad/raw/raw-config.cpp deleted file mode 100644 index b5c3be7426..0000000000 --- a/pcsx2/USB/usb-pad/raw/raw-config.cpp +++ /dev/null @@ -1,925 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" - -#if !defined(_WIN32_WINNT) -#define _WIN32_WINNT 0x0502 -#endif -#include -#include -#include -#include -#include - -#include -#include -#include "USB/configuration.h" -#include "usb-pad-raw.h" -#include "raw-config-res.h" -#include - -extern HINSTANCE hInst; -#define MSG_PRESS_ESC(wnd) SendDlgItemMessageW(wnd, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)L"Capturing, press ESC to cancel") - -namespace usb_pad -{ - namespace raw - { - - inline bool MapExists(const MapVector& maps, const std::wstring& hid) - { - for (auto& it : maps) - if (!it.hidPath.compare(hid)) - return true; - return false; - } - - void LoadMappings(const char* dev_type, MapVector& maps) - { - maps.clear(); - - WCHAR bind[32] = {0}; - for (int j = 0; j < 25; j++) - { - std::wstring hid, tmp; - //swprintf_s(sec, TEXT("%S RAW DEVICE %d"), dev_type, j++); - - if (LoadSetting(dev_type, j, "RAW DEVICE", TEXT("HID"), hid) && !hid.empty() && !MapExists(maps, hid)) - { - Mappings m; - ZeroMemory(&m, sizeof(Mappings)); - maps.push_back(m); - Mappings& ptr = maps.back(); - - ptr.hidPath = hid; - ptr.devName = hid; - //pad_reset_data(&ptr.data[0]); - //pad_reset_data(&ptr.data[1]); - memset(&ptr.data[0], 0xFF, sizeof(wheel_data_t)); - memset(&ptr.data[1], 0xFF, sizeof(wheel_data_t)); - ptr.data[0].buttons = 0; - ptr.data[1].buttons = 0; - ptr.data[0].hatswitch = 0x8; //memset to 0xFF already or set to -1 - ptr.data[1].hatswitch = 0x8; - - for (int i = 0; i < MAX_BUTTONS; i++) - { - swprintf_s(bind, L"Button %d", i); - if (LoadSetting(dev_type, j, "RAW DEVICE", bind, tmp)) - swscanf_s(tmp.c_str(), L"%08X", &(ptr.btnMap[i])); - } - - for (int i = 0; i < MAX_AXES; i++) - { - swprintf_s(bind, L"Axis %d", i); - if (LoadSetting(dev_type, j, "RAW DEVICE", bind, tmp)) - swscanf_s(tmp.c_str(), L"%08X", &(ptr.axisMap[i])); - } - - for (int i = 0; i < 4 /*PAD_HAT_COUNT*/; i++) - { - swprintf_s(bind, L"Hat %d", i); - if (LoadSetting(dev_type, j, "RAW DEVICE", bind, tmp)) - swscanf_s(tmp.c_str(), L"%08X", &(ptr.hatMap[i])); - } - } - } - return; - } - - void SaveMappings(const char* dev_type, MapVector& maps) - { - uint32_t numDevice = 0; - for (auto& it : maps) - { - WCHAR dev[1024] = {0}, tmp[16] = {0}, bind[32] = {0}; - - SaveSetting(dev_type, numDevice, "RAW DEVICE", TEXT("HID"), it.hidPath); - - //writing everything separately, then string lengths are more predictable - for (int i = 0; i < MAX_BUTTONS; i++) - { - swprintf_s(bind, L"Button %d", i); - swprintf_s(tmp, L"%08X", it.btnMap[i]); - SaveSetting(dev_type, numDevice, "RAW DEVICE", bind, tmp); - } - - for (int i = 0; i < MAX_AXES; i++) - { - swprintf_s(bind, L"Axis %d", i); - swprintf_s(tmp, L"%08X", it.axisMap[i]); - SaveSetting(dev_type, numDevice, "RAW DEVICE", bind, tmp); - } - - for (int i = 0; i < 4 /*PAD_HAT_COUNT*/; i++) - { - swprintf_s(bind, L"Hat %d", i); - swprintf_s(tmp, L"%08X", it.hatMap[i]); - SaveSetting(dev_type, numDevice, "RAW DEVICE", bind, tmp); - } - numDevice++; - } - - return; - } - -/// Dialogs -#define TXT(x) (#x) - const char* BTN2TXT[] = { - "Cross", - "Square", - "Circle", - "Triangle", - "R1", - "L1", - "R2", - "L2", - "Select", - "Start", - "R3", - "L3"}; - - const char* AXIS2TXT[] = { - "Axis X", - "Axis Y", - "Axis Z", - //"Axis RX", - //"Axis RY", - "Axis RZ", - "Hat Switch"}; - - void resetState(HWND hW); - HWND dgHwnd = NULL; - //std::vector joysName; - static std::vector joysDev; - static DWORD selectedJoy[2]; - //std::vector::iterator* tmpIter; - - typedef struct _DevInfo - { - int ply; - RID_DEVICE_INFO_HID hid; - - bool operator==(const _DevInfo& t) const - { - if (ply == t.ply && hid.dwProductId == t.hid.dwProductId && - hid.dwVendorId == t.hid.dwVendorId && - hid.dwVersionNumber == t.hid.dwVersionNumber && - hid.usUsage == t.hid.usUsage && - hid.usUsagePage == t.hid.usUsagePage) - return true; - return false; - } - - bool operator<(const _DevInfo& t) const - { - if (ply < t.ply) - return true; - if (hid.dwProductId < t.hid.dwProductId) - return true; - if (hid.dwVendorId < t.hid.dwVendorId) - return true; - if (hid.dwVersionNumber < t.hid.dwVersionNumber) - return true; - return false; - } - - } DevInfo_t; - - //typedef std::map MappingsMap; - //MappingsMap mappings; - - uint32_t axisDiff[MAX_AXES]; //previous axes values - bool axisPass2 = false; - - //eh, global var for currently selected player - static int plyCapturing = 0; - PS2Buttons btnCapturing = PAD_BUTTON_COUNT; - PS2Axis axisCapturing = PAD_AXIS_COUNT; - PS2HatSwitch hatCapturing = PAD_HAT_COUNT; - - void populate(HWND hW, RawDlgConfig* cfg) - { - //mappings.clear(); - //joysName.clear(); - //joysName.push_back("None"); - joysDev.clear(); - joysDev.push_back(L""); - - int i = 0, sel_idx = 1; - HANDLE usbHandle = INVALID_HANDLE_VALUE; - DWORD needed = 0; - HDEVINFO devInfo; - GUID guid; - SP_DEVICE_INTERFACE_DATA diData; - PSP_DEVICE_INTERFACE_DETAIL_DATA didData = NULL; - HIDD_ATTRIBUTES attr; - PHIDP_PREPARSED_DATA pPreparsedData = NULL; - HIDP_CAPS caps; - OVERLAPPED ovl; - - memset(&ovl, 0, sizeof(OVERLAPPED)); - ovl.hEvent = CreateEvent(0, 0, 0, 0); - ovl.Offset = 0; - ovl.OffsetHigh = 0; - - HidD_GetHidGuid(&guid); - - devInfo = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_DEVICEINTERFACE); - if (!devInfo) - return; - - diData.cbSize = sizeof(diData); - - //Mappings listview - LVCOLUMN LvCol; - memset(&LvCol, 0, sizeof(LvCol)); - LvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - LvCol.pszText = TEXT("Device"); - LvCol.cx = 0x4F; - ListView_InsertColumn(GetDlgItem(hW, IDC_LIST1), 0, &LvCol); - - LvCol.pszText = TEXT("PC"); - ListView_InsertColumn(GetDlgItem(hW, IDC_LIST1), 1, &LvCol); - - LvCol.pszText = TEXT("PS2"); - ListView_InsertColumn(GetDlgItem(hW, IDC_LIST1), 2, &LvCol); - - //Tab control - TCITEM tie; - tie.pszText = TEXT("Player 1"); - tie.cchTextMax = 32; - tie.mask = TCIF_TEXT; - SendDlgItemMessage(hW, IDC_TAB1, TCM_INSERTITEM, 0, (LPARAM)&tie); - - tie.pszText = TEXT("Player 2"); - SendDlgItemMessage(hW, IDC_TAB1, TCM_INSERTITEM, 1, (LPARAM)&tie); - - //Selected FFB target device - SendDlgItemMessageA(hW, IDC_COMBO_FFB, CB_ADDSTRING, 0, (LPARAM) "None"); - SendDlgItemMessage(hW, IDC_COMBO_FFB, CB_SETCURSEL, 0, 0); - - while (SetupDiEnumDeviceInterfaces(devInfo, 0, &guid, i, &diData)) - { - if (usbHandle != INVALID_HANDLE_VALUE) - CloseHandle(usbHandle); - - SetupDiGetDeviceInterfaceDetail(devInfo, &diData, 0, 0, &needed, 0); - - didData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(needed); - if (!didData) - break; - didData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - if (!SetupDiGetDeviceInterfaceDetail(devInfo, &diData, didData, needed, 0, 0)) - { - free(didData); - break; - } - - usbHandle = CreateFile(didData->DevicePath, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - - if (usbHandle == INVALID_HANDLE_VALUE) - { - Console.Warning("Could not open device %i", i); - free(didData); - i++; - continue; - } - - HidD_GetAttributes(usbHandle, &attr); - if (!HidD_GetPreparsedData(usbHandle, &pPreparsedData)) - { - Console.Warning("Could not get preparsed data from %04x:%04x", attr.VendorID, attr.ProductID); - free(didData); - i++; - continue; - } - - HidP_GetCaps(pPreparsedData, &caps); - - if (caps.UsagePage == HID_USAGE_PAGE_GENERIC && - (caps.Usage == HID_USAGE_GENERIC_JOYSTICK || caps.Usage == HID_USAGE_GENERIC_GAMEPAD)) - { - std::wstring strPath(didData->DevicePath); - std::transform(strPath.begin(), strPath.end(), strPath.begin(), ::toupper); - joysDev.push_back(strPath); - - wchar_t str[MAX_PATH + 1]; - if (HidD_GetProductString(usbHandle, str, sizeof(str))) - SendDlgItemMessageW(hW, IDC_COMBO_FFB, CB_ADDSTRING, 0, (LPARAM)str); - else - { - swprintf_s(str, L"%04X:%04X", attr.VendorID, attr.ProductID); - SendDlgItemMessageW(hW, IDC_COMBO_FFB, CB_ADDSTRING, 0, (LPARAM)str); - } - - if (cfg->player_joys[0] == strPath) - { - SendDlgItemMessage(hW, IDC_COMBO_FFB, CB_SETCURSEL, sel_idx, 0); - selectedJoy[0] = sel_idx; - } - else if (cfg->player_joys[1] == strPath) - { - selectedJoy[1] = sel_idx; - } - sel_idx++; - } - free(didData); - HidD_FreePreparsedData(pPreparsedData); - i++; - } - if (usbHandle != INVALID_HANDLE_VALUE) - CloseHandle(usbHandle); - } - - void populateMappings(HWND hW) - { - LVITEM lvItem; - HWND lv = GetDlgItem(hW, IDC_LIST1); - - //LoadMappings(mapVector); - - memset(&lvItem, 0, sizeof(lvItem)); - - lvItem.mask = LVIF_TEXT | LVIF_PARAM; - lvItem.cchTextMax = 256; - lvItem.iItem = 0; - lvItem.iSubItem = 0; - - TCHAR tmp[256]; - int m[3]; - - for (auto& it : mapVector) - { - //TODO feels a bit hacky - bool isKB = (it.devName == TEXT("Keyboard")); - if (isKB) - { - m[0] = PAD_BUTTON_COUNT; - m[1] = PAD_AXIS_COUNT; - m[2] = 4; - } - else - { - m[0] = MAX_BUTTONS; - m[1] = MAX_AXES; - m[2] = 4; - } - - for (int i = 0; i < m[0]; i++) - { - //if((*it).btnMap[i] >= PAD_BUTTON_COUNT) - // continue; - int btn = it.btnMap[i]; - int val = PLY_GET_VALUE(plyCapturing, btn); - - lvItem.iItem = ListView_GetItemCount(lv); - if (PLY_IS_MAPPED(plyCapturing, btn) /*&& val < PAD_BUTTON_COUNT*/) - { - swprintf_s(tmp, 255, L"%s", it.devName.c_str()); //TODO - lvItem.pszText = tmp; - lvItem.lParam = (LPARAM) & (it.btnMap[i]); - ListView_InsertItem(lv, &lvItem); - swprintf_s(tmp, 255, L"P%d: Button %d", plyCapturing + 1, isKB ? val : i); - ListView_SetItemText(lv, lvItem.iItem, 1, tmp); - - swprintf_s(tmp, 255, L"%S", isKB ? BTN2TXT[i] : BTN2TXT[val]); - ListView_SetItemText(lv, lvItem.iItem, 2, tmp); - } - } - - for (int i = 0; i < m[1]; i++) - { - //if(it.axisMap[i] >= PAD_AXIS_COUNT) - // continue; - int axis = it.axisMap[i]; - int val = PLY_GET_VALUE(plyCapturing, axis); - - if (PLY_IS_MAPPED(plyCapturing, axis) /* && val < PAD_AXIS_COUNT*/) - { - lvItem.iItem = ListView_GetItemCount(lv); - //swprintf_s(tmp, 255, L"%s", it.devName.c_str()); //TODO - lvItem.pszText = (LPWSTR)it.devName.c_str(); - lvItem.lParam = (LPARAM) & (it.axisMap[i]); - ListView_InsertItem(lv, &lvItem); - - swprintf_s(tmp, 255, L"P%d: Axis %d", plyCapturing + 1, isKB ? val : i); - ListView_SetItemText(lv, lvItem.iItem, 1, tmp); - - swprintf_s(tmp, 255, L"%S", isKB ? AXIS2TXT[i] : AXIS2TXT[val]); - ListView_SetItemText(lv, lvItem.iItem, 2, tmp); - } - } - - for (int i = 0; i < m[2]; i++) - { - int hat = it.hatMap[i]; - int val = PLY_GET_VALUE(plyCapturing, hat); - - if (PLY_IS_MAPPED(plyCapturing, hat) /*&& val < PAD_HAT_COUNT*/) - { - lvItem.iItem = ListView_GetItemCount(lv); - lvItem.pszText = (LPWSTR)it.devName.c_str(); - lvItem.lParam = (LPARAM) & (it.hatMap[i]); - ListView_InsertItem(lv, &lvItem); - - swprintf_s(tmp, 255, L"P%d: Hat %d", plyCapturing + 1, isKB ? val : i); - ListView_SetItemText(lv, lvItem.iItem, 1, tmp); - - swprintf_s(tmp, 255, L"Hat %d", isKB ? i : val); - ListView_SetItemText(lv, lvItem.iItem, 2, tmp); - } - } - } - } - - static void ParseRawInput(PRAWINPUT pRawInput, HWND hW) - { - PHIDP_PREPARSED_DATA pPreparsedData = NULL; - HIDP_CAPS Caps; - PHIDP_BUTTON_CAPS pButtonCaps = NULL; - PHIDP_VALUE_CAPS pValueCaps = NULL; - USHORT capsLength; - UINT bufferSize; - USAGE usage[MAX_BUTTONS]; - ULONG i, usageLength, value; - TCHAR name[1024] = {0}; - UINT nameSize = 1024; - UINT pSize; - RID_DEVICE_INFO devInfo; - std::wstring devName; - //DevInfo_t mapDevInfo; - Mappings* mapping = NULL; - int axis; - TCHAR buf[256]; - //std::pair iter; - - // - // Get the preparsed data block - // - GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, name, &nameSize); - pSize = sizeof(devInfo); - GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &devInfo, &pSize); - - if (devInfo.dwType == RIM_TYPEKEYBOARD) - { - devName = TEXT("Keyboard"); - } - else - { - CHECK(GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0); - CHECK(pPreparsedData = (PHIDP_PREPARSED_DATA)malloc(bufferSize)); - CHECK((int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0); - CHECK(HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS); - - devName = name; - std::transform(devName.begin(), devName.end(), devName.begin(), ::toupper); - } - - for (auto& it : mapVector) - { - if (it.hidPath == devName) - { - mapping = &(it); - break; - } - } - - if (mapping == NULL) - { - Mappings m; // = new Mappings; - ZeroMemory(&m, sizeof(Mappings)); - mapVector.push_back(m); - mapping = &mapVector.back(); - mapping->devName = devName; - mapping->hidPath = devName; - } - //TODO get real dev name, probably from registry (see PAD Windows) - if (!mapping->devName.length()) - mapping->devName = devName; - - if (devInfo.dwType == RIM_TYPEKEYBOARD && - (pRawInput->data.keyboard.Flags & RI_KEY_BREAK) != RI_KEY_BREAK) - { - if (pRawInput->data.keyboard.VKey == 0xff) - return; //TODO - if (pRawInput->data.keyboard.VKey == VK_ESCAPE) - { - resetState(hW); - return; - } - if (btnCapturing < PAD_BUTTON_COUNT) - { - mapping->btnMap[btnCapturing] = PLY_SET_MAPPED(plyCapturing, pRawInput->data.keyboard.VKey); - swprintf_s(buf, TEXT("Captured KB button %d"), pRawInput->data.keyboard.VKey); - SendDlgItemMessage(dgHwnd, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)buf); - btnCapturing = PAD_BUTTON_COUNT; - } - else if (hatCapturing < PAD_HAT_COUNT) - { - for (int h = 0; h < 4; h++) - { - if (HATS_8TO4[h] == hatCapturing) - mapping->hatMap[h] = PLY_SET_MAPPED(plyCapturing, pRawInput->data.keyboard.VKey); - } - swprintf_s(buf, TEXT("Captured KB button %d"), pRawInput->data.keyboard.VKey); - SendDlgItemMessage(dgHwnd, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)buf); - hatCapturing = PAD_HAT_COUNT; - } - else if (axisCapturing < PAD_AXIS_COUNT) - { - mapping->axisMap[axisCapturing] = PLY_SET_MAPPED(plyCapturing, pRawInput->data.keyboard.VKey); - swprintf_s(buf, TEXT("Captured KB button %d"), pRawInput->data.keyboard.VKey); - SendDlgItemMessage(dgHwnd, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)buf); - axisCapturing = PAD_AXIS_COUNT; - } - } - else //if(devInfo.dwType == RIM_TYPEHID) - { - //Capture buttons - if (btnCapturing < PAD_BUTTON_COUNT || hatCapturing < PAD_HAT_COUNT) - { - // Button caps - CHECK(pButtonCaps = (PHIDP_BUTTON_CAPS)malloc(sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps)); - - capsLength = Caps.NumberInputButtonCaps; - CHECK(HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS); - int numberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1; - - usageLength = numberOfButtons; - CHECK( - HidP_GetUsages( - HidP_Input, pButtonCaps->UsagePage, 0, usage, &usageLength, pPreparsedData, - (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS); - - if (usageLength > 0) //Using first button only though - { - if (btnCapturing < PAD_BUTTON_COUNT) - { - mapping->btnMap[usage[0] - pButtonCaps->Range.UsageMin] = PLY_SET_MAPPED(plyCapturing, btnCapturing); - btnCapturing = PAD_BUTTON_COUNT; - } - else if (hatCapturing < PAD_HAT_COUNT) - { - mapping->hatMap[usage[0] - pButtonCaps->Range.UsageMin] = PLY_SET_MAPPED(plyCapturing, hatCapturing); - hatCapturing = PAD_HAT_COUNT; - } - } - } - else if (axisCapturing < PAD_AXIS_COUNT) - { - // Value caps - CHECK(pValueCaps = (PHIDP_VALUE_CAPS)malloc(sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps)); - capsLength = Caps.NumberInputValueCaps; - CHECK(HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS); - - for (i = 0; i < Caps.NumberInputValueCaps && axisCapturing < PAD_AXIS_COUNT; i++) - { - CHECK( - HidP_GetUsageValue( - HidP_Input, pValueCaps[i].UsagePage, 0, pValueCaps[i].Range.UsageMin, &value, pPreparsedData, - (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS); - - uint32_t logical = pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin; - - switch (pValueCaps[i].Range.UsageMin) - { - case HID_USAGE_GENERIC_X: - case HID_USAGE_GENERIC_Y: - case HID_USAGE_GENERIC_Z: - case HID_USAGE_GENERIC_RX: - case HID_USAGE_GENERIC_RY: - case HID_USAGE_GENERIC_RZ: - axis = pValueCaps[i].Range.UsageMin - HID_USAGE_GENERIC_X; - if (axisPass2) - { - if ((uint32_t)abs((int)(axisDiff[axis] - value)) > (logical >> 2)) - { - mapping->axisMap[axis] = PLY_SET_MAPPED(plyCapturing, axisCapturing); - axisPass2 = false; - swprintf_s(buf, TEXT("Captured wheel axis %d"), axis); - SendDlgItemMessage(dgHwnd, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)buf); - axisCapturing = PAD_AXIS_COUNT; - goto Error; - } - break; - } - axisDiff[axis] = value; - break; - - case 0x39: // Hat Switch - if (value < 0x8) - { - mapping->axisMap[6] = PLY_SET_MAPPED(plyCapturing, axisCapturing); - axisPass2 = false; - swprintf_s(buf, TEXT("Captured wheel hat switch")); - SendDlgItemMessage(dgHwnd, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)buf); - axisCapturing = PAD_AXIS_COUNT; - goto Error; - } - break; - } - } - - axisPass2 = true; - } - } - Error: - SAFE_FREE(pPreparsedData); - SAFE_FREE(pButtonCaps); - SAFE_FREE(pValueCaps); - } - - //Also when switching player - void resetState(HWND hW) - { - SendDlgItemMessage(hW, IDC_COMBO_FFB, CB_SETCURSEL, selectedJoy[plyCapturing], 0); - SendDlgItemMessage(hW, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)TEXT("")); - - btnCapturing = PAD_BUTTON_COUNT; - axisCapturing = PAD_AXIS_COUNT; - hatCapturing = PAD_HAT_COUNT; - ListView_DeleteAllItems(GetDlgItem(hW, IDC_LIST1)); - populateMappings(hW); - } - - static void Register(HWND hW) - { - RAWINPUTDEVICE rid[3]; - rid[0].usUsagePage = 0x01; - rid[0].usUsage = 0x05; - rid[0].dwFlags = hW ? RIDEV_INPUTSINK : RIDEV_REMOVE; // adds game pad - rid[0].hwndTarget = hW; - - rid[1].usUsagePage = 0x01; - rid[1].usUsage = 0x04; - rid[1].dwFlags = hW ? RIDEV_INPUTSINK : RIDEV_REMOVE; // adds joystick - rid[1].hwndTarget = hW; - - rid[2].usUsagePage = 0x01; - rid[2].usUsage = 0x06; - rid[2].dwFlags = hW ? RIDEV_INPUTSINK /*| RIDEV_NOLEGACY*/ : RIDEV_REMOVE; // adds HID keyboard and also ignores legacy keyboard messages - rid[2].hwndTarget = hW; - - if (!RegisterRawInputDevices(rid, 3, sizeof(rid[0]))) - { - SendDlgItemMessage(hW, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)TEXT("Could not register raw input devices.")); - } - } - - INT_PTR CALLBACK ConfigureRawDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - - TCHAR buf[256]; - LVITEM lv; - RawDlgConfig* cfg = (RawDlgConfig*)GetWindowLongPtr(hW, GWLP_USERDATA); - int ret = 0; - switch (uMsg) - { - case WM_INITDIALOG: - if (!InitHid()) - return FALSE; - dgHwnd = hW; - SetWindowLongPtr(hW, GWLP_USERDATA, lParam); - //SendDlgItemMessage(hW, IDC_BUILD_DATE, WM_SETTEXT, 0, (LPARAM)__DATE__ " " __TIME__); - ListView_SetExtendedListViewStyle(GetDlgItem(hW, IDC_LIST1), LVS_EX_FULLROWSELECT); - - //LoadConfig(); - cfg = (RawDlgConfig*)lParam; - - if (!LoadSetting(cfg->dev_type, PLAYER_ONE_PORT, APINAME, N_JOYSTICK, cfg->player_joys[0])) - cfg->player_joys[0].clear(); - - if (!LoadSetting(cfg->dev_type, PLAYER_TWO_PORT, APINAME, N_JOYSTICK, cfg->player_joys[1])) - cfg->player_joys[1].clear(); - - LoadSetting(cfg->dev_type, PLAYER_ONE_PORT, APINAME, N_WHEEL_PT, cfg->pt[0]); - LoadSetting(cfg->dev_type, PLAYER_TWO_PORT, APINAME, N_WHEEL_PT, cfg->pt[1]); - - Register(hW); - LoadMappings(cfg->dev_type, mapVector); - //if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); - CheckDlgButton(hW, IDC_DFP_PASS, cfg->pt[0]); - populate(hW, cfg); - resetState(hW); - return TRUE; - - case WM_INPUT: - ret = S_FALSE; - if (axisCapturing != PAD_AXIS_COUNT || - btnCapturing != PAD_BUTTON_COUNT || - hatCapturing != PAD_HAT_COUNT) - { - ret = 0; - PRAWINPUT pRawInput; - UINT bufferSize; - UINT cbsize = sizeof(RAWINPUT); - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER)); - pRawInput = (PRAWINPUT)malloc(bufferSize); - if (!pRawInput) - break; - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, pRawInput, &bufferSize, sizeof(RAWINPUTHEADER)) > 0) - ParseRawInput(pRawInput, hW); - free(pRawInput); - - if (axisCapturing == PAD_AXIS_COUNT && - btnCapturing == PAD_BUTTON_COUNT && - hatCapturing == PAD_HAT_COUNT) - { - resetState(hW); - } - } - DefWindowProc(hW, uMsg, wParam, lParam); //TODO should call for cleanup? - return ret; - - case WM_KEYDOWN: - if (LOWORD(lParam) == VK_ESCAPE) - { - resetState(hW); - return TRUE; - } - break; - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->code) - { - case TCN_SELCHANGE: - switch (LOWORD(wParam)) - { - case IDC_TAB1: - plyCapturing = SendDlgItemMessage(hW, IDC_TAB1, TCM_GETCURSEL, 0, 0); - CheckDlgButton(hW, IDC_DFP_PASS, cfg->pt[plyCapturing]); - resetState(hW); - break; - } - break; - } - break; - case WM_TIMER: - if (wParam == 1) - resetState(hW); - break; - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case LBN_SELCHANGE: - switch (LOWORD(wParam)) - { - case IDC_COMBO_FFB: - selectedJoy[plyCapturing] = SendDlgItemMessage(hW, IDC_COMBO_FFB, CB_GETCURSEL, 0, 0); - //player_joys[plyCapturing] = *(joysDev.begin() + selectedJoy[plyCapturing]); - //resetState(hW); - /*if(selectedJoy[plyCapturing] > 0 && selectedJoy[plyCapturing] == selectedJoy[1-plyCapturing]) - { - selectedJoy[plyCapturing] = 0; - resetState(hW); - SendDlgItemMessage(hW, IDC_COMBO_FFB, CB_SETCURSEL, selectedJoy[plyCapturing], 0); - //But would you want to? - SendDlgItemMessageA(hW, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)"Both players can't have the same controller."); //Actually, whatever, but config logics are limited ;P - }*/ - break; - case IDC_COMBO1: - break; - case IDC_COMBO2: - break; - default: - return FALSE; - } - break; - case BN_CLICKED: - if (axisCapturing == PAD_AXIS_COUNT && - btnCapturing == PAD_BUTTON_COUNT && - LOWORD(wParam) >= IDC_BUTTON13 && - LOWORD(wParam) <= IDC_BUTTON16) - { - switch (LOWORD(wParam)) - { - case IDC_BUTTON13: - hatCapturing = PAD_HAT_N; - break; - case IDC_BUTTON14: - hatCapturing = PAD_HAT_W; - break; - case IDC_BUTTON15: - hatCapturing = PAD_HAT_E; - break; - case IDC_BUTTON16: - hatCapturing = PAD_HAT_S; - break; - } - MSG_PRESS_ESC(hW); - SetTimer(hW, 1, 5000, nullptr); - } - - switch (LOWORD(wParam)) - { - case IDC_DFP_PASS: - cfg->pt[plyCapturing] = IsDlgButtonChecked(hW, IDC_DFP_PASS) > 0; - break; - case IDC_UNBIND: - HWND lhW; - lhW = GetDlgItem(hW, IDC_LIST1); - while (1) - { - ZeroMemory(&lv, sizeof(LVITEM)); - int sel = ListView_GetNextItem(lhW, -1, LVNI_SELECTED); - if (sel < 0) - break; - lv.iItem = sel; - lv.mask = LVIF_PARAM; - ListView_GetItem(lhW, &lv); - ListView_DeleteItem(lhW, sel); - ListView_EnsureVisible(lhW, sel, 0); - - int* map = (int*)lv.lParam; - if (map) - *map = PLY_UNSET_MAPPED(plyCapturing, *map); - } - resetState(hW); - break; - case IDC_BUTTON1: //cross - case IDC_BUTTON2: //square - case IDC_BUTTON3: //circle - case IDC_BUTTON4: //triangle - case IDC_BUTTON5: //L1 - case IDC_BUTTON6: //R1 - case IDC_BUTTON7: //L2 - case IDC_BUTTON8: //R2 - case IDC_BUTTON9: //select - case IDC_BUTTON10: //start - case IDC_BUTTON11: //L3 - case IDC_BUTTON12: //R3 - if (axisCapturing == PAD_AXIS_COUNT && - hatCapturing == PAD_HAT_COUNT) - { - btnCapturing = (PS2Buttons)(LOWORD(wParam) - IDC_BUTTON1); - MSG_PRESS_ESC(hW); - SetTimer(hW, 1, 5000, nullptr); - } - return TRUE; - case IDC_BUTTON17: //x - case IDC_BUTTON18: //y - case IDC_BUTTON19: //z - case IDC_BUTTON20: //rz - case IDC_BUTTON21: //hat - if (btnCapturing == PAD_BUTTON_COUNT && - hatCapturing == PAD_HAT_COUNT) - { - axisCapturing = (PS2Axis)(LOWORD(wParam) - IDC_BUTTON17); - swprintf_s(buf, TEXT("Capturing for axis %u, press ESC to cancel"), axisCapturing); - SendDlgItemMessage(hW, IDC_STATIC_CAP, WM_SETTEXT, 0, (LPARAM)buf); - SetTimer(hW, 1, 5000, nullptr); - } - return TRUE; - case IDCANCEL: - if (btnCapturing < PAD_BUTTON_COUNT || - axisCapturing < PAD_AXIS_COUNT || - hatCapturing < PAD_HAT_COUNT) - return FALSE; - Register(nullptr); - EndDialog(hW, FALSE); - return TRUE; - case IDOK: - Register(nullptr); - cfg = (RawDlgConfig*)GetWindowLongPtr(hW, GWLP_USERDATA); - cfg->player_joys[0] = joysDev[selectedJoy[0]]; - cfg->player_joys[1] = joysDev[selectedJoy[1]]; - - INT_PTR res = RESULT_OK; - - if (!SaveSetting(cfg->dev_type, PLAYER_ONE_PORT, APINAME, N_JOYSTICK, cfg->player_joys[0])) - res = RESULT_FAILED; - if (!SaveSetting(cfg->dev_type, PLAYER_TWO_PORT, APINAME, N_JOYSTICK, cfg->player_joys[1])) - res = RESULT_FAILED; - - SaveMappings(cfg->dev_type, mapVector); - - if (!SaveSetting(cfg->dev_type, PLAYER_ONE_PORT, APINAME, N_WHEEL_PT, cfg->pt[0])) - res = RESULT_FAILED; - if (!SaveSetting(cfg->dev_type, PLAYER_TWO_PORT, APINAME, N_WHEEL_PT, cfg->pt[1])) - res = RESULT_FAILED; - - EndDialog(hW, res); - return TRUE; - } - } - } - - return S_OK; //DefWindowProc(hW, uMsg, wParam, lParam); - } - - } // namespace raw -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/raw/raw-config.rc b/pcsx2/USB/usb-pad/raw/raw-config.rc deleted file mode 100644 index 173f9cc239..0000000000 --- a/pcsx2/USB/usb-pad/raw/raw-config.rc +++ /dev/null @@ -1,146 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "raw-config-res.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winresrc.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_RAWCONFIG DIALOGEX 0, 0, 385, 272 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Configure (raw input api)" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,270,252,50,14 - PUSHBUTTON "Cancel",IDCANCEL,326,252,50,14 - CONTROL "",IDC_TAB1,"SysTabControl32",0x0,6,6,372,240 - PUSHBUTTON "Cross / A",IDC_BUTTON1,192,42,50,14 - PUSHBUTTON "Square / X",IDC_BUTTON2,246,42,50,14 - PUSHBUTTON "Circle / B",IDC_BUTTON3,192,60,50,14 - PUSHBUTTON "Triangle / Y",IDC_BUTTON4,246,60,50,14 - PUSHBUTTON "L1 / L",IDC_BUTTON6,192,79,50,14 - PUSHBUTTON "R1 / R",IDC_BUTTON5,246,79,50,14 - PUSHBUTTON "L2",IDC_BUTTON8,192,96,50,14 - PUSHBUTTON "R2",IDC_BUTTON7,246,96,50,14 - PUSHBUTTON "L3",IDC_BUTTON12,192,114,50,14 - PUSHBUTTON "R3",IDC_BUTTON11,246,114,50,14 - PUSHBUTTON "Select",IDC_BUTTON9,192,132,50,14 - PUSHBUTTON "Start",IDC_BUTTON10,246,132,50,14 - PUSHBUTTON "Up",IDC_BUTTON13,224,161,50,14 - PUSHBUTTON "Left",IDC_BUTTON14,194,179,50,14 - PUSHBUTTON "Right",IDC_BUTTON15,254,179,50,14 - PUSHBUTTON "Down",IDC_BUTTON16,224,196,50,14 - PUSHBUTTON "Wheel",IDC_BUTTON17,306,42,50,14 - PUSHBUTTON "Clutch",IDC_BUTTON18,306,60,50,14 - PUSHBUTTON "Throttle",IDC_BUTTON19,306,79,50,14 - PUSHBUTTON "Brake",IDC_BUTTON20,306,95,50,14 - PUSHBUTTON "Hatswitch",IDC_BUTTON21,306,114,50,14 - LTEXT "",IDC_STATIC_CAP,6,252,156,8 - CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,12,42,174,136 - PUSHBUTTON "Unbind",IDC_UNBIND,12,180,50,14 - COMBOBOX IDC_COMBO_FFB,18,207,167,51,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Logitech wheel pass-through",IDC_DFP_PASS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,224,168,12,WS_EX_STATICEDGE - GROUPBOX "Bind axes",IDC_STATIC,301,30,59,102 - GROUPBOX "Bind hat switch to a button",IDC_STATIC,192,150,114,66 - GROUPBOX "FF / pass-through device",IDC_STATIC,14,196,175,43 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_RAWCONFIG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 378 - TOPMARGIN, 7 - BOTTOMMARGIN, 265 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_RAWCONFIG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// Spanish (Argentina) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESS) -LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "raw-config-res.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""WinResrc.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // Spanish (Argentina) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp b/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp deleted file mode 100644 index d8cef35aec..0000000000 --- a/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "USB/USB.h" -#include "USB/Win32/Config_usb.h" -#include "usb-pad-raw.h" - -namespace usb_pad -{ - namespace raw - { - - void RawInputPad::WriterThread(void* ptr) - { - DWORD written = 0; - std::array buf; - - RawInputPad* pad = static_cast(ptr); - pad->mWriterThreadIsRunning = true; - - while (pad->mUsbHandle != INVALID_HANDLE_VALUE) - { - if (pad->mFFData.wait_dequeue_timed(buf, std::chrono::milliseconds(1000))) - { - const DWORD res = WriteFile(pad->mUsbHandle, buf.data(), buf.size(), &written, &pad->mOLWrite); - uint8_t* d = buf.data(); - - WaitForSingleObject(pad->mOLWrite.hEvent, 1000); - } - } - - pad->mWriterThreadIsRunning = false; - } - - void RawInputPad::ReaderThread(void* ptr) - { - RawInputPad* pad = static_cast(ptr); - DWORD read = 0; - std::array report; //32 is random - - pad->mReaderThreadIsRunning = true; - int errCount = 0; - - while (pad->mUsbHandle != INVALID_HANDLE_VALUE) - { - if (GetOverlappedResult(pad->mUsbHandle, &pad->mOLRead, &read, FALSE)) // TODO check if previous read finally completed after WaitForSingleObject timed out - ReadFile(pad->mUsbHandle, report.data(), std::min(pad->mCaps.InputReportByteLength, (USHORT)report.size()), nullptr, &pad->mOLRead); // Seems to only read data when input changes and not current state overall - - if (WaitForSingleObject(pad->mOLRead.hEvent, 1000) == WAIT_OBJECT_0) - { - if (!pad->mReportData.try_enqueue(report)) // TODO May leave queue with too stale data. Use multi-producer/consumer queue? - { - if (!errCount) - Console.Warning("%s: Could not enqueue report data: %zd\n", APINAME, pad->mReportData.size_approx()); - errCount = (++errCount) % 16; - } - } - } - - pad->mReaderThreadIsRunning = false; - } - - int RawInputPad::TokenIn(uint8_t* buf, int len) - { - const int player = 1 - mPort; - - //Console.Warning("usb-pad: poll len=%li\n", len); - if (mDoPassthrough) - { - std::array report; //32 is random - if (mReportData.try_dequeue(report)) - { - //ZeroMemory(buf, len); - int size = std::min((int)mCaps.InputReportByteLength, len); - memcpy(buf, report.data(), size); - return size; - } - return 0; - } - - //in case compiler magic with bitfields interferes - wheel_data_t data_summed; - memset(&data_summed, 0xFF, sizeof(data_summed)); - data_summed.hatswitch = 0x8; - data_summed.buttons = 0; - - int copied = 0; - //TODO fix the logics, also Config.cpp - for (auto& it : mapVector) - { - - if (data_summed.steering < it.data[player].steering) - { - data_summed.steering = it.data[player].steering; - copied |= 1; - } - - //if(data_summed.clutch < it.data[player].clutch) - // data_summed.clutch = it.data[player].clutch; - - if (data_summed.throttle < it.data[player].throttle) - { - data_summed.throttle = it.data[player].throttle; - copied |= 2; - } - - if (data_summed.brake < it.data[player].brake) - { - data_summed.brake = it.data[player].brake; - copied |= 4; - } - - data_summed.buttons |= it.data[player].buttons; - if (data_summed.hatswitch > it.data[player].hatswitch) - data_summed.hatswitch = it.data[player].hatswitch; - } - - if (!copied) - memcpy(&data_summed, &mDataCopy, sizeof(wheel_data_t)); - else - { - if (!(copied & 1)) - data_summed.steering = mDataCopy.steering; - if (!(copied & 2)) - data_summed.throttle = mDataCopy.throttle; - if (!(copied & 4)) - data_summed.brake = mDataCopy.brake; - } - - pad_copy_data(mType, buf, data_summed); - - if (copied) - memcpy(&mDataCopy, &data_summed, sizeof(wheel_data_t)); - return len; - } - - int RawInputPad::TokenOut(const uint8_t* data, int len) - { - if (mUsbHandle == INVALID_HANDLE_VALUE) - return 0; - - if (data[0] == 0x8 || data[0] == 0xB) - return len; - //if(data[0] == 0xF8 && data[1] == 0x5) sendCrap = true; - if (data[0] == 0xF8 && - /* Allow range changes */ - !(data[1] == 0x81 || data[1] == 0x02 || data[1] == 0x03)) - return len; //don't send extended commands - - std::array report{0}; - - //If i'm reading it correctly MOMO report size for output has Report Size(8) and Report Count(7), so that's 7 bytes - //Now move that 7 bytes over by one and add report id of 0 (right?). Supposedly mandatory for HIDs. - memcpy(report.data() + 1, data, report.size() - 1); - - if (!mFFData.enqueue(report)) - { - return 0; - } - - return len; - } - - static void ParseRawInputHID(PRAWINPUT pRawInput, int subtype) - { - PHIDP_PREPARSED_DATA pPreparsedData = NULL; - HIDP_CAPS Caps; - PHIDP_BUTTON_CAPS pButtonCaps = NULL; - PHIDP_VALUE_CAPS pValueCaps = NULL; - UINT bufferSize = 0; - ULONG usageLength, value; - TCHAR name[1024] = {0}; - UINT nameSize = 1024; - RID_DEVICE_INFO devInfo = {0}; - std::wstring devName; - USHORT capsLength = 0; - USAGE usage[MAX_BUTTONS] = {0}; - Mappings* mapping = NULL; - int numberOfButtons; - - auto iter = mappings.find(pRawInput->header.hDevice); - if (iter != mappings.end()) - { - mapping = iter->second; - } - else - { - GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, name, &nameSize); - - devName = name; - std::transform(devName.begin(), devName.end(), devName.begin(), ::toupper); - - for (auto& it : mapVector) - { - if (it.hidPath == devName) - { - mapping = ⁢ - mappings[pRawInput->header.hDevice] = mapping; - break; - } - } - } - - if (mapping == NULL) - return; - - CHECK(GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0); - CHECK(pPreparsedData = (PHIDP_PREPARSED_DATA)malloc(bufferSize)); - CHECK((int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0); - CHECK(HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS); - //pSize = sizeof(devInfo); - //GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &devInfo, &pSize); - - //Unset buttons/axes mapped to this device - //pad_reset_data(&mapping->data[0]); - //pad_reset_data(&mapping->data[1]); - memset(&mapping->data[0], 0xFF, sizeof(wheel_data_t)); - memset(&mapping->data[1], 0xFF, sizeof(wheel_data_t)); - mapping->data[0].buttons = 0; - mapping->data[1].buttons = 0; - mapping->data[0].hatswitch = 0x8; - mapping->data[1].hatswitch = 0x8; - - //Get pressed buttons - CHECK(pButtonCaps = (PHIDP_BUTTON_CAPS)malloc(sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps)); - //If fails, maybe wheel only has axes - capsLength = Caps.NumberInputButtonCaps; - HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData); - - numberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1; - usageLength = countof(usage); - NTSTATUS stat; - if ((stat = HidP_GetUsages( - HidP_Input, pButtonCaps->UsagePage, 0, usage, &usageLength, pPreparsedData, - (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid)) == HIDP_STATUS_SUCCESS) - { - for (uint32_t i = 0; i < usageLength; i++) - { - uint16_t btn = mapping->btnMap[usage[i] - pButtonCaps->Range.UsageMin]; - for (int j = 0; j < 2; j++) - { - PS2WheelTypes wt = (PS2WheelTypes)subtype; - if (PLY_IS_MAPPED(j, btn)) - { - uint32_t wtbtn = 1 << convert_wt_btn(wt, PLY_GET_VALUE(j, btn)); - mapping->data[j].buttons |= wtbtn; - } - } - } - } - - /// Get axes' values - CHECK(pValueCaps = (PHIDP_VALUE_CAPS)malloc(sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps)); - capsLength = Caps.NumberInputValueCaps; - if (HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS) - { - for (USHORT i = 0; i < capsLength; i++) - { - if (HidP_GetUsageValue( - HidP_Input, pValueCaps[i].UsagePage, 0, - pValueCaps[i].Range.UsageMin, &value, pPreparsedData, - (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS) - { - continue; // if here then maybe something is up with HIDP_CAPS.NumberInputValueCaps - } - - //Console.Warning("Min/max %d/%d\t", pValueCaps[i].LogicalMin, pValueCaps[i].LogicalMax); - - //Get mapped axis for physical axis - uint16_t v = 0; - switch (pValueCaps[i].Range.UsageMin) - { - case HID_USAGE_GENERIC_X: //0x30 - case HID_USAGE_GENERIC_Y: - case HID_USAGE_GENERIC_Z: - case HID_USAGE_GENERIC_RX: - case HID_USAGE_GENERIC_RY: - case HID_USAGE_GENERIC_RZ: //0x35 - v = mapping->axisMap[pValueCaps[i].Range.UsageMin - HID_USAGE_GENERIC_X]; - break; - case HID_USAGE_GENERIC_HATSWITCH: - //Console.Warning("Hat: %02X\n", value); - v = mapping->axisMap[6]; - break; - } - - for (int j = 0; j < 2; j++) - { - if (!PLY_IS_MAPPED(j, v)) - continue; - - switch (PLY_GET_VALUE(j, v)) - { - case PAD_AXIS_X: // X-axis - //Console.Warning("X: %d\n", value); - // Need for logical min too? - //generic_data.axis_x = ((value - pValueCaps[i].LogicalMin) * 0x3FF) / (pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin); - if (subtype == WT_DRIVING_FORCE_PRO || subtype == WT_DRIVING_FORCE_PRO_1102) - mapping->data[j].steering = (value * 0x3FFF) / pValueCaps[i].LogicalMax; - else - //XXX Limit value range to 0..1023 if using 'generic' wheel descriptor - mapping->data[j].steering = (value * 0x3FF) / pValueCaps[i].LogicalMax; - break; - - case PAD_AXIS_Y: // Y-axis - //XXX Limit value range to 0..255 - mapping->data[j].clutch = (value * 0xFF) / pValueCaps[i].LogicalMax; - break; - - case PAD_AXIS_Z: // Z-axis - //Console.Warning("Z: %d\n", value); - //XXX Limit value range to 0..255 - mapping->data[j].throttle = (value * 0xFF) / pValueCaps[i].LogicalMax; - break; - - case PAD_AXIS_RZ: // Rotate-Z - //Console.Warning("Rz: %d\n", value); - //XXX Limit value range to 0..255 - mapping->data[j].brake = (value * 0xFF) / pValueCaps[i].LogicalMax; - break; - - case PAD_AXIS_HAT: // TODO Hat Switch - //Console.Warning("Hat: %02X\n", value); - //TODO 4 vs 8 direction hat switch - if (pValueCaps[i].LogicalMax == 4 && value < 4) - mapping->data[j].hatswitch = HATS_8TO4[value]; - else - mapping->data[j].hatswitch = value; - break; - } - } - } - } - - Error: - SAFE_FREE(pPreparsedData); - SAFE_FREE(pButtonCaps); - SAFE_FREE(pValueCaps); - } - - static void ParseRawInputKB(PRAWINPUT pRawInput, int subtype) - { - Mappings* mapping = nullptr; - - for (auto& it : mapVector) - { - if (!it.hidPath.compare(TEXT("Keyboard"))) - { - mapping = ⁢ - break; - } - } - - if (mapping == NULL) - return; - - for (uint32_t i = 0; i < PAD_BUTTON_COUNT; i++) - { - uint16_t btn = mapping->btnMap[i]; - for (int j = 0; j < 2; j++) - { - if (PLY_IS_MAPPED(j, btn)) - { - PS2WheelTypes wt = (PS2WheelTypes) subtype; - if (PLY_GET_VALUE(j, mapping->btnMap[i]) == pRawInput->data.keyboard.VKey) - { - uint32_t wtbtn = convert_wt_btn(wt, i); - if (pRawInput->data.keyboard.Flags & RI_KEY_BREAK) - mapping->data[j].buttons &= ~(1 << wtbtn); //unset - else /* if(pRawInput->data.keyboard.Flags == RI_KEY_MAKE) */ - mapping->data[j].buttons |= (1 << wtbtn); //set - } - } - } - } - - for (uint32_t i = 0; i < 4 /*PAD_HAT_COUNT*/; i++) - { - uint16_t btn = mapping->hatMap[i]; - for (int j = 0; j < 2; j++) - { - if (PLY_IS_MAPPED(j, btn)) - { - if (PLY_GET_VALUE(j, mapping->hatMap[i]) == pRawInput->data.keyboard.VKey) - if (pRawInput->data.keyboard.Flags & RI_KEY_BREAK) - mapping->data[j].hatswitch = 0x8; - else //if(pRawInput->data.keyboard.Flags == RI_KEY_MAKE) - mapping->data[j].hatswitch = HATS_8TO4[i]; - } - } - } - } - - void RawInputPad::ParseRawInput(PRAWINPUT pRawInput) - { - if (pRawInput->header.dwType == RIM_TYPEKEYBOARD) - ParseRawInputKB(pRawInput, Type()); - else if (pRawInput->header.dwType == RIM_TYPEHID) - ParseRawInputHID(pRawInput, Type()); - } - - int RawInputPad::Open() - { - PHIDP_PREPARSED_DATA pPreparsedData = nullptr; - HIDD_ATTRIBUTES attr; - - Close(); - - mappings.clear(); - LoadMappings(mDevType, mapVector); - - memset(&mOLRead, 0, sizeof(OVERLAPPED)); - memset(&mOLWrite, 0, sizeof(OVERLAPPED)); - memset(&mDataCopy, 0xFF, sizeof(mDataCopy)); - - mUsbHandle = INVALID_HANDLE_VALUE; - std::wstring path; - if (!LoadSetting(mDevType, mPort, APINAME, N_JOYSTICK, path)) - return 1; - - LoadSetting(mDevType, mPort, APINAME, N_WHEEL_PT, mDoPassthrough); - - mUsbHandle = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - - if (mUsbHandle != INVALID_HANDLE_VALUE) - { - mOLRead.hEvent = CreateEvent(0, 0, 0, 0); - mOLWrite.hEvent = CreateEvent(0, 0, 0, 0); - - HidD_GetAttributes(mUsbHandle, &(attr)); - - bool isClassicLogitech = (attr.VendorID == PAD_VID) && (attr.ProductID != 0xC262); - bool isKeyboardmania = (attr.VendorID == 0x0507) && (attr.ProductID == 0x0010); - if (!isClassicLogitech && !isKeyboardmania) - { - Console.Warning("USB: Vendor is not Logitech or wheel is G920. Not sending force feedback commands for safety reasons.\n"); - mDoPassthrough = 0; - Close(); - } - else if (!mWriterThreadIsRunning) - { - if (mWriterThread.joinable()) - mWriterThread.join(); - mWriterThread = std::thread(RawInputPad::WriterThread, this); - } - - if (mDoPassthrough) - { - // for passthrough only - HidD_GetPreparsedData(mUsbHandle, &pPreparsedData); - HidP_GetCaps(pPreparsedData, &(mCaps)); - HidD_FreePreparsedData(pPreparsedData); - - if (!mReaderThreadIsRunning) - { - if (mReaderThread.joinable()) - mReaderThread.join(); - mReaderThread = std::thread(RawInputPad::ReaderThread, this); - } - } - - shared::rawinput::RegisterCallback(this); - return 0; - } - else - Console.Warning("USB: Could not open device '%s'.\nPassthrough and FFB will not work.\n", path.c_str()); - - return 0; - } - - int RawInputPad::Close() - { - if (mUsbHandle != INVALID_HANDLE_VALUE) - { - Reset(); - Sleep(100); // give WriterThread some time to write out Reset() commands - CloseHandle(mUsbHandle); - CloseHandle(mOLRead.hEvent); - CloseHandle(mOLWrite.hEvent); - } - - shared::rawinput::UnregisterCallback(this); - mUsbHandle = INVALID_HANDLE_VALUE; - return 0; - } - -// --------- -#include "raw-config-res.h" - - INT_PTR CALLBACK ConfigureRawDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam); - int RawInputPad::Configure(int port, const char* dev_type, void* data) - { - Win32Handles* h = (Win32Handles*)data; - INT_PTR res = RESULT_FAILED; - if (shared::rawinput::Initialize(h->hWnd)) - { - RawDlgConfig config(port, dev_type); - res = DialogBoxParam(h->hInst, MAKEINTRESOURCE(IDD_RAWCONFIG), h->hWnd, ConfigureRawDlgProc, (LPARAM)&config); - shared::rawinput::Uninitialize(); - } - return (int)res; - } - - } // namespace raw -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/raw/usb-pad-raw.h b/pcsx2/USB/usb-pad/raw/usb-pad-raw.h deleted file mode 100644 index c4373a2d45..0000000000 --- a/pcsx2/USB/usb-pad/raw/usb-pad-raw.h +++ /dev/null @@ -1,144 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 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 Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#ifndef USBPADRAW_H -#define USBPADRAW_H -#include -#include -#include -#include "USB/usb-pad/padproxy.h" -#include "USB/usb-pad/usb-pad.h" -#include "USB/shared/rawinput_usb.h" -#include "USB/readerwriterqueue/readerwriterqueue.h" - -namespace usb_pad -{ - namespace raw - { - - static const char* APINAME = "rawinput"; - - class RawInputPad : public Pad, shared::rawinput::ParseRawInputCB - { - public: - RawInputPad(int port, const char* dev_type) - : Pad(port, dev_type) - , mDoPassthrough(false) - , mUsbHandle(INVALID_HANDLE_VALUE) - , mWriterThreadIsRunning(false) - , mReaderThreadIsRunning(false) - { - if (!InitHid()) - throw PadError("InitHid() failed!"); - } - ~RawInputPad() - { - Close(); - if (mWriterThread.joinable()) - mWriterThread.join(); - } - int Open(); - int Close(); - int TokenIn(uint8_t* buf, int len); - int TokenOut(const uint8_t* data, int len); - int Reset() - { - uint8_t reset[7] = {0}; - reset[0] = 0xF3; //stop forces - TokenOut(reset, sizeof(reset)); - return 0; - } - void ParseRawInput(PRAWINPUT pRawInput); - - static const TCHAR* Name() - { - return TEXT("Raw Input"); - } - - static int Configure(int port, const char* dev_type, void* data); - - protected: - static void WriterThread(void* ptr); - static void ReaderThread(void* ptr); - HIDP_CAPS mCaps; - HANDLE mUsbHandle = (HANDLE)-1; - OVERLAPPED mOLRead; - OVERLAPPED mOLWrite; - - int32_t mDoPassthrough; - wheel_data_t mDataCopy; - std::thread mWriterThread; - std::thread mReaderThread; - std::atomic mWriterThreadIsRunning; - std::atomic mReaderThreadIsRunning; - moodycamel::BlockingReaderWriterQueue, 32> mFFData; - moodycamel::BlockingReaderWriterQueue, 16> mReportData; //TODO 32 is random - }; - -/* -Layout: - 0x8000 bit means it is a valid mapping, - where value is PS2 button/axis and - array (Mappings::btnMap etc.) index is physical button/axis - (reversed for keyboard mappings). - [31..16] bits player 2 mapping - [15..0] bits player 1 mapping -*/ -//Maybe getting too convoluted -//Check for which player(s) the mapping is for -//Using MSB (right? :P) to indicate if valid mapping -#define PLY_IS_MAPPED(p, x) (x & (0x8000 << (16 * p))) -// Add owning player bits to mapping -#define PLY_SET_MAPPED(p, x) (((x & 0x7FFF) | 0x8000) << (16 * p)) -#define PLY_UNSET_MAPPED(p, x) (x & ~(0xFFFF << (16 * p))) -#define PLY_GET_VALUE(p, x) ((x >> (16 * p)) & 0x7FFF) - - //Joystick: btnMap/axisMap/hatMap[physical button] = ps2 button - //Keyboard: btnMap/axisMap/hatMap[ps2 button] = vkey - struct Mappings - { - uint32_t btnMap[MAX_BUTTONS]; - uint32_t axisMap[MAX_AXES]; - uint32_t hatMap[8]; - wheel_data_t data[2]; - std::wstring devName; -#if _WIN32 - std::wstring hidPath; -#endif - }; - - struct RawDlgConfig - { - int port; - const char* dev_type; - std::wstring player_joys[2]; - int32_t pt[2]{}; - RawDlgConfig(int p, const char* dev_type_) - : port(p) - , dev_type(dev_type_) - { - } - }; - - typedef std::vector MapVector; - static MapVector mapVector; - static std::map mappings; - - void LoadMappings(const char* dev_type, MapVector& maps); - void SaveMappings(const char* dev_type, MapVector& maps); - - } // namespace raw -} // namespace usb_pad -#endif diff --git a/pcsx2/USB/usb-pad/usb-pad-ff.cpp b/pcsx2/USB/usb-pad/usb-pad-ff.cpp index 3b601b09fe..f7fa848c55 100644 --- a/pcsx2/USB/usb-pad/usb-pad-ff.cpp +++ b/pcsx2/USB/usb-pad/usb-pad-ff.cpp @@ -19,8 +19,7 @@ namespace usb_pad { - - void SetConstantForce(FFDevice* ffdev, int force) + static void SetConstantForce(FFDevice* ffdev, int force) { //parsed_ff_data ff; @@ -28,7 +27,7 @@ namespace usb_pad ffdev->SetConstantForce(level); } - void SetSpringForce(FFDevice* ffdev, const spring& force, int caps) + static void SetSpringForce(FFDevice* ffdev, const spring& force, int caps) { parsed_ff_data ff; @@ -53,7 +52,7 @@ namespace usb_pad ffdev->SetSpringForce(ff); } - void SetDamperForce(FFDevice* ffdev, const damper& force, int caps) + static void SetDamperForce(FFDevice* ffdev, const damper& force, int caps) { parsed_ff_data ff; @@ -69,7 +68,7 @@ namespace usb_pad ffdev->SetDamperForce(ff); } - void SetFrictionForce(FFDevice* ffdev, const friction& frict) + static void SetFrictionForce(FFDevice* ffdev, const friction& frict) { parsed_ff_data ff; @@ -88,21 +87,21 @@ namespace usb_pad ffdev->SetFrictionForce(ff); } - void SetAutoCenter(FFDevice* ffdev, const autocenter& effect) + static void SetAutoCenter(FFDevice* ffdev, const autocenter& effect) { DevCon.WriteLn("%s: k1 %d k2 %d clip %d\n", __func__, effect.k1, effect.k2, effect.clip); ffdev->SetAutoCenter((effect.k1 * effect.clip / 255) * 100 / 255); // FIXME } // Unless passing ff packets straight to a device, parse it here - void Pad::ParseFFData(const ff_data* ffdata, bool isDFP) + void PadState::ParseFFData(const ff_data* ffdata, bool isDFP) { if (!mFFdev) return; DevCon.WriteLn("FFB %02X, %02X, %02X, %02X : %02X, %02X, %02X, %02X", - ffdata->cmdslot, ffdata->type, ffdata->u.params[0], ffdata->u.params[1], - ffdata->u.params[2], ffdata->u.params[3], ffdata->u.params[4], ffdata->padd0); + ffdata->cmdslot, ffdata->type, ffdata->u.params[0], ffdata->u.params[1], + ffdata->u.params[2], ffdata->u.params[3], ffdata->u.params[4], ffdata->padd0); if (ffdata->cmdslot != CMD_EXTENDED_CMD) { @@ -145,22 +144,22 @@ namespace usb_pad t++; force = (std::min)((std::max)(force + t - 128, -128), 127); } - SetConstantForce(mFFdev, 128 + force); + SetConstantForce(mFFdev.get(), 128 + force); } else { for (int i = 0; i < 4; i++) { if (slots == (1 << i)) - SetConstantForce(mFFdev, ffdata->u.params[i]); + SetConstantForce(mFFdev.get(), ffdata->u.params[i]); } } break; case FTYPE_SPRING: - SetSpringForce(mFFdev, ffdata->u.spring, isDFP ? 0 : FF_LG_CAPS_OLD_LOW_RES_COEF); + SetSpringForce(mFFdev.get(), ffdata->u.spring, isDFP ? 0 : FF_LG_CAPS_OLD_LOW_RES_COEF); break; case FTYPE_HIGH_RESOLUTION_SPRING: - SetSpringForce(mFFdev, ffdata->u.spring, FF_LG_CAPS_HIGH_RES_COEF | FF_LG_CAPS_HIGH_RES_DEADBAND); + SetSpringForce(mFFdev.get(), ffdata->u.spring, FF_LG_CAPS_HIGH_RES_COEF | FF_LG_CAPS_HIGH_RES_DEADBAND); break; case FTYPE_VARIABLE: //Ramp-like //SetRampVariable(mFFdev, ffdata->u.variable); @@ -172,13 +171,13 @@ namespace usb_pad if (warned == 0) { DevCon.WriteLn("variable force cannot be converted to constant force (l1=%hhu, t1=%hhu, s1=%hhu, d1=%hhu\n", - ffdata->u.variable.l1, ffdata->u.variable.t1, ffdata->u.variable.s1, ffdata->u.variable.d1); + ffdata->u.variable.l1, ffdata->u.variable.t1, ffdata->u.variable.s1, ffdata->u.variable.d1); warned = 1; } } else { - SetConstantForce(mFFdev, ffdata->u.variable.l1); + SetConstantForce(mFFdev.get(), ffdata->u.variable.l1); } } else if (slots & (1 << 2)) @@ -188,30 +187,30 @@ namespace usb_pad if (warned == 0) { DevCon.WriteLn("variable force cannot be converted to constant force (l2=%hhu, t2=%hhu, s2=%hhu, d2=%hhu\n", - ffdata->u.variable.l2, ffdata->u.variable.t2, ffdata->u.variable.s2, ffdata->u.variable.d2); + ffdata->u.variable.l2, ffdata->u.variable.t2, ffdata->u.variable.s2, ffdata->u.variable.d2); warned = 1; } } else { - SetConstantForce(mFFdev, ffdata->u.variable.l2); + SetConstantForce(mFFdev.get(), ffdata->u.variable.l2); } } break; case FTYPE_FRICTION: - SetFrictionForce(mFFdev, ffdata->u.friction); + SetFrictionForce(mFFdev.get(), ffdata->u.friction); break; case FTYPE_DAMPER: - SetDamperForce(mFFdev, ffdata->u.damper, 0); + SetDamperForce(mFFdev.get(), ffdata->u.damper, 0); break; case FTYPE_HIGH_RESOLUTION_DAMPER: caps = FF_LG_CAPS_HIGH_RES_COEF; if (isDFP) caps |= FF_LG_CAPS_DAMPER_CLIP; - SetDamperForce(mFFdev, ffdata->u.damper, caps); + SetDamperForce(mFFdev.get(), ffdata->u.damper, caps); break; case FTYPE_AUTO_CENTER_SPRING: - SetAutoCenter(mFFdev, ffdata->u.autocenter); + SetAutoCenter(mFFdev.get(), ffdata->u.autocenter); break; default: DevCon.WriteLn("CMD_DOWNLOAD_AND_PLAY: unhandled force type 0x%02X in slots 0x%02X\n", ffdata->type, slots); @@ -264,7 +263,7 @@ namespace usb_pad if (slots == 0x0F) { //just release force - SetConstantForce(mFFdev, 127); + SetConstantForce(mFFdev.get(), 127); } else { @@ -301,7 +300,7 @@ namespace usb_pad { } DevCon.WriteLn("CMD_EXTENDED: unhandled cmd 0x%02X%02X%02X\n", - ffdata->type, ffdata->u.params[0], ffdata->u.params[1]); + ffdata->type, ffdata->u.params[0], ffdata->u.params[1]); } } diff --git a/pcsx2/USB/usb-pad/usb-pad-sdl-ff.cpp b/pcsx2/USB/usb-pad/usb-pad-sdl-ff.cpp new file mode 100644 index 0000000000..c357233088 --- /dev/null +++ b/pcsx2/USB/usb-pad/usb-pad-sdl-ff.cpp @@ -0,0 +1,337 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2020 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "USB/usb-pad/usb-pad-sdl-ff.h" +#include "Frontend/InputManager.h" +#include "common/Console.h" +#include "fmt/format.h" +#include + +#ifdef SDL_BUILD + +namespace usb_pad +{ + SDLFFDevice::SDLFFDevice(SDL_Haptic* haptic) + : m_haptic(haptic) + { + } + + SDLFFDevice::~SDLFFDevice() + { + if (m_haptic) + { + DestroyEffects(); + + SDL_HapticClose(m_haptic); + m_haptic = nullptr; + } + } + + std::unique_ptr SDLFFDevice::Create(const std::string_view& device) + { + SDLInputSource* source = static_cast(InputManager::GetInputSourceInterface(InputSourceType::SDL)); + if (!source) + return nullptr; + + SDL_Joystick* joystick = source->GetJoystickForDevice(device); + if (!joystick) + { + Console.Error(fmt::format("No SDL_Joystick for {}. Cannot use FF.", device)); + return nullptr; + } + + SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick); + if (!haptic) + { + Console.Error(fmt::format("Haptic is not supported on {}.", device)); + return nullptr; + } + + std::unique_ptr ret(new SDLFFDevice(haptic)); + ret->CreateEffects(device); + return ret; + } + + void SDLFFDevice::CreateEffects(const std::string_view& device) + { + constexpr u32 length = 1000; // good enough? + + const unsigned int supported = SDL_HapticQuery(m_haptic); + if (supported & SDL_HAPTIC_CONSTANT) + { + m_constant_effect.type = SDL_HAPTIC_CONSTANT; + m_constant_effect.constant.direction.type = SDL_HAPTIC_CARTESIAN; + m_constant_effect.constant.length = length; + + m_constant_effect_id = SDL_HapticNewEffect(m_haptic, &m_constant_effect); + if (m_constant_effect_id < 0) + Console.Error("SDL_HapticNewEffect() for constant failed: %s", SDL_GetError()); + } + else + { + Console.Warning(fmt::format("(SDLFFDevice) Constant effect is not supported on '{}'", device)); + } + + if (supported & SDL_HAPTIC_SPRING) + { + m_spring_effect.type = SDL_HAPTIC_SPRING; + m_spring_effect.condition.direction.type = SDL_HAPTIC_CARTESIAN; + m_spring_effect.condition.length = length; + + m_spring_effect_id = SDL_HapticNewEffect(m_haptic, &m_spring_effect); + if (m_spring_effect_id < 0) + Console.Error("SDL_HapticNewEffect() for spring failed: %s", SDL_GetError()); + } + else + { + Console.Warning(fmt::format("(SDLFFDevice) Spring effect is not supported on '{}'", device)); + } + + if (supported & SDL_HAPTIC_DAMPER) + { + m_damper_effect.type = SDL_HAPTIC_DAMPER; + m_damper_effect.condition.direction.type = SDL_HAPTIC_CARTESIAN; + m_damper_effect.condition.length = length; + + m_damper_effect_id = SDL_HapticNewEffect(m_haptic, &m_damper_effect); + if (m_damper_effect_id < 0) + Console.Error("SDL_HapticNewEffect() for damper failed: %s", SDL_GetError()); + } + else + { + Console.Warning(fmt::format("(SDLFFDevice) Damper effect is not supported on '{}'", device)); + } + + if (supported & SDL_HAPTIC_FRICTION) + { + m_friction_effect.type = SDL_HAPTIC_FRICTION; + m_friction_effect.condition.direction.type = SDL_HAPTIC_CARTESIAN; + m_friction_effect.condition.length = length; + + m_friction_effect_id = SDL_HapticNewEffect(m_haptic, &m_friction_effect); + if (m_friction_effect_id < 0) + Console.Error("SDL_HapticNewEffect() for friction failed: %s", SDL_GetError()); + } + else + { + Console.Warning(fmt::format("(SDLFFDevice) Friction effect is not supported on '{}'", device)); + } + + m_autocenter_supported = (supported & SDL_HAPTIC_AUTOCENTER) != 0; + if (!m_autocenter_supported) + Console.Warning(fmt::format("(SDLFFDevice) Autocenter effect is not supported on '{}'", device)); + } + + void SDLFFDevice::DestroyEffects() + { + if (m_friction_effect_id >= 0) + { + if (m_friction_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_friction_effect_id); + m_friction_effect_running = false; + } + SDL_HapticDestroyEffect(m_haptic, m_friction_effect_id); + m_friction_effect_id = -1; + } + + if (m_damper_effect_id >= 0) + { + if (m_damper_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_damper_effect_id); + m_damper_effect_running = false; + } + SDL_HapticDestroyEffect(m_haptic, m_damper_effect_id); + m_damper_effect_id = -1; + } + + if (m_spring_effect_id >= 0) + { + if (m_spring_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_spring_effect_id); + m_spring_effect_running = false; + } + SDL_HapticDestroyEffect(m_haptic, m_spring_effect_id); + m_spring_effect_id = -1; + } + + if (m_constant_effect_id >= 0) + { + if (m_constant_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_constant_effect_id); + m_constant_effect_running = false; + } + SDL_HapticDestroyEffect(m_haptic, m_constant_effect_id); + m_constant_effect_id = -1; + } + } + + void SDLFFDevice::SetConstantForce(int level) + { + if (m_constant_effect_id < 0) + return; + + const s16 new_level = static_cast(std::clamp(level, -32768, 32767)); + if (m_constant_effect.constant.level != level) + { + m_constant_effect.constant.level = new_level; + if (SDL_HapticUpdateEffect(m_haptic, m_constant_effect_id, &m_constant_effect) != 0) + Console.Warning("SDL_HapticUpdateEffect() for constant failed: %s", SDL_GetError()); + } + + if (!m_constant_effect_running && SDL_HapticRunEffect(m_haptic, m_constant_effect_id, SDL_HAPTIC_INFINITY) == 0) + m_constant_effect_running = true; + else + Console.Error("SDL_HapticRunEffect() for constant failed: %s", SDL_GetError()); + } + + template + static u16 ClampU16(T val) + { + return static_cast(std::clamp(val, 0, 0xFFFF)); + } + + void SDLFFDevice::SetSpringForce(const parsed_ff_data& ff) + { + if (m_spring_effect_id < 0) + return; + + m_spring_effect.condition.left_sat[0] = ClampU16(ff.u.condition.left_saturation); + m_spring_effect.condition.left_coeff[0] = ClampU16(ff.u.condition.left_coeff); + m_spring_effect.condition.right_sat[0] = ClampU16(ff.u.condition.right_saturation); + m_spring_effect.condition.right_coeff[0] = ClampU16(ff.u.condition.right_coeff); + m_spring_effect.condition.deadband[0] = ClampU16(ff.u.condition.deadband); + m_spring_effect.condition.center[0] = ClampU16(ff.u.condition.center); + + if (SDL_HapticUpdateEffect(m_haptic, m_spring_effect_id, &m_spring_effect) != 0) + Console.Warning("SDL_HapticUpdateEffect() for spring failed: %s", SDL_GetError()); + + if (!m_spring_effect_running && SDL_HapticRunEffect(m_haptic, m_spring_effect_id, SDL_HAPTIC_INFINITY) == 0) + m_spring_effect_running = true; + else + Console.Warning("SDL_HapticRunEffect() for spring failed: %s", SDL_GetError()); + } + + void SDLFFDevice::SetDamperForce(const parsed_ff_data& ff) + { + if (m_damper_effect_id < 0) + return; + + m_damper_effect.condition.left_sat[0] = ClampU16(ff.u.condition.left_saturation); + m_damper_effect.condition.left_coeff[0] = ClampU16(ff.u.condition.left_coeff); + m_damper_effect.condition.right_sat[0] = ClampU16(ff.u.condition.right_saturation); + m_damper_effect.condition.right_coeff[0] = ClampU16(ff.u.condition.right_coeff); + m_damper_effect.condition.deadband[0] = ClampU16(ff.u.condition.deadband); + m_damper_effect.condition.center[0] = ClampU16(ff.u.condition.center); + + if (SDL_HapticUpdateEffect(m_haptic, m_damper_effect_id, &m_damper_effect) != 0) + Console.Warning("SDL_HapticUpdateEffect() for damper failed: %s", SDL_GetError()); + + if (!m_damper_effect_running && SDL_HapticRunEffect(m_haptic, m_damper_effect_id, SDL_HAPTIC_INFINITY) == 0) + m_damper_effect_running = true; + else + Console.Warning("SDL_HapticRunEffect() for damper failed: %s", SDL_GetError()); + } + + void SDLFFDevice::SetFrictionForce(const parsed_ff_data& ff) + { + if (m_friction_effect_id < 0) + return; + + m_friction_effect.condition.left_sat[0] = ClampU16(ff.u.condition.left_saturation); + m_friction_effect.condition.left_coeff[0] = ClampU16(ff.u.condition.left_coeff); + m_friction_effect.condition.right_sat[0] = ClampU16(ff.u.condition.right_saturation); + m_friction_effect.condition.right_coeff[0] = ClampU16(ff.u.condition.right_coeff); + m_friction_effect.condition.deadband[0] = ClampU16(ff.u.condition.deadband); + m_friction_effect.condition.center[0] = ClampU16(ff.u.condition.center); + + if (SDL_HapticUpdateEffect(m_haptic, m_friction_effect_id, &m_friction_effect) != 0) + Console.Warning("SDL_HapticUpdateEffect() for friction failed: %s", SDL_GetError()); + + if (!m_friction_effect_running && SDL_HapticRunEffect(m_haptic, m_friction_effect_id, SDL_HAPTIC_INFINITY) == 0) + m_friction_effect_running = true; + else + Console.Warning("SDL_HapticRunEffect() for friction failed: %s", SDL_GetError()); + } + + void SDLFFDevice::SetAutoCenter(int value) + { + if (m_autocenter_supported) + { + if (SDL_HapticSetAutocenter(m_haptic, value) != 0) + Console.Warning("SDL_HapticSetAutocenter() failed: %s", SDL_GetError()); + } + } + + void SDLFFDevice::DisableForce(EffectID force) + { + switch (force) + { + case EFF_CONSTANT: + { + if (m_constant_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_constant_effect_id); + m_constant_effect_running = false; + } + } + break; + + case EFF_SPRING: + { + if (m_spring_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_spring_effect_id); + m_spring_effect_running = false; + } + } + break; + + case EFF_DAMPER: + { + if (m_damper_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_damper_effect_id); + m_damper_effect_running = false; + } + } + break; + + case EFF_FRICTION: + { + if (m_friction_effect_running) + { + SDL_HapticStopEffect(m_haptic, m_friction_effect_id); + m_friction_effect_running = false; + } + } + break; + + case EFF_RUMBLE: + // Not implemented? + break; + + default: + break; + } + } + +} // namespace usb_pad + +#endif \ No newline at end of file diff --git a/pcsx2/USB/usb-pad/usb-pad-sdl-ff.h b/pcsx2/USB/usb-pad/usb-pad-sdl-ff.h new file mode 100644 index 0000000000..4b3fc8edd8 --- /dev/null +++ b/pcsx2/USB/usb-pad/usb-pad-sdl-ff.h @@ -0,0 +1,68 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "USB/usb-pad/usb-pad.h" + +#ifdef SDL_BUILD + +#include "Frontend/SDLInputSource.h" + +namespace usb_pad +{ + class SDLFFDevice : public FFDevice + { + public: + ~SDLFFDevice() override; + + static std::unique_ptr Create(const std::string_view& device); + + void SetConstantForce(int level) override; + void SetSpringForce(const parsed_ff_data& ff) override; + void SetDamperForce(const parsed_ff_data& ff) override; + void SetFrictionForce(const parsed_ff_data& ff) override; + void SetAutoCenter(int value) override; + void DisableForce(EffectID force) override; + + private: + SDLFFDevice(SDL_Haptic* haptic); + + void CreateEffects(const std::string_view& device); + void DestroyEffects(); + + SDL_Haptic* m_haptic = nullptr; + + SDL_HapticEffect m_constant_effect = {}; + int m_constant_effect_id = -1; + bool m_constant_effect_running = false; + + SDL_HapticEffect m_spring_effect = {}; + int m_spring_effect_id = -1; + bool m_spring_effect_running = false; + + SDL_HapticEffect m_damper_effect = {}; + int m_damper_effect_id = -1; + bool m_damper_effect_running = false; + + SDL_HapticEffect m_friction_effect = {}; + int m_friction_effect_id = -1; + bool m_friction_effect_running = false; + + bool m_autocenter_supported = false; + }; +} // namespace usb_pad + +#endif diff --git a/pcsx2/USB/usb-pad/usb-pad.cpp b/pcsx2/USB/usb-pad/usb-pad.cpp index 94ce73bdf8..c0eae6ea35 100644 --- a/pcsx2/USB/usb-pad/usb-pad.cpp +++ b/pcsx2/USB/usb-pad/usb-pad.cpp @@ -14,14 +14,17 @@ */ #include "PrecompiledHeader.h" -#include "padproxy.h" #include "usb-pad.h" -#include "USB/qemu-usb/desc.h" -#include "USB/shared/inifile_usb.h" +#include "USB/qemu-usb/USBinternal.h" +#include "USB/USB.h" +#include "StateWrapper.h" + +#ifdef SDL_BUILD +#include "USB/usb-pad/usb-pad-sdl-ff.h" +#endif namespace usb_pad { - static const USBDescStrings df_desc_strings = { "", "Logitech Driving Force", @@ -38,7 +41,7 @@ namespace usb_pad static const USBDescStrings gtf_desc_strings = { "", - "Logitech", //actual index @ 0x04 + "Logitech", //actual index @ 0x04 "Logitech GT Force" //actual index @ 0x20 }; @@ -59,103 +62,518 @@ namespace usb_pad "", "KONAMI"}; - static const USBDescStrings gametrak_desc_strings = { - "", - "In2Games Ltd.", - "Game-Trak V1.3"}; - - static const USBDescStrings realplay_desc_strings = { - "", - "In2Games", - "Real Play"}; - - std::list PadDevice::ListAPIs() + static gsl::span GetWheelBindings(PS2WheelTypes wt) { - return RegisterPad::instance().Names(); - } - - const TCHAR* PadDevice::LongAPIName(const std::string& name) - { - auto proxy = RegisterPad::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } - - std::list RBDrumKitDevice::ListAPIs() - { - return PadDevice::ListAPIs(); - } - - const TCHAR* RBDrumKitDevice::LongAPIName(const std::string& name) - { - return PadDevice::LongAPIName(name); - } - - std::list BuzzDevice::ListAPIs() - { - return PadDevice::ListAPIs(); - } - - const TCHAR* BuzzDevice::LongAPIName(const std::string& name) - { - return PadDevice::LongAPIName(name); - } - - std::list KeyboardmaniaDevice::ListAPIs() - { - return PadDevice::ListAPIs(); - } - - const TCHAR* KeyboardmaniaDevice::LongAPIName(const std::string& name) - { - return PadDevice::LongAPIName(name); - } - - std::list GametrakDevice::ListAPIs() - { - return PadDevice::ListAPIs(); - } - - const TCHAR* GametrakDevice::LongAPIName(const std::string& name) - { - return PadDevice::LongAPIName(name); - } - - std::list RealPlayDevice::ListAPIs() - { - return PadDevice::ListAPIs(); - } - - const TCHAR* RealPlayDevice::LongAPIName(const std::string& name) - { - return PadDevice::LongAPIName(name); - } - -#ifdef _DEBUG - void PrintBits(void* data, int size) - { - std::vector buf(size * 8 + 1 + size); - unsigned char* bits = buf.data(); - unsigned char* ptrD = (unsigned char*)data; - unsigned char* ptrB = bits; - for (int i = 0; i < size * 8; i++) + switch (wt) { - *(ptrB++) = '0' + (*(ptrD + i / 8) & (1 << (i % 8)) ? 1 : 0); - if (i % 8 == 7) - *(ptrB++) = ' '; + case WT_GENERIC: + { + static constexpr const InputBindingInfo bindings[] = { + {"SteeringLeft", "Steering Left", InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"SteeringRight", "Steering Right", InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"Throttle", "Throttle", InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, + {"Brake", "Brake", InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, + {"DPadUp", "D-Pad Up", InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", "D-Pad Down", InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", "D-Pad Left", InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", "D-Pad Right", InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, + {"Cross", "Cross", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, + {"Square", "Square", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, + {"Circle", "Circle", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Triangle", "Triangle", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"L1", "L1", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L1}, + {"R1", "R1", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, + {"L2", "L2", InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, // used L2 for brake + {"R2", "R2", InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, // used R2 for throttle + {"Select", "Select", InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", "Start", InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + {"FFDevice", "Force Feedback", InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, + }; + + return bindings; + } + + case WT_DRIVING_FORCE_PRO: + case WT_DRIVING_FORCE_PRO_1102: + { + static constexpr const InputBindingInfo bindings[] = { + {"SteeringLeft", "Steering Left", InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"SteeringRight", "Steering Right", InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"Throttle", "Throttle", InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, + {"Brake", "Brake", InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, + {"DPadUp", "D-Pad Up", InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", "D-Pad Down", InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", "D-Pad Left", InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", "D-Pad Right", InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, + {"Cross", "Cross", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, + {"Square", "Square", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, + {"Circle", "Circle", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Triangle", "Triangle", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"R1", "Shift Up / R1", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::R1}, + {"L1", "Shift Down / L1", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L1}, + {"Select", "Select", InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", "Start", InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + {"L2", "L2", InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, // used L2 for brake + {"R2", "R2", InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, // used R2 for throttle + {"L3", "L3", InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::L3}, + {"R3", "R3", InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::R3}, + {"FFDevice", "Force Feedback", InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, + }; + + return bindings; + } + + case WT_GT_FORCE: + { + static constexpr const InputBindingInfo bindings[] = { + {"SteeringLeft", "Steering Left", InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"SteeringRight", "Steering Right", InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"Throttle", "Throttle", InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::R2}, + {"Brake", "Brake", InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::L2}, + {"MenuUp", "Menu Up", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::DPadUp}, + {"MenuDown", "Menu Down", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::DPadDown}, + {"X", "X", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Square}, + {"Y", "Y", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"A", "A", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, + {"B", "B", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Circle}, + {"FFDevice", "Force Feedback", InputBindingInfo::Type::Device, 0, GenericInputBinding::Unknown}, + }; + + return bindings; + } + + default: + { + return {}; + } } - *ptrB = '\0'; } -#else -#define PrintBits(...) -#define DbgPrint(...) -#endif //_DEBUG - - uint32_t gametrak_compute_key(uint32_t* key) + static gsl::span GetWheelSettings(PS2WheelTypes wt) { - uint32_t ret = 0; + if (wt <= WT_GT_FORCE) + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::Integer, "SteeringSmoothing", "Steering Smoothing", + "Smooths out changes in steering to the specified percentage per poll. Needed for using keyboards.", + "0", "0", "100", "1", "%d%%", nullptr, nullptr, 1.0f}, + }; + + return info; + } + else + { + return {}; + } + } + + PadState::PadState(u32 port_, PS2WheelTypes type_) + : port(port_) + , type(type_) + { + if (type_ == WT_DRIVING_FORCE_PRO || type_ == WT_DRIVING_FORCE_PRO_1102) + steering_range = 0x3FFF >> 1; + else if (type_ == WT_SEGA_SEAMIC) + steering_range = 0xFF >> 1; + else + steering_range = 0x3FF >> 1; + + steering_step = std::numeric_limits::max(); + + // steering starts in the center + data.last_steering = steering_range; + data.steering = steering_range; + + // throttle/brake start unpressed + data.throttle = 255; + data.brake = 255; + + Reset(); + } + + PadState::~PadState() = default; + + void PadState::UpdateSettings(SettingsInterface& si, const char* devname) + { + const s32 smoothing_percent = USB::GetConfigInt(si, port, devname, "SteeringSmoothing", 0); + if (smoothing_percent <= 0) + { + // none, allow any amount of change + steering_step = std::numeric_limits::max(); + } + else + { + steering_step = static_cast(std::clamp((steering_range * smoothing_percent) / 100, + 1, std::numeric_limits::max())); + } + + if (HasFF()) + { + const std::string ffdevname(USB::GetConfigString(si, port, devname, "FFDevice")); + if (ffdevname != mFFdevName) + { + mFFdev.reset(); + mFFdevName = std::move(ffdevname); + OpenFFDevice(); + } + } + } + + void PadState::Reset() + { + data.steering = steering_range; + mFFstate = {}; + } + + int PadState::TokenIn(u8* buf, int len) + { + // TODO: This is still pretty gross and needs cleaning up. + + struct wheel_lohi + { + u32 lo; + u32 hi; + }; + + wheel_lohi* w = reinterpret_cast(buf); + std::memset(w, 0, 8); + + switch (type) + { + case WT_GENERIC: + { + UpdateSteering(); + UpdateHatSwitch(); + + DbgCon.WriteLn("Steering: %d Throttle: %d Brake: %d Buttons: %d", + data.steering, data.throttle, data.brake, data.buttons); + + w->lo = data.steering & 0x3FF; + w->lo |= (data.buttons & 0xFFF) << 10; + w->lo |= 0xFF << 24; + + w->hi = (data.hatswitch & 0xF); + w->hi |= (data.throttle & 0xFF) << 8; + w->hi |= (data.brake & 0xFF) << 16; + + return len; + } + + case WT_GT_FORCE: + { + UpdateSteering(); + UpdateHatSwitch(); + + w->lo = data.steering & 0x3FF; + w->lo |= (data.buttons & 0xFFF) << 10; + w->lo |= 0xFF << 24; + + w->hi = (data.throttle & 0xFF); + w->hi |= (data.brake & 0xFF) << 8; + + return len; + } + + case WT_DRIVING_FORCE_PRO: + { + UpdateSteering(); + UpdateHatSwitch(); + + w->lo = data.steering & 0x3FFF; + w->lo |= (data.buttons & 0x3FFF) << 14; + w->lo |= (data.hatswitch & 0xF) << 28; + + w->hi = 0x00; + w->hi |= data.throttle << 8; + w->hi |= data.brake << 16; //axis_rz + w->hi |= 0x11 << 24; //enables wheel and pedals? + + return len; + } + + case WT_DRIVING_FORCE_PRO_1102: + { + UpdateSteering(); + UpdateHatSwitch(); + + // what's up with the bitmap? + // xxxxxxxx xxxxxxbb bbbbbbbb bbbbhhhh ???????? ?01zzzzz 1rrrrrr1 10001000 + w->lo = data.steering & 0x3FFF; + w->lo |= (data.buttons & 0x3FFF) << 14; + w->lo |= (data.hatswitch & 0xF) << 28; + + w->hi = 0x00; + //w->hi |= 0 << 9; //bit 9 must be 0 + w->hi |= (1 | (data.throttle * 0x3F) / 0xFF) << 10; //axis_z + w->hi |= 1 << 16; //bit 16 must be 1 + w->hi |= ((0x3F - (data.brake * 0x3F) / 0xFF) & 0x3F) << 17; //axis_rz + w->hi |= 1 << 23; //bit 23 must be 1 + w->hi |= 0x11 << 24; //enables wheel and pedals? + + return len; + } + + case WT_ROCKBAND1_DRUMKIT: + { + UpdateHatSwitch(); + + w->lo = (data.buttons & 0xFFF); + w->lo |= (data.hatswitch & 0xF) << 16; + + return len; + } + + case WT_BUZZ_CONTROLLER: + { + // https://gist.github.com/Lewiscowles1986/eef220dac6f0549e4702393a7b9351f6 + buf[0] = 0x7f; + buf[1] = 0x7f; + buf[2] = data.buttons & 0xff; + buf[3] = (data.buttons >> 8) & 0xff; + buf[4] = 0xf0 | ((data.buttons >> 16) & 0xf); + + return 5; + } + + case WT_SEGA_SEAMIC: + { + UpdateSteering(); + UpdateHatSwitch(); + + buf[0] = data.steering & 0xFF; + buf[1] = data.throttle & 0xFF; + buf[2] = data.brake & 0xFF; + buf[3] = data.hatswitch & 0x0F; // 4bits? + buf[3] |= (data.buttons & 0x0F) << 4; // 4 bits // TODO Or does it start at buf[4]? + buf[4] = (data.buttons >> 4) & 0x3F; // 10 - 4 = 6 bits + + return len; + } + + case WT_KEYBOARDMANIA_CONTROLLER: + { + buf[0] = 0x3F; + buf[1] = data.buttons & 0xFF; + buf[2] = (data.buttons >> 8) & 0xFF; + buf[3] = (data.buttons >> 16) & 0xFF; + buf[4] = (data.buttons >> 24) & 0xFF; + + return len; + } + + default: + { + return len; + } + } + } + + int PadState::TokenOut(const u8* data, int len) + { + const ff_data* ffdata = reinterpret_cast(data); + bool hires = (type == WT_DRIVING_FORCE_PRO || type == WT_DRIVING_FORCE_PRO_1102); + ParseFFData(ffdata, hires); + return len; + } + + float PadState::GetBindValue(u32 bind_index) const + { + switch (bind_index) + { + case CID_STEERING_L: + return static_cast(data.steering_left) / static_cast(steering_range); + + case CID_STEERING_R: + return static_cast(data.steering_right) / static_cast(steering_range); + + case CID_THROTTLE: + return 1.0f - (static_cast(data.throttle) / 255.0f); + + case CID_BRAKE: + return 1.0f - (static_cast(data.brake) / 255.0f); + + case CID_DPAD_UP: + return static_cast(data.hat_up); + + case CID_DPAD_DOWN: + return static_cast(data.hat_down); + + case CID_DPAD_LEFT: + return static_cast(data.hat_left); + + case CID_DPAD_RIGHT: + return static_cast(data.hat_right); + + case CID_BUTTON0: + case CID_BUTTON1: + case CID_BUTTON2: + case CID_BUTTON3: + case CID_BUTTON5: + case CID_BUTTON4: + case CID_BUTTON7: + case CID_BUTTON6: + case CID_BUTTON8: + case CID_BUTTON9: + case CID_BUTTON10: + case CID_BUTTON11: + case CID_BUTTON12: + case CID_BUTTON13: + case CID_BUTTON14: + case CID_BUTTON15: + case CID_BUTTON16: + case CID_BUTTON17: + case CID_BUTTON18: + case CID_BUTTON19: + case CID_BUTTON20: + case CID_BUTTON21: + case CID_BUTTON22: + case CID_BUTTON23: + case CID_BUTTON24: + { + const u32 mask = (1u << (bind_index - CID_BUTTON0)); + return ((data.buttons & mask) != 0u) ? 1.0f : 0.0f; + } + + default: + return 0.0f; + } + } + + void PadState::SetBindValue(u32 bind_index, float value) + { + switch (bind_index) + { + case CID_STEERING_L: + data.steering_left = static_cast(std::lroundf(value * static_cast(steering_range))); + UpdateSteering(); + break; + + case CID_STEERING_R: + data.steering_right = static_cast(std::lroundf(value * static_cast(steering_range))); + UpdateSteering(); + break; + + case CID_THROTTLE: + data.throttle = static_cast(255 - std::clamp(std::lroundf(value * 255.0f), 0, 255)); + break; + + case CID_BRAKE: + data.brake = static_cast(255 - std::clamp(std::lroundf(value * 255.0f), 0, 255)); + break; + + case CID_DPAD_UP: + data.hat_up = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + UpdateHatSwitch(); + break; + case CID_DPAD_DOWN: + data.hat_down = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + UpdateHatSwitch(); + break; + case CID_DPAD_LEFT: + data.hat_left = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + UpdateHatSwitch(); + break; + case CID_DPAD_RIGHT: + data.hat_right = static_cast(std::clamp(std::lroundf(value * 255.0f), 0, 255)); + UpdateHatSwitch(); + break; + + case CID_BUTTON0: + case CID_BUTTON1: + case CID_BUTTON2: + case CID_BUTTON3: + case CID_BUTTON5: + case CID_BUTTON4: + case CID_BUTTON7: + case CID_BUTTON6: + case CID_BUTTON8: + case CID_BUTTON9: + case CID_BUTTON10: + case CID_BUTTON11: + case CID_BUTTON12: + case CID_BUTTON13: + case CID_BUTTON14: + case CID_BUTTON15: + case CID_BUTTON16: + case CID_BUTTON17: + case CID_BUTTON18: + case CID_BUTTON19: + case CID_BUTTON20: + case CID_BUTTON21: + case CID_BUTTON22: + { + const u32 mask = (1u << (bind_index - CID_BUTTON0)); + if (value >= 0.5f) + data.buttons |= mask; + else + data.buttons &= ~mask; + } + break; + + default: + break; + } + } + + void PadState::UpdateSteering() + { + u16 value; + if (data.steering_left > 0) + value = static_cast(std::max(steering_range - data.steering_left, 0)); + else + value = static_cast(std::min(steering_range + data.steering_right, steering_range * 2)); + + // TODO: Smoothing, don't jump too much + //data.steering = value; + if (value < data.steering) + data.steering -= std::min(data.steering - value, steering_step); + else if (value > data.steering) + data.steering += std::min(value - data.steering, steering_step); + } + + void PadState::UpdateHatSwitch() + { + if (data.hat_up && data.hat_right) + data.hatswitch = 1; + else if (data.hat_right && data.hat_down) + data.hatswitch = 3; + else if (data.hat_down && data.hat_left) + data.hatswitch = 5; + else if (data.hat_left && data.hat_up) + data.hatswitch = 7; + else if (data.hat_up) + data.hatswitch = 0; + else if (data.hat_right) + data.hatswitch = 2; + else if (data.hat_down) + data.hatswitch = 4; + else if (data.hat_left) + data.hatswitch = 6; + else + data.hatswitch = 8; + } + + bool PadState::HasFF() const + { + // only do force feedback for wheels... + return (type <= WT_GT_FORCE); + } + + void PadState::OpenFFDevice() + { + if (mFFdevName.empty()) + return; + + mFFdev.reset(); + +#ifdef SDL_BUILD + mFFdev = SDLFFDevice::Create(mFFdevName); +#endif + } + + static u32 gametrak_compute_key(u32* key) + { + u32 ret = 0; ret = *key << 2 & 0xFC0000; ret |= *key << 17 & 0x020000; ret ^= *key << 16 & 0xFE0000; @@ -164,70 +582,11 @@ namespace usb_pad ret |= *key << 7 & 0x008080; *key = ret; return ret >> 16; - }; - - typedef struct PADState - { - USBDevice dev; - USBDesc desc; - USBDescDevice desc_dev; - Pad* pad; - uint8_t port; - struct freeze - { - int dev_subtype; - } f; - uint8_t gametrak_state; - uint32_t gametrak_key; - uint8_t realplay_state; - } PADState; - - typedef struct u_wheel_data_t - { - union - { - generic_data_t generic_data; - dfp_data_t dfp_data; - gtforce_data_t gtf_data; - rb1drumkit_t rb1dk_data; - } u; - } u_wheel_data_t; - - //Convert DF Pro buttons to selected wheel type - uint32_t convert_wt_btn(PS2WheelTypes type, uint32_t inBtn) - { - if (type == WT_GT_FORCE) - { - /*** - R1 > SQUARE == menu down L1 > CROSS == menu up - SQUARE > CIRCLE == X TRIANG > TRIANG == Y - CROSS > R1 == A CIRCLE > L1 == B - ***/ - switch (inBtn) - { - case PAD_L1: - return PAD_CROSS; - case PAD_R1: - return PAD_SQUARE; - case PAD_SQUARE: - return PAD_CIRCLE; - case PAD_TRIANGLE: - return PAD_TRIANGLE; - case PAD_CIRCLE: - return PAD_L1; - case PAD_CROSS: - return PAD_R1; - default: - return PAD_BUTTON_COUNT; //Aka invalid - } - } - - return inBtn; } static void pad_handle_data(USBDevice* dev, USBPacket* p) { - PADState* s = (PADState*)dev; + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); uint8_t data[64]; int ret = 0; @@ -236,43 +595,12 @@ namespace usb_pad switch (p->pid) { case USB_TOKEN_IN: - if (devep == 1 && s->pad) + if (devep == 1) { - if (s->pad->Type() == WT_GAMETRAK_CONTROLLER) - { - if (s->gametrak_state == 0) - { - s->gametrak_state = 1; - const unsigned char secret[] = "Gametrak\0\0\0\0\0\0\0\0"; - memcpy(data, secret, sizeof(secret)); - usb_packet_copy(p, data, 16); - break; - } - else if (s->gametrak_state == 1) - { - s->pad->TokenIn(data, p->iov.size); - data[0x00] |= s->gametrak_key >> 16 & 1; - data[0x02] |= s->gametrak_key >> 17 & 1; - data[0x04] |= s->gametrak_key >> 18 & 1; - data[0x06] |= s->gametrak_key >> 19 & 1; - data[0x08] |= s->gametrak_key >> 20 & 1; - data[0x0A] |= s->gametrak_key >> 21 & 1; - usb_packet_copy(p, data, 16); - break; - } - } - else if (s->pad->Type() >= WT_REALPLAY_RACING && s->pad->Type() <= WT_REALPLAY_POOL) - { - s->pad->TokenIn(data, p->iov.size); - // simulate a slight move to avoid a game "protection" : controller disconnected - s->realplay_state = !s->realplay_state; - data[0] |= s->realplay_state; - usb_packet_copy(p, data, 19); - break; - } - ret = s->pad->TokenIn(data, p->iov.size); + ret = s->TokenIn(data, p->iov.size); + if (ret > 0) - usb_packet_copy(p, data, MIN(ret, (int)sizeof(data))); + usb_packet_copy(p, data, std::min(ret, (int)sizeof(data))); else p->status = ret; } @@ -282,11 +610,11 @@ namespace usb_pad } break; case USB_TOKEN_OUT: - usb_packet_copy(p, data, MIN(p->iov.size, sizeof(data))); + usb_packet_copy(p, data, std::min(p->iov.size, sizeof(data))); /*Console.Warning("usb-pad: data token out len=0x%X %X,%X,%X,%X,%X,%X,%X,%X\n",len, data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]);*/ //Console.Warning("usb-pad: data token out len=0x%X\n",len); - ret = s->pad->TokenOut(data, p->iov.size); + ret = s->TokenOut(data, p->iov.size); break; default: fail: @@ -297,20 +625,16 @@ namespace usb_pad static void pad_handle_reset(USBDevice* dev) { - /* XXX: do it */ - PADState* s = (PADState*)dev; - s->pad->Reset(); - return; + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); + s->Reset(); } static void pad_handle_control(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + int index, int length, uint8_t* data) { - PADState* s = (PADState*)dev; + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); int ret = 0; - int t = s->pad->Type(); - switch (request) { case DeviceRequest | USB_REQ_GET_DESCRIPTOR: @@ -322,74 +646,42 @@ namespace usb_pad case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: //GT3 switch (value >> 8) { + // TODO: Move to constructor case USB_DT_REPORT: - if (t == WT_DRIVING_FORCE_PRO || t == WT_DRIVING_FORCE_PRO_1102) + if (s->type == WT_DRIVING_FORCE_PRO || s->type == WT_DRIVING_FORCE_PRO_1102) { ret = sizeof(pad_driving_force_pro_hid_report_descriptor); memcpy(data, pad_driving_force_pro_hid_report_descriptor, ret); } - else if (t == WT_GT_FORCE) + else if (s->type == WT_GT_FORCE) { ret = sizeof(pad_gtforce_hid_report_descriptor); memcpy(data, pad_gtforce_hid_report_descriptor, ret); } - else if (t == WT_KEYBOARDMANIA_CONTROLLER) + else if (s->type == WT_KEYBOARDMANIA_CONTROLLER) { ret = sizeof(kbm_hid_report_descriptor); memcpy(data, kbm_hid_report_descriptor, ret); } - else if (t == WT_GENERIC) + else if (s->type == WT_GENERIC) { ret = sizeof(pad_driving_force_hid_separate_report_descriptor); memcpy(data, pad_driving_force_hid_separate_report_descriptor, ret); } - else if (t == WT_BUZZ_CONTROLLER) + else if (s->type == WT_BUZZ_CONTROLLER) { ret = sizeof(buzz_hid_report_descriptor); memcpy(data, buzz_hid_report_descriptor, ret); } - else if (t == WT_GAMETRAK_CONTROLLER) - { - ret = sizeof(gametrak_hid_report_descriptor); - memcpy(data, gametrak_hid_report_descriptor, ret); - } - else if (t >= WT_REALPLAY_RACING && t <= WT_REALPLAY_POOL) - { - ret = sizeof(realplay_hid_report_descriptor); - memcpy(data, realplay_hid_report_descriptor, ret); - } p->actual_length = ret; break; default: goto fail; } break; + /* hid specific requests */ case SET_REPORT: - if (t == WT_GAMETRAK_CONTROLLER) - { - const char secret[] = "Gametrak"; - if (length == 8 && memcmp(data, secret, 8) == 0) - { - s->gametrak_state = 0; - s->gametrak_key = 0; - } - else if (length == 2) - { - if (data[0] == 0x45) - { - s->gametrak_key = data[1] << 16; - } - if ((s->gametrak_key >> 16) == data[1]) - { - gametrak_compute_key(&s->gametrak_key); - } - else - { - fprintf(stderr, "gametrak error : own key = %02x, recv key = %02x\n", s->gametrak_key >> 16, data[1]); - } - } - } // no idea, Rock Band 2 keeps spamming this if (length > 0) { @@ -418,324 +710,32 @@ namespace usb_pad static void pad_handle_destroy(USBDevice* dev) { - PADState* s = (PADState*)dev; + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); delete s; } - int pad_open(USBDevice* dev) + static void pad_init(PadState* s) { - PADState* s = (PADState*)dev; - if (s) - return s->pad->Open(); - return 1; - } - - void pad_close(USBDevice* dev) - { - PADState* s = (PADState*)dev; - if (s) - s->pad->Close(); - } - - void pad_reset_data(generic_data_t* d) - { - memset(d, 0, sizeof(generic_data_t)); - d->axis_x = 0x3FF >> 1; - d->axis_y = 0xFF; - d->axis_z = 0xFF; - d->axis_rz = 0xFF; - } - - void pad_reset_data(dfp_data_t* d) - { - memset(d, 0, sizeof(dfp_data_t)); - d->axis_x = 0x3FFF >> 1; - //d->axis_y = 0xFF; - d->axis_z = 0x3F; - d->axis_rz = 0x3F; - } - - void pad_copy_data(PS2WheelTypes type, uint8_t* buf, wheel_data_t& data) - { -#if 1 - struct wheel_data_t - { - uint32_t lo; - uint32_t hi; - }; - - wheel_data_t* w = (wheel_data_t*)buf; - memset(w, 0, 8); - - switch (type) - { - case WT_GENERIC: - w->lo = data.steering & 0x3FF; - w->lo |= (data.buttons & 0xFFF) << 10; - w->lo |= 0xFF << 24; - - w->hi = (data.hatswitch & 0xF); - w->hi |= (data.throttle & 0xFF) << 8; - w->hi |= (data.brake & 0xFF) << 16; - - break; - - case WT_GT_FORCE: - - w->lo = data.steering & 0x3FF; - w->lo |= (data.buttons & 0xFFF) << 10; - w->lo |= 0xFF << 24; - - w->hi = (data.throttle & 0xFF); - w->hi |= (data.brake & 0xFF) << 8; - - break; - case WT_DRIVING_FORCE_PRO: - - w->lo = data.steering & 0x3FFF; - w->lo |= (data.buttons & 0x3FFF) << 14; - w->lo |= (data.hatswitch & 0xF) << 28; - - w->hi = 0x00; - w->hi |= data.throttle << 8; - w->hi |= data.brake << 16; //axis_rz - w->hi |= 0x11 << 24; //enables wheel and pedals? - - //PrintBits(w, sizeof(*w)); - - break; - case WT_DRIVING_FORCE_PRO_1102: - - // what's up with the bitmap? - // xxxxxxxx xxxxxxbb bbbbbbbb bbbbhhhh ???????? ?01zzzzz 1rrrrrr1 10001000 - w->lo = data.steering & 0x3FFF; - w->lo |= (data.buttons & 0x3FFF) << 14; - w->lo |= (data.hatswitch & 0xF) << 28; - - w->hi = 0x00; - //w->hi |= 0 << 9; //bit 9 must be 0 - w->hi |= (1 | (data.throttle * 0x3F) / 0xFF) << 10; //axis_z - w->hi |= 1 << 16; //bit 16 must be 1 - w->hi |= ((0x3F - (data.brake * 0x3F) / 0xFF) & 0x3F) << 17; //axis_rz - w->hi |= 1 << 23; //bit 23 must be 1 - w->hi |= 0x11 << 24; //enables wheel and pedals? - - //PrintBits(w, sizeof(*w)); - - break; - case WT_ROCKBAND1_DRUMKIT: - w->lo = (data.buttons & 0xFFF); - w->lo |= (data.hatswitch & 0xF) << 16; - break; - - case WT_BUZZ_CONTROLLER: - // https://gist.github.com/Lewiscowles1986/eef220dac6f0549e4702393a7b9351f6 - buf[0] = 0x7f; - buf[1] = 0x7f; - buf[2] = data.buttons & 0xff; - buf[3] = (data.buttons >> 8) & 0xff; - buf[4] = 0xf0 | ((data.buttons >> 16) & 0xf); - break; - - case WT_GAMETRAK_CONTROLLER: - memset(buf, 0, 16); - buf[0] = data.clutch & 0xfe; - buf[1] = data.clutch >> 8; - buf[2] = data.throttle & 0xfe; - buf[3] = data.throttle >> 8; - buf[4] = data.brake & 0xfe; - buf[5] = data.brake >> 8; - buf[6] = data.hatswitch & 0xfe; - buf[7] = data.hatswitch >> 8; - buf[8] = data.hat_horz & 0xfe; - buf[9] = data.hat_horz >> 8; - buf[10] = data.hat_vert & 0xfe; - buf[11] = data.hat_vert >> 8; - buf[12] = data.buttons; - break; - - case WT_REALPLAY_RACING: - case WT_REALPLAY_SPHERE: - case WT_REALPLAY_GOLF: - case WT_REALPLAY_POOL: - memset(buf, 0, 19); - buf[0] = data.clutch & 0xfe; - buf[1] = data.clutch >> 8; - buf[2] = data.throttle & 0xff; - buf[3] = data.throttle >> 8; - buf[4] = data.brake & 0xff; - buf[5] = data.brake >> 8; - buf[14] = data.buttons; - break; - - case WT_SEGA_SEAMIC: - buf[0] = data.steering & 0xFF; - buf[1] = data.throttle & 0xFF; - buf[2] = data.brake & 0xFF; - buf[3] = data.hatswitch & 0x0F; // 4bits? - buf[3] |= (data.buttons & 0x0F) << 4; // 4 bits // TODO Or does it start at buf[4]? - buf[4] = (data.buttons >> 4) & 0x3F; // 10 - 4 = 6 bits - break; - - case WT_KEYBOARDMANIA_CONTROLLER: - buf[0] = 0x3F; - buf[1] = data.buttons & 0xFF; - buf[2] = (data.buttons >> 8) & 0xFF; - buf[3] = (data.buttons >> 16) & 0xFF; - buf[4] = (data.buttons >> 24) & 0xFF; - break; - - default: - break; - } - -#else - - u_wheel_data_t* w = (u_wheel_data_t*)buf; - - //Console.Warning("usb-pad: axis x %d\n", data.axis_x); - switch (type) - { - case WT_GENERIC: - memset(&w->u.generic_data, 0xff, sizeof(generic_data_t)); - //pad_reset_data(&w->u.generic_data); - - w->u.generic_data.buttons = data.buttons; - w->u.generic_data.hatswitch = data.hatswitch; - w->u.generic_data.axis_x = data.steering; - w->u.generic_data.axis_y = 0xFF; //data.clutch; - w->u.generic_data.axis_z = data.throttle; - w->u.generic_data.axis_rz = data.brake; - - break; - - case WT_DRIVING_FORCE_PRO: - //memset(&w->u.dfp_data, 0, sizeof(dfp_data_t)); - //pad_reset_data(&w->u.dfp_data); - - w->u.dfp_data.buttons = data.buttons; - w->u.dfp_data.hatswitch = data.hatswitch; - w->u.dfp_data.axis_x = data.steering; - w->u.dfp_data.axis_z = data.throttle; - w->u.dfp_data.axis_rz = data.brake; - - w->u.dfp_data.magic1 = 1; - w->u.dfp_data.magic2 = 1; - w->u.dfp_data.magic3 = 1; - w->u.dfp_data.magic4 = - 1 << 0 | //enable pedals? - 0 << 1 | - 0 << 2 | - 0 << 3 | - 1 << 4 | //enable wheel? - 0 << 5 | - 0 << 6 | - 0 << 7; - - PrintBits(&w->u.dfp_data, sizeof(dfp_data_t)); - - break; - - case WT_DRIVING_FORCE_PRO_1102: - //memset(&w->u.dfp_data, 0, sizeof(dfp_data_t)); - //pad_reset_data(&w->u.dfp_data); - - w->u.dfp_data.buttons = data.buttons; - w->u.dfp_data.hatswitch = data.hatswitch; - w->u.dfp_data.axis_x = data.steering; - w->u.dfp_data.axis_z = 1 | (data.throttle * 0x3F) / 0xFF; //TODO Always > 0 or everything stops working, wut. - w->u.dfp_data.axis_rz = 0x3F - (data.brake * 0x3F) / 0xFF; - - w->u.dfp_data.magic1 = 1; - w->u.dfp_data.magic2 = 1; - w->u.dfp_data.magic3 = 1; - w->u.dfp_data.magic4 = - 1 << 0 | //enable pedals? - 0 << 1 | - 0 << 2 | - 0 << 3 | - 1 << 4 | //enable wheel? - 0 << 5 | - 0 << 6 | - 0 << 7; - - PrintBits(&w->u.dfp_data, sizeof(dfp_data_t)); - - break; - - case WT_GT_FORCE: - memset(&w->u.gtf_data, 0xff, sizeof(gtforce_data_t)); - - w->u.gtf_data.buttons = data.buttons; - w->u.gtf_data.axis_x = data.steering; - w->u.gtf_data.axis_y = 0xFF; //data.clutch; - w->u.gtf_data.axis_z = data.throttle; - w->u.gtf_data.axis_rz = data.brake; - - break; - - case WT_ROCKBAND1_DRUMKIT: - memset(&w->u.rb1dk_data, 0x0, sizeof(rb1drumkit_t)); - - w->u.rb1dk_data.u.buttons = data.buttons; - w->u.rb1dk_data.hatswitch = data.hatswitch; - - break; - - default: - break; - } - -#endif - } - - static void pad_init(PADState* s, int port, Pad* pad) - { - s->f.dev_subtype = pad->Type(); - s->pad = pad; - s->port = port; - s->dev.speed = USB_SPEED_FULL; s->dev.klass.handle_attach = usb_desc_attach; s->dev.klass.handle_reset = pad_handle_reset; s->dev.klass.handle_control = pad_handle_control; s->dev.klass.handle_data = pad_handle_data; s->dev.klass.unrealize = pad_handle_destroy; - s->dev.klass.open = pad_open; - s->dev.klass.close = pad_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = nullptr; usb_desc_init(&s->dev); usb_ep_init(&s->dev); - pad_handle_reset((USBDevice*)s); + pad_handle_reset(&s->dev); } - USBDevice* PadDevice::CreateDevice(int port) + USBDevice* PadDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("USB: PAD: Invalid input API.\n"); - return nullptr; - } - - Pad* pad = proxy->CreateObject(port, TypeName()); - - if (!pad) + if (subtype >= WT_COUNT) return nullptr; - pad->Type((PS2WheelTypes)GetSelectedSubtype(std::make_pair(port, TypeName()))); - PADState* s = new PADState(); - + PadState* s = new PadState(port, static_cast(subtype)); s->desc.full = &s->desc_dev; s->desc.str = df_desc_strings; @@ -744,7 +744,7 @@ namespace usb_pad const uint8_t* config_desc = df_config_descriptor; int config_desc_len = sizeof(df_config_descriptor); - switch (pad->Type()) + switch (s->type) { case WT_DRIVING_FORCE_PRO: { @@ -781,72 +781,99 @@ namespace usb_pad if (usb_desc_parse_config(config_desc, config_desc_len, s->desc_dev) < 0) goto fail; - pad_init(s, port, pad); + s->UpdateSettings(si, TypeName()); + pad_init(s); - return (USBDevice*)s; + return &s->dev; fail: - pad_handle_destroy((USBDevice*)s); + pad_handle_destroy(&s->dev); return nullptr; } - int PadDevice::Configure(int port, const std::string& api, void* data) + const char* PadDevice::Name() const { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; + return "Wheel Device"; } - int PadDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* PadDevice::TypeName() const { - PADState* s = (PADState*)dev; + return "Pad"; + } - if (!s) - return 0; - switch (mode) - { - case FreezeAction::Load: - s->f = *(PADState::freeze*)data; - s->pad->Type((PS2WheelTypes)s->f.dev_subtype); - return sizeof(PADState::freeze); - case FreezeAction::Save: - *(PADState::freeze*)data = s->f; - return sizeof(PADState::freeze); - case FreezeAction::Size: - return sizeof(PADState::freeze); - default: - break; - } - return 0; + bool PadDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); + + if (!sw.DoMarker("PadDevice")) + return false; + + sw.Do(&s->data.last_steering); + sw.DoPOD(&s->mFFstate); + return true; + } + + void PadDevice::UpdateSettings(USBDevice* dev, SettingsInterface& si) const + { + USB_CONTAINER_OF(dev, PadState, dev)->UpdateSettings(si, TypeName()); + } + + float PadDevice::GetBindingValue(const USBDevice* dev, u32 bind_index) const + { + const PadState* s = USB_CONTAINER_OF(dev, const PadState, dev); + return s->GetBindValue(bind_index); + } + + void PadDevice::SetBindingValue(USBDevice* dev, u32 bind_index, float value) const + { + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); + s->SetBindValue(bind_index, value); + } + + std::vector PadDevice::SubTypes() const + { + return {"Driving Force", "Driving Force Pro", "Driving Force Pro (rev11.02)", "GT Force"}; + } + + gsl::span PadDevice::Bindings(u32 subtype) const + { + return GetWheelBindings(static_cast(subtype)); + } + + gsl::span PadDevice::Settings(u32 subtype) const + { + return GetWheelSettings(static_cast(subtype)); + } + + void PadDevice::InputDeviceConnected(USBDevice* dev, const std::string_view& identifier) const + { + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); + if (s->mFFdevName == identifier) + s->OpenFFDevice(); + } + + void PadDevice::InputDeviceDisconnected(USBDevice* dev, const std::string_view& identifier) const + { + PadState* s = USB_CONTAINER_OF(dev, PadState, dev); + if (s->mFFdevName == identifier) + s->mFFdev.reset(); } // ---- Rock Band drum kit ---- - USBDevice* RBDrumKitDevice::CreateDevice(int port) + const char* RBDrumKitDevice::Name() const { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("RBDK: Invalid input API.\n"); - return nullptr; - } + return "Rock Band Drum Kit"; + } - Pad* pad = proxy->CreateObject(port, TypeName()); + const char* RBDrumKitDevice::TypeName() const + { + return "RBDrumKit"; + } - if (!pad) - return nullptr; - - pad->Type(WT_ROCKBAND1_DRUMKIT); - PADState* s = new PADState(); + USBDevice* RBDrumKitDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + PadState* s = new PadState(port, WT_ROCKBAND1_DRUMKIT); s->desc.full = &s->desc_dev; s->desc.str = rb1_desc_strings; @@ -856,54 +883,78 @@ namespace usb_pad if (usb_desc_parse_config(rb1_config_descriptor, sizeof(rb1_config_descriptor), s->desc_dev) < 0) goto fail; - pad_init(s, port, pad); + pad_init(s); - return (USBDevice*)s; + return &s->dev; fail: - pad_handle_destroy((USBDevice*)s); + pad_handle_destroy(&s->dev); return nullptr; } - int RBDrumKitDevice::Configure(int port, const std::string& api, void* data) + std::vector RBDrumKitDevice::SubTypes() const { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; + return {}; } - int RBDrumKitDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + gsl::span RBDrumKitDevice::Bindings(u32 subtype) const { - return PadDevice::Freeze(mode, dev, data); + static constexpr const InputBindingInfo bindings[] = { + {"Blue", "Blue", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::R1}, + {"Green", "Green", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Triangle}, + {"Red", "Red", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Yellow", "Yellow", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square}, + {"Orange", "Orange", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, + {"Select", "Select", InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select}, + {"Start", "Start", InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start}, + }; + + return bindings; + } + + gsl::span RBDrumKitDevice::Settings(u32 subtype) const + { + return {}; } // ---- Buzz ---- - USBDevice* BuzzDevice::CreateDevice(int port) + const char* BuzzDevice::Name() const { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("Buzz: Invalid input API.\n"); - return nullptr; - } + return "Buzz Controller"; + } - Pad* pad = proxy->CreateObject(port, TypeName()); + const char* BuzzDevice::TypeName() const + { + return "BuzzDevice"; + } - if (!pad) - return nullptr; + std::vector BuzzDevice::SubTypes() const + { + return {}; + } - pad->Type(WT_BUZZ_CONTROLLER); - PADState* s = new PADState(); + gsl::span BuzzDevice::Bindings(u32 subtype) const + { + static constexpr const InputBindingInfo bindings[] = { + {"Red", "Red", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::R1}, + {"Yellow", "Yellow", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Triangle}, + {"Green", "Green", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"Orange", "Orange", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square}, + {"Blue", "Blue", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Cross}, + }; + + return bindings; + } + + gsl::span BuzzDevice::Settings(u32 subtype) const + { + return {}; + } + + USBDevice* BuzzDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + PadState* s = new PadState(port, WT_BUZZ_CONTROLLER); s->desc.full = &s->desc_dev; s->desc.str = buzz_desc_strings; @@ -913,54 +964,73 @@ namespace usb_pad if (usb_desc_parse_config(buzz_config_descriptor, sizeof(buzz_config_descriptor), s->desc_dev) < 0) goto fail; - pad_init(s, port, pad); + pad_init(s); - return (USBDevice*)s; + return &s->dev; fail: - pad_handle_destroy((USBDevice*)s); + pad_handle_destroy(&s->dev); return nullptr; } - int BuzzDevice::Configure(int port, const std::string& api, void* data) - { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; - } - - int BuzzDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) - { - return PadDevice::Freeze(mode, dev, data); - } - // ---- Keyboardmania ---- - USBDevice* KeyboardmaniaDevice::CreateDevice(int port) + const char* KeyboardmaniaDevice::Name() const { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("usb-pad: %s: Invalid input API.", TypeName()); - return nullptr; - } + return "Keyboardmania"; + } - Pad* pad = proxy->CreateObject(port, TypeName()); + const char* KeyboardmaniaDevice::TypeName() const + { + return "Keyboardmania"; + } - if (!pad) - return nullptr; + std::vector KeyboardmaniaDevice::SubTypes() const + { + return {}; + } - pad->Type(WT_KEYBOARDMANIA_CONTROLLER); - PADState* s = new PADState(); + gsl::span KeyboardmaniaDevice::Bindings(u32 subtype) const + { + static constexpr const InputBindingInfo bindings[] = { + {"C", "C", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Unknown}, + {"CSharp", "C#", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Unknown}, + {"D", "D", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Unknown}, + {"EFlat", "Eb", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Unknown}, + {"E", "E", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Unknown}, + {"F", "F", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::Unknown}, + {"FSharp", "F#", InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::Unknown}, + {"G", "G", InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::Unknown}, + {"AFlat", "Ab", InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Unknown}, + {"A", "A", InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Unknown}, + {"BFlat", "Bb", InputBindingInfo::Type::Button, CID_BUTTON10, GenericInputBinding::Unknown}, + {"B", "B", InputBindingInfo::Type::Button, CID_BUTTON11, GenericInputBinding::Unknown}, + {"C2", "+C", InputBindingInfo::Type::Button, CID_BUTTON12, GenericInputBinding::Unknown}, + {"CSharp2", "+C#", InputBindingInfo::Type::Button, CID_BUTTON13, GenericInputBinding::Unknown}, + {"D2", "+D", InputBindingInfo::Type::Button, CID_BUTTON14, GenericInputBinding::Unknown}, + {"EFlat2", "+Eb", InputBindingInfo::Type::Button, CID_BUTTON15, GenericInputBinding::Unknown}, + {"E2", "+E", InputBindingInfo::Type::Button, CID_BUTTON16, GenericInputBinding::Unknown}, + {"F2", "+F", InputBindingInfo::Type::Button, CID_BUTTON17, GenericInputBinding::Unknown}, + {"FSharp2", "+F#", InputBindingInfo::Type::Button, CID_BUTTON18, GenericInputBinding::Unknown}, + {"G2", "+G", InputBindingInfo::Type::Button, CID_BUTTON19, GenericInputBinding::Unknown}, + {"AFlat2", "+Ab", InputBindingInfo::Type::Button, CID_BUTTON20, GenericInputBinding::Unknown}, + {"A2", "+A", InputBindingInfo::Type::Button, CID_BUTTON21, GenericInputBinding::Unknown}, + {"BFlat2", "+Bb", InputBindingInfo::Type::Button, CID_BUTTON22, GenericInputBinding::Unknown}, + {"B2", "+B", InputBindingInfo::Type::Button, CID_BUTTON23, GenericInputBinding::Unknown}, + {"C3", "++C", InputBindingInfo::Type::Button, CID_BUTTON24, GenericInputBinding::Unknown}, + }; + + return bindings; + } + + gsl::span KeyboardmaniaDevice::Settings(u32 subtype) const + { + return {}; + } + + USBDevice* KeyboardmaniaDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + PadState* s = new PadState(port, WT_KEYBOARDMANIA_CONTROLLER); s->desc.full = &s->desc_dev; s->desc.str = kbm_desc_strings; @@ -970,178 +1040,12 @@ namespace usb_pad if (usb_desc_parse_config(kbm_config_descriptor, sizeof(kbm_config_descriptor), s->desc_dev) < 0) goto fail; - pad_init(s, port, pad); + pad_init(s); - return (USBDevice*)s; + return &s->dev; fail: - pad_handle_destroy((USBDevice*)s); + pad_handle_destroy(&s->dev); return nullptr; } - - int KeyboardmaniaDevice::Configure(int port, const std::string& api, void* data) - { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; - } - - int KeyboardmaniaDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) - { - return PadDevice::Freeze(mode, dev, data); - } - - // ---- Gametrak ---- - - USBDevice* GametrakDevice::CreateDevice(int port) - { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("Gametrak: Invalid input API."); - return nullptr; - } - - Pad* pad = proxy->CreateObject(port, TypeName()); - - if (!pad) - return nullptr; - - pad->Type(WT_GAMETRAK_CONTROLLER); - PADState* s = new PADState(); - - s->desc.full = &s->desc_dev; - s->desc.str = gametrak_desc_strings; - - if (usb_desc_parse_dev(gametrak_dev_descriptor, sizeof(gametrak_dev_descriptor), s->desc, s->desc_dev) < 0) - goto fail; - if (usb_desc_parse_config(gametrak_config_descriptor, sizeof(gametrak_config_descriptor), s->desc_dev) < 0) - goto fail; - - s->f.dev_subtype = pad->Type(); - s->pad = pad; - s->port = port; - s->dev.speed = USB_SPEED_FULL; - s->dev.klass.handle_attach = usb_desc_attach; - s->dev.klass.handle_reset = pad_handle_reset; - s->dev.klass.handle_control = pad_handle_control; - s->dev.klass.handle_data = pad_handle_data; - s->dev.klass.unrealize = pad_handle_destroy; - s->dev.klass.open = pad_open; - s->dev.klass.close = pad_close; - s->dev.klass.usb_desc = &s->desc; - s->dev.klass.product_desc = s->desc.str[2]; - - usb_desc_init(&s->dev); - usb_ep_init(&s->dev); - pad_handle_reset((USBDevice*)s); - - return (USBDevice*)s; - - fail: - pad_handle_destroy((USBDevice*)s); - return nullptr; - } - - int GametrakDevice::Configure(int port, const std::string& api, void* data) - { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; - } - - int GametrakDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) - { - return PadDevice::Freeze(mode, dev, data); - } - - // ---- RealPlay ---- - - USBDevice* RealPlayDevice::CreateDevice(int port) - { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("RealPlay: Invalid input API"); - return nullptr; - } - - Pad* pad = proxy->CreateObject(port, TypeName()); - - if (!pad) - return nullptr; - - pad->Type((PS2WheelTypes)(WT_REALPLAY_RACING + GetSelectedSubtype(std::make_pair(port, TypeName())))); - PADState* s = new PADState(); - - s->desc.full = &s->desc_dev; - s->desc.str = realplay_desc_strings; - - if (pad->Type() == WT_REALPLAY_RACING && usb_desc_parse_dev(realplay_racing_dev_descriptor, sizeof(realplay_racing_dev_descriptor), s->desc, s->desc_dev) < 0) - goto fail; - if (pad->Type() == WT_REALPLAY_SPHERE && usb_desc_parse_dev(realplay_sphere_dev_descriptor, sizeof(realplay_sphere_dev_descriptor), s->desc, s->desc_dev) < 0) - goto fail; - if (pad->Type() == WT_REALPLAY_GOLF && usb_desc_parse_dev(realplay_golf_dev_descriptor, sizeof(realplay_golf_dev_descriptor), s->desc, s->desc_dev) < 0) - goto fail; - if (pad->Type() == WT_REALPLAY_POOL && usb_desc_parse_dev(realplay_pool_dev_descriptor, sizeof(realplay_pool_dev_descriptor), s->desc, s->desc_dev) < 0) - goto fail; - if (usb_desc_parse_config(realplay_config_descriptor, sizeof(realplay_config_descriptor), s->desc_dev) < 0) - goto fail; - - s->f.dev_subtype = pad->Type(); - s->pad = pad; - s->port = port; - s->dev.speed = USB_SPEED_FULL; - s->dev.klass.handle_attach = usb_desc_attach; - s->dev.klass.handle_reset = pad_handle_reset; - s->dev.klass.handle_control = pad_handle_control; - s->dev.klass.handle_data = pad_handle_data; - s->dev.klass.unrealize = pad_handle_destroy; - s->dev.klass.open = pad_open; - s->dev.klass.close = pad_close; - s->dev.klass.usb_desc = &s->desc; - s->dev.klass.product_desc = s->desc.str[1]; - - usb_desc_init(&s->dev); - usb_ep_init(&s->dev); - pad_handle_reset((USBDevice*)s); - - return (USBDevice*)s; - - fail: - pad_handle_destroy((USBDevice*)s); - return nullptr; - } - - int RealPlayDevice::Configure(int port, const std::string& api, void* data) - { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; - } - - int RealPlayDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) - { - return PadDevice::Freeze(mode, dev, data); - } - } // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/usb-pad.h b/pcsx2/USB/usb-pad/usb-pad.h index 10153060e7..befefef65b 100644 --- a/pcsx2/USB/usb-pad/usb-pad.h +++ b/pcsx2/USB/usb-pad/usb-pad.h @@ -13,196 +13,125 @@ * If not, see . */ -#ifndef USBPAD_H -#define USBPAD_H +#pragma once -#include "USB/qemu-usb/vl.h" -#include "USB/configuration.h" +#include "USB/qemu-usb/qusb.h" +#include "USB/qemu-usb/desc.h" #include "USB/deviceproxy.h" namespace usb_pad { - -#define CHECK(exp) \ - do \ - { \ - if (!(exp)) \ - goto Error; \ - } while (0) -#define SAFE_FREE(p) \ - do \ - { \ - if (p) \ - { \ - free(p); \ - (p) = NULL; \ - } \ - } while (0) - -#define S_CONFIG_JOY TEXT("Joystick") -#define N_JOYSTICK TEXT("joystick") - - class PadDevice + enum ControlID { - public: - virtual ~PadDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Wheel device"); - } - static const char* TypeName() - { - return "pad"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {"Driving Force", "Driving Force Pro", "Driving Force Pro (rev11.02)", "GT Force"}; - } + CID_STEERING_L, + CID_STEERING_R, + CID_THROTTLE, + CID_BRAKE, + CID_DPAD_UP, + CID_DPAD_DOWN, + CID_DPAD_LEFT, + CID_DPAD_RIGHT, + CID_BUTTON0, // cross + CID_BUTTON1, // square + CID_BUTTON2, // circle + CID_BUTTON3, // triangle + CID_BUTTON4, // R1 + CID_BUTTON5, // L1 + CID_BUTTON6, // R2 + CID_BUTTON7, // L2 + CID_BUTTON8, // select + CID_BUTTON9, // start + CID_BUTTON10, // R3 + CID_BUTTON11, // L3 + CID_BUTTON12, + CID_BUTTON13, + CID_BUTTON14, + CID_BUTTON15, + CID_BUTTON16, + CID_BUTTON17, + CID_BUTTON18, + CID_BUTTON19, + CID_BUTTON20, + CID_BUTTON21, + CID_BUTTON22, + CID_BUTTON23, + CID_BUTTON24, + CID_COUNT, }; - class RBDrumKitDevice + enum PS2WheelTypes { - public: - virtual ~RBDrumKitDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Rock Band drum kit"); - } - static const char* TypeName() - { - return "rbdrumkit"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + WT_GENERIC, // DF or any other LT wheel in non-native mode + WT_DRIVING_FORCE_PRO, //LPRC-11000? DF GT can be downgraded to Pro (?) + WT_DRIVING_FORCE_PRO_1102, //hw with buggy hid report? + WT_GT_FORCE, //formula gp + WT_ROCKBAND1_DRUMKIT, + WT_BUZZ_CONTROLLER, + WT_SEGA_SEAMIC, + WT_KEYBOARDMANIA_CONTROLLER, + WT_COUNT, }; - class BuzzDevice + class PadDevice : public DeviceProxy { public: - virtual ~BuzzDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Buzz Device"); - } - static const char* TypeName() - { - return "buzz_device"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* Name() const override; + const char* TypeName() const override; + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + void UpdateSettings(USBDevice* dev, SettingsInterface& si) const override; + float GetBindingValue(const USBDevice* dev, u32 bind_index) const override; + void SetBindingValue(USBDevice* dev, u32 bind_index, float value) const override; + void InputDeviceConnected(USBDevice* dev, const std::string_view& identifier) const override; + void InputDeviceDisconnected(USBDevice* dev, const std::string_view& identifier) const override; + std::vector SubTypes() const override; + gsl::span Bindings(u32 subtype) const override; + gsl::span Settings(u32 subtype) const override; }; - class GametrakDevice + class RBDrumKitDevice final : public PadDevice { public: - virtual ~GametrakDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Gametrak Device"); - } - static const char* TypeName() - { - return "gametrak_device"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return std::vector(); - } - static void Initialize(); + const char* Name() const override; + const char* TypeName() const override; + std::vector SubTypes() const override; + gsl::span Bindings(u32 subtype) const override; + gsl::span Settings(u32 subtype) const override; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; }; - class RealPlayDevice + class BuzzDevice final : public PadDevice { public: - virtual ~RealPlayDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("RealPlay Device"); - } - static const char* TypeName() - { - return "realplay_device"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {"RealPlay Racing", "RealPlay Sphere", "RealPlay Golf", "RealPlay Pool"}; - } - static void Initialize(); + const char* Name() const; + const char* TypeName() const; + std::vector SubTypes() const; + gsl::span Bindings(u32 subtype) const; + gsl::span Settings(u32 subtype) const; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const; }; - class SeamicDevice + class SeamicDevice final : public PadDevice { public: - virtual ~SeamicDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Sega Seamic"); - } - static const char* TypeName() - { - return "seamic"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + const char* Name() const; + const char* TypeName() const; + std::vector SubTypes() const; + gsl::span Bindings(u32 subtype) const; + gsl::span Settings(u32 subtype) const; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const; + bool Freeze(USBDevice* dev, StateWrapper& sw) const; }; - class KeyboardmaniaDevice + class KeyboardmaniaDevice final : public PadDevice { public: - virtual ~KeyboardmaniaDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Keyboardmania"); - } - static const char* TypeName() - { - return "keyboardmania"; - } - static std::list ListAPIs(); - static const TCHAR* LongAPIName(const std::string& name); - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - return {}; - } + const char* Name() const; + const char* TypeName() const; + std::vector SubTypes() const; + gsl::span Bindings(u32 subtype) const; + gsl::span Settings(u32 subtype) const; + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const; }; // Most likely as seen on https://github.com/matlo/GIMX @@ -245,45 +174,7 @@ namespace usb_pad #define FTYPE_HIGH_RESOLUTION_AUTO_CENTER_SPRING 0x0D #define FTYPE_FRICTION 0x0E - enum PS2WheelTypes - { - WT_GENERIC, // DF or any other LT wheel in non-native mode - WT_DRIVING_FORCE_PRO, //LPRC-11000? DF GT can be downgraded to Pro (?) - WT_DRIVING_FORCE_PRO_1102, //hw with buggy hid report? - WT_GT_FORCE, //formula gp - WT_ROCKBAND1_DRUMKIT, - WT_BUZZ_CONTROLLER, - WT_GAMETRAK_CONTROLLER, - WT_REALPLAY_RACING, - WT_REALPLAY_SPHERE, - WT_REALPLAY_GOLF, - WT_REALPLAY_POOL, - WT_SEGA_SEAMIC, - WT_KEYBOARDMANIA_CONTROLLER, - }; - inline int range_max(PS2WheelTypes type) - { - if (type == WT_DRIVING_FORCE_PRO || type == WT_DRIVING_FORCE_PRO_1102) - return 0x3FFF; - if (type == WT_SEGA_SEAMIC) - return 255; - return 0x3FF; - } - - // hold intermediate wheel data - struct wheel_data_t - { - int32_t steering; - uint32_t buttons; - uint32_t hatswitch; - uint32_t hat_horz; - uint32_t hat_vert; - - int32_t clutch; //no game uses though - int32_t throttle; - int32_t brake; - }; struct spring { @@ -415,117 +306,61 @@ namespace usb_pad virtual void DisableForce(EffectID force) = 0; }; - class Pad + struct PadState { - public: - Pad(int port, const char* dev_type) - : mPort(port) - , mDevType(dev_type) - { - memset(&mFFstate, 0, sizeof(mFFstate)); - } - virtual ~Pad() - { - delete mFFdev; - mFFdev = nullptr; - } - virtual int Open() = 0; - virtual int Close() = 0; - virtual int TokenIn(uint8_t* buf, int len) = 0; - virtual int TokenOut(const uint8_t* data, int len) = 0; - virtual int Reset() = 0; + PadState(u32 port_, PS2WheelTypes type_); + ~PadState(); - virtual PS2WheelTypes Type() { return mType; } - virtual void Type(PS2WheelTypes type) { mType = type; } - virtual int Port() { return mPort; } - virtual void Port(int port) { mPort = port; } + void UpdateSettings(SettingsInterface& si, const char* devname); + + float GetBindValue(u32 bind) const; + void SetBindValue(u32 bind, float value); + + void Reset(); + int TokenIn(u8* buf, int len); + int TokenOut(const u8* buf, int len); + + void UpdateSteering(); + void UpdateHatSwitch(); + + bool HasFF() const; + void OpenFFDevice(); void ParseFFData(const ff_data* ffdata, bool isDFP); - protected: - PS2WheelTypes mType = PS2WheelTypes::WT_GENERIC; - wheel_data_t mWheelData{}; - ff_state mFFstate; - FFDevice* mFFdev = nullptr; - int mPort; - const char* mDevType; + USBDevice dev{}; + USBDesc desc{}; + USBDescDevice desc_dev{}; + + u32 port = 0; + PS2WheelTypes type = WT_GENERIC; + + s16 steering_range = 0; + u16 steering_step = 0; + + struct + { + // intermediate state, resolved at query time + s16 steering_left; + s16 steering_right; + bool hat_left : 1; + bool hat_right : 1; + bool hat_up : 1; + bool hat_down : 1; + + u8 hatswitch; // direction + u16 steering; // 0..steering_range*2 + u16 last_steering; + u32 buttons; // active high + + u8 throttle; // inverted, 0 = fully depressed + u8 brake; // inverted, 0 = fully depressed + } data = {}; + + std::string mFFdevName; + std::unique_ptr mFFdev; + ff_state mFFstate{}; }; - //L3/R3 for newer wheels - //enum PS2Buttons : uint32_t { - // PAD_CROSS = 0, PAD_SQUARE, PAD_CIRCLE, PAD_TRIANGLE, - // PAD_L1, PAD_L2, PAD_R1, PAD_R2, - // PAD_SELECT, PAD_START, - // PAD_L3, PAD_R3, //order - // PAD_BUTTON_COUNT - //}; - - //??? - //enum DFButtons : uint32_t { - // PAD_CROSS = 0, PAD_SQUARE, PAD_CIRCLE, PAD_TRIANGLE, - // PAD_R2, - // PAD_L2, - // PAD_R1, - // PAD_L1, - // PAD_SELECT, PAD_START, - // PAD_BUTTON_COUNT - //}; - - //DF Pro buttons (?) - //Based on Tokyo Xtreme Racer Drift 2 - //GT4 flips R1/L1 with R2/L2 with DF wheel type - enum PS2Buttons : uint32_t - { - PAD_CROSS = 0, //menu up - GT Force - PAD_SQUARE, //menu down - PAD_CIRCLE, //X - PAD_TRIANGLE, //Y - PAD_R1, //A? in GT4 - PAD_L1, //B - PAD_R2, - PAD_L2, - PAD_SELECT, - PAD_START, - PAD_R3, - PAD_L3, //order, only GT Force/Force EX? - PAD_SHIFT_UP, - PAD_SHIFT_DOWN, // DF Pro - PAD_BUTTON_COUNT - }; - - enum PS2Axis : uint32_t - { - PAD_AXIS_X, - PAD_AXIS_Y, - PAD_AXIS_Z, - PAD_AXIS_RZ, - PAD_AXIS_HAT, //Treat as axis for mapping purposes - PAD_AXIS_COUNT - }; - - enum PS2HatSwitch - { - PAD_HAT_N = 0, - PAD_HAT_NE, - PAD_HAT_E, - PAD_HAT_SE, - PAD_HAT_S, - PAD_HAT_SW, - PAD_HAT_W, - PAD_HAT_NW, - PAD_HAT_COUNT - }; - - enum Buzz - { - BUZZ_RED, - BUZZ_YELLOW, - BUZZ_GREEN, - BUZZ_ORANGE, - BUZZ_BLUE, - }; - - static const int HATS_8TO4[] = {PAD_HAT_N, PAD_HAT_E, PAD_HAT_S, PAD_HAT_W}; - #define PAD_VID 0x046D #define PAD_MOMO 0xCA03 //black MOMO #define GENERIC_PID 0xC294 //actually Driving Force aka PID that most logitech wheels initially report @@ -1507,464 +1342,4 @@ namespace usb_pad 0x81, 0x01, // INPUT (Constant,Array,Absolute) 0xc0 // END_COLLECTION }; - - ////////////// - // GameTrak // - ////////////// - - // Product ID : - // 0x0982 - PlayStation 2 - // 0x0984 - ??? - - [[maybe_unused]] static uint8_t gametrak_dev_descriptor[] = { - 0x12, // bLength - 0x01, // bDescriptorType (Device) - 0x10, 0x01, // bcdUSB 1.10 - 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - 0x08, // bMaxPacketSize0 8 - 0xB7, 0x14, // idVendor 0x14B7 - 0x82, 0x09, // idProduct 0x0982 - 0x01, 0x00, // bcdDevice 0.01 - 0x01, // iManufacturer (String Index) - 0x02, // iProduct (String Index) - 0x00, // iSerialNumber (String Index) - 0x01, // bNumConfigurations 1 - }; - - static const uint8_t gametrak_config_descriptor[] = { - 0x09, // bLength - 0x02, // bDescriptorType (Configuration) - 0x22, 0x00, // wTotalLength 34 - 0x01, // bNumInterfaces 1 - 0x01, // bConfigurationValue - 0x00, // iConfiguration (String Index) - 0x80, // bmAttributes - 0x0A, // bMaxPower 20mA - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x00, // bInterfaceNumber 0 - 0x00, // bAlternateSetting - 0x01, // bNumEndpoints 1 - 0x03, // bInterfaceClass - 0x00, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface (String Index) - - 0x09, // bLength - 0x21, // bDescriptorType (HID) - 0x01, 0x01, // bcdHID 1.01 - 0x00, // bCountryCode - 0x01, // bNumDescriptors - 0x22, // bDescriptorType[0] (HID) - 0x7A, 0x00, // wDescriptorLength[0] 122 - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x81, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x10, 0x00, // wMaxPacketSize 16 - 0x0A, // bInterval 10 (unit depends on device speed) - }; - - static const uint8_t gametrak_hid_report_descriptor[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x04, // Usage (Joystick) - 0xA1, 0x01, // Collection (Application) - 0x09, 0x01, // Usage (Pointer) - 0xA1, 0x00, // Collection (Physical) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x09, 0x32, // Usage (Z) - 0x09, 0x33, // Usage (Rx) - 0x09, 0x34, // Usage (Ry) - 0x09, 0x35, // Usage (Rz) - 0x16, 0x00, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x0F, // Logical Maximum (4095) - 0x36, 0x00, 0x00, // Physical Minimum (0) - 0x46, 0xFF, 0x0F, // Physical Maximum (4095) - 0x66, 0x00, 0x00, // Unit (None) - 0x75, 0x10, // Report Size (16) - 0x95, 0x06, // Report Count (6) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, // End Collection - 0x09, 0x39, // Usage (Hat switch) - 0x15, 0x01, // Logical Minimum (1) - 0x25, 0x08, // Logical Maximum (8) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0x3B, 0x01, // Physical Maximum (315) - 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) - 0x75, 0x04, // Report Size (4) - 0x95, 0x01, // Report Count (1) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (0x01) - 0x29, 0x0C, // Usage Maximum (0x0C) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x0C, // Report Count (12) - 0x55, 0x00, // Unit Exponent (0) - 0x65, 0x00, // Unit (None) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x08, // Report Size (8) - 0x95, 0x02, // Report Count (2) - 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x08, // Usage Page (LEDs) - 0x09, 0x43, // Usage (Slow Blink On Time) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0xFF, 0x00, // Physical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x01, // Report Count (1) - 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) - 0x09, 0x44, // Usage (Slow Blink Off Time) - 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) - 0x09, 0x45, // Usage (Fast Blink On Time) - 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) - 0x09, 0x46, // Usage (Fast Blink Off Time) - 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) - 0xC0, // End Collection - }; - - ////////////// - // RealPlay // - ////////////// - - // Product ID : - // Racing - 0x09B2 - // Sphere - 0x09B3 - // Golf - 0x09B5 - // Pool - 0x09B6 - - // RealPlay Golf is dumped from a real controller. - // The others were force-brutted to be accepted by games - they may be inaccurate. - - static const uint8_t realplay_racing_dev_descriptor[] = { - 0x12, // bLength - 0x01, // bDescriptorType (Device) - 0x00, 0x02, // bcdUSB 2.00 - 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - 0x40, // bMaxPacketSize0 64 - 0xB7, 0x14, // idVendor 0x14B7 - 0xB2, 0x09, // idProduct 0x09B2 - 0x00, 0x01, // bcdDevice 2.00 - 0x01, // iManufacturer (String Index) - 0x02, // iProduct (String Index) - 0x00, // iSerialNumber (String Index) - 0x01, // bNumConfigurations 1 - }; - - static const uint8_t realplay_sphere_dev_descriptor[] = { - 0x12, // bLength - 0x01, // bDescriptorType (Device) - 0x00, 0x02, // bcdUSB 2.00 - 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - 0x40, // bMaxPacketSize0 64 - 0xB7, 0x14, // idVendor 0x14B7 - 0xB3, 0x09, // idProduct 0x09B3 - 0x00, 0x01, // bcdDevice 2.00 - 0x01, // iManufacturer (String Index) - 0x02, // iProduct (String Index) - 0x00, // iSerialNumber (String Index) - 0x01, // bNumConfigurations 1 - }; - - static const uint8_t realplay_golf_dev_descriptor[] = { - 0x12, // bLength - 0x01, // bDescriptorType (Device) - 0x00, 0x02, // bcdUSB 2.00 - 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - 0x40, // bMaxPacketSize0 64 - 0xB7, 0x14, // idVendor 0x14B7 - 0xB5, 0x09, // idProduct 0x09B5 - 0x00, 0x01, // bcdDevice 2.00 - 0x01, // iManufacturer (String Index) - 0x02, // iProduct (String Index) - 0x00, // iSerialNumber (String Index) - 0x01, // bNumConfigurations 1 - }; - - static const uint8_t realplay_pool_dev_descriptor[] = { - 0x12, // bLength - 0x01, // bDescriptorType (Device) - 0x00, 0x02, // bcdUSB 2.00 - 0x00, // bDeviceClass (Use class information in the Interface Descriptors) - 0x00, // bDeviceSubClass - 0x00, // bDeviceProtocol - 0x40, // bMaxPacketSize0 64 - 0xB7, 0x14, // idVendor 0x14B7 - 0xB6, 0x09, // idProduct 0x09B6 - 0x00, 0x01, // bcdDevice 2.00 - 0x01, // iManufacturer (String Index) - 0x02, // iProduct (String Index) - 0x00, // iSerialNumber (String Index) - 0x01, // bNumConfigurations 1 - }; - - static const uint8_t realplay_config_descriptor[] = { - 0x09, // bLength - 0x02, // bDescriptorType (Configuration) - 0x29, 0x00, // wTotalLength 41 - 0x01, // bNumInterfaces 1 - 0x01, // bConfigurationValue - 0x00, // iConfiguration (String Index) - 0x80, // bmAttributes - 0x32, // bMaxPower 100mA - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x00, // bInterfaceNumber 0 - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints 2 - 0x03, // bInterfaceClass - 0x00, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface (String Index) - - 0x09, // bLength - 0x21, // bDescriptorType (HID) - 0x11, 0x01, // bcdHID 1.11 - 0x00, // bCountryCode - 0x01, // bNumDescriptors - 0x22, // bDescriptorType[0] (HID) - 0x85, 0x00, // wDescriptorLength[0] 133 - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x02, // bEndpointAddress (OUT/H2D) - 0x03, // bmAttributes (Interrupt) - 0x40, 0x00, // wMaxPacketSize 64 - 0x0A, // bInterval 10 (unit depends on device speed) - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x81, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x40, 0x00, // wMaxPacketSize 64 - 0x0A, // bInterval 10 (unit depends on device speed) - }; - - static const uint8_t realplay_hid_report_descriptor[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x05, // Usage (Game Pad) - 0xA1, 0x01, // Collection (Application) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x0F, // Logical Maximum (4095) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0xFF, 0x0F, // Physical Maximum (4095) - 0x09, 0x30, // Usage (X) - 0x75, 0x0C, // Report Size (12) - 0x95, 0x01, // Report Count (1) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x01, // Report Size (1) - 0x95, 0x04, // Report Count (4) - 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x09, 0x31, // Usage (Y) - 0x75, 0x0C, // Report Size (12) - 0x95, 0x01, // Report Count (1) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x01, // Report Size (1) - 0x95, 0x04, // Report Count (4) - 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x09, 0x32, // Usage (Z) - 0x75, 0x0C, // Report Size (12) - 0x95, 0x01, // Report Count (1) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x01, // Report Size (1) - 0x95, 0x04, // Report Count (4) - 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) - 0x09, 0x20, // Usage (0x20) - 0x09, 0x21, // Usage (0x21) - 0x09, 0x22, // Usage (0x22) - 0x09, 0x23, // Usage (0x23) - 0x09, 0x24, // Usage (0x24) - 0x09, 0x25, // Usage (0x25) - 0x09, 0x26, // Usage (0x26) - 0x09, 0x27, // Usage (0x27) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x46, 0xFF, 0x00, // Physical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x08, // Report Count (8) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x35, 0x00, // Physical Minimum (0) - 0x45, 0x01, // Physical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x08, // Report Count (8) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (0x01) - 0x29, 0x08, // Usage Maximum (0x08) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) - 0x09, 0x28, // Usage (0x28) - 0x09, 0x29, // Usage (0x29) - 0x09, 0x2A, // Usage (0x2A) - 0x09, 0x2B, // Usage (0x2B) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x46, 0xFF, 0x00, // Physical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x04, // Report Count (4) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0xC0, // End Collection - }; - - struct dfp_buttons_t - { - uint16_t cross : 1; - uint16_t square : 1; - uint16_t circle : 1; - uint16_t triangle : 1; - uint16_t rpaddle_R1 : 1; - uint16_t lpaddle_L1 : 1; - uint16_t R2 : 1; - uint16_t L2 : 1; - uint16_t select : 1; - uint16_t start : 1; - uint16_t R3 : 1; - uint16_t L3 : 1; - uint16_t shifter_back : 1; - uint16_t shifter_fwd : 1; - uint16_t padding : 2; - }; - - struct dfgt_buttons_t - { - uint16_t cross : 1; - uint16_t square : 1; - uint16_t circle : 1; - uint16_t triangle : 1; - uint16_t rpaddle_R1 : 1; - uint16_t lpaddle_L1 : 1; - uint16_t R2 : 1; - uint16_t L2 : 1; - uint16_t select : 1; - uint16_t start : 1; - uint16_t R3 : 1; - uint16_t L3 : 1; - uint16_t shifter_back : 1; - uint16_t shifter_fwd : 1; - uint16_t dial_center : 1; - uint16_t dial_cw : 1; - - uint16_t dial_ccw : 1; - uint16_t rocker_minus : 1; - uint16_t horn : 1; - uint16_t ps_button : 1; - uint16_t padding : 12; - }; - - struct dfp_data_t - { - uint32_t axis_x : 14; - uint32_t buttons : 14; - uint32_t hatswitch : 4; - - uint32_t pad0 : 8; - uint32_t magic1 : 2; //8 //constant? - uint32_t axis_z : 6; //10 - - uint32_t magic2 : 1; //16 //constant? - uint32_t axis_rz : 6; //17 - - uint32_t magic3 : 1; //23 - - uint32_t magic4 : 8; //constant - }; - - struct momo2_data_t - { - uint32_t pad0 : 8; //report id probably - uint32_t axis_x : 10; - uint32_t buttons : 10; - uint32_t padding0 : 4; //32 - - uint8_t padding1; - uint8_t axis_z; - uint8_t axis_rz; - uint8_t padding2; //32 - }; - - // DF or any LG wheel in non-native mode - struct generic_data_t - { - uint32_t axis_x : 10; - uint32_t buttons : 12; - uint32_t pad0 : 2; //vendor - uint32_t axis_y : 8; //constant (0x7f on PC, 0xFF on console?) - - uint32_t hatswitch : 4; - uint32_t pad1 : 4; //vendor - uint32_t axis_z : 8; - uint32_t axis_rz : 8; - uint32_t pad2 : 8; - }; - - // GT Force? - struct gtforce_data_t - { - uint32_t axis_x : 10; - uint32_t buttons : 6; - uint32_t pad0 : 8; - uint32_t axis_y : 8; - - uint32_t axis_z : 8; - uint32_t axis_rz : 8; - - uint32_t pad1 : 16; - }; - - struct random_data_t - { - uint32_t axis_x : 10; - uint32_t buttons : 10; - uint32_t pad1 : 12; - - uint32_t axis_y : 8; //constant - uint32_t axis_z : 8; - uint32_t axis_rz : 8; - uint32_t pad2 : 8; - }; - - struct rb1drumkit_t - { - union u - { - uint16_t buttons; - struct s - { - uint16_t blue : 1; - uint16_t green : 1; - uint16_t red : 1; - uint16_t yellow : 1; - uint16_t orange : 1; - uint16_t something0 : 3; - - uint16_t select : 1; - uint16_t start : 1; - uint16_t something1 : 6; - } s; - } u; - - uint8_t hatswitch; - }; - - void pad_reset_data(generic_data_t* d); - void pad_reset_data(dfp_data_t* d); - void pad_copy_data(PS2WheelTypes type, uint8_t* buf, wheel_data_t& data); - //Convert DF Pro buttons to selected wheel type - uint32_t convert_wt_btn(PS2WheelTypes type, uint32_t inBtn); - } // namespace usb_pad -#endif diff --git a/pcsx2/USB/usb-pad/usb-seamic.cpp b/pcsx2/USB/usb-pad/usb-seamic.cpp index bdae3afad5..90b63249c5 100644 --- a/pcsx2/USB/usb-pad/usb-seamic.cpp +++ b/pcsx2/USB/usb-pad/usb-seamic.cpp @@ -14,11 +14,10 @@ */ #include "PrecompiledHeader.h" -#include "padproxy.h" -#include "usb-pad.h" +#include "USB/usb-pad/usb-pad.h" #include "USB/qemu-usb/desc.h" #include "USB/usb-mic/usb-mic-singstar.h" -#include "USB/shared/inifile_usb.h" +#include "USB/USB.h" namespace usb_pad { @@ -234,32 +233,18 @@ namespace usb_pad // 134 bytes }; - std::list SeamicDevice::ListAPIs() + struct SeamicState : public PadState { - return RegisterPad::instance().Names(); - } + explicit SeamicState(u32 port); - const TCHAR* SeamicDevice::LongAPIName(const std::string& name) - { - auto proxy = RegisterPad::instance().Proxy(name); - if (proxy) - return proxy->Name(); - return nullptr; - } - - typedef struct SeamicState - { - USBDevice dev; - USBDesc desc; - USBDescDevice desc_dev; USBDevice* mic; - Pad* pad; - uint8_t port; - } SeamicState; + }; + + SeamicState::SeamicState(u32 port) : PadState(port, WT_SEGA_SEAMIC) {} static void pad_handle_data(USBDevice* dev, USBPacket* p) { - SeamicState* s = (SeamicState*)dev; + SeamicState* s = USB_CONTAINER_OF(dev, SeamicState, dev); uint8_t data[64]; int ret = 0; @@ -268,15 +253,15 @@ namespace usb_pad switch (p->pid) { case USB_TOKEN_IN: - if (devep == 1 && s->mic) + if (devep == 1) { s->mic->klass.handle_data(s->mic, p); } - else if (devep == 2 && s->pad) + else if (devep == 2) { - ret = s->pad->TokenIn(data, p->iov.size); + ret = s->TokenIn(data, p->iov.size); if (ret > 0) - usb_packet_copy(p, data, MIN((unsigned long)ret, sizeof(data))); + usb_packet_copy(p, data, std::min((size_t)ret, sizeof(data))); else p->status = ret; } @@ -286,8 +271,8 @@ namespace usb_pad } break; case USB_TOKEN_OUT: - usb_packet_copy(p, data, MIN(p->iov.size, sizeof(data))); - ret = s->pad->TokenOut(data, p->iov.size); + usb_packet_copy(p, data, std::min(p->iov.size, sizeof(data))); + ret = s->TokenOut(data, p->iov.size); break; default: fail: @@ -298,9 +283,8 @@ namespace usb_pad static void pad_handle_reset(USBDevice* dev) { - /* XXX: do it */ - SeamicState* s = (SeamicState*)dev; - s->pad->Reset(); + SeamicState* s = USB_CONTAINER_OF(dev, SeamicState, dev); + s->Reset(); s->mic->klass.handle_reset(s->mic); return; } @@ -354,73 +338,72 @@ namespace usb_pad static void pad_handle_destroy(USBDevice* dev) { - SeamicState* s = (SeamicState*)dev; + SeamicState* s = USB_CONTAINER_OF(dev, SeamicState, dev); s->mic->klass.unrealize(s->mic); - s->mic = nullptr; delete s; } - static int pad_open(USBDevice* dev) + const char* SeamicDevice::Name() const { - SeamicState* s = (SeamicState*)dev; - if (s) - { - s->mic->klass.open(s->mic); - return s->pad->Open(); - } - return 1; + return "Sega Seamic"; } - static void pad_close(USBDevice* dev) + const char* SeamicDevice::TypeName() const { - SeamicState* s = (SeamicState*)dev; - if (s) - { - s->mic->klass.close(s->mic); - s->pad->Close(); - } + return "seamic"; } - USBDevice* SeamicDevice::CreateDevice(int port) + std::vector SeamicDevice::SubTypes() const { - std::string varApi; -#ifdef _WIN32 - std::wstring tmp; - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, tmp); - varApi = wstr_to_str(tmp); -#else - LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); -#endif - PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); - if (!proxy) - { - Console.WriteLn("USB: PAD: Invalid input API.\n"); - return NULL; - } + return {}; + } - std::string api; + gsl::span SeamicDevice::Bindings(u32 subtype) const + { + // TODO: This is likely wrong. Someone who cares can fix it. + static constexpr const InputBindingInfo bindings[] = { + {"StickLeft", "Stick Left", InputBindingInfo::Type::HalfAxis, CID_STEERING_L, GenericInputBinding::LeftStickLeft}, + {"StickRight", "Stick Right", InputBindingInfo::Type::HalfAxis, CID_STEERING_R, GenericInputBinding::LeftStickRight}, + {"StickUp", "Stick Up", InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::LeftStickUp}, + {"StickDown", "Stick Down", InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::LeftStickDown}, + {"A", "A", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross}, + {"B", "B", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle}, + {"X", "X", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square}, + {"Y", "Y", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle}, + {"Z", "Z", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::L1}, + {"C", "C", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::R1}, + {"DPadUp", "D-Pad Up", InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp}, + {"DPadDown", "D-Pad Down", InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown}, + {"DPadLeft", "D-Pad Left", InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft}, + {"DPadRight", "D-Pad Right", InputBindingInfo::Type::Button, CID_DPAD_RIGHT, GenericInputBinding::DPadRight}, + }; + return bindings; + } -#ifdef _WIN32 - if (!LoadSetting(nullptr, port, usb_mic::SingstarDevice::TypeName(), N_DEVICE_API, tmp)) + gsl::span SeamicDevice::Settings(u32 subtype) const + { + static constexpr const SettingInfo info[] = { + {SettingInfo::Type::StringList, "input_device_name", "Input Device", "Selects the device to read audio from.", "", nullptr, + nullptr, nullptr, nullptr, nullptr, &AudioDevice::GetInputDeviceList}, + {SettingInfo::Type::Integer, "input_latency", "Input Latency", "Specifies the latency to the host input device.", + AudioDevice::DEFAULT_LATENCY_STR, "0", "1000", "1", "%dms", nullptr, nullptr, 1.0f}, + }; + return info; + } + + USBDevice* SeamicDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const + { + const usb_mic::SingstarDevice* mic_proxy = + static_cast(RegisterDevice::instance().Device(DEVTYPE_SINGSTAR)); + if (!mic_proxy) return nullptr; - api = wstr_to_str(tmp); -#else - if (!LoadSetting(nullptr, port, usb_mic::SingstarDevice::TypeName(), N_DEVICE_API, api)) - return nullptr; -#endif - USBDevice* mic = usb_mic::SingstarDevice::CreateDevice(port, api); + USBDevice* mic = mic_proxy->CreateDevice(si, port, 0, false, TypeName()); if (!mic) return nullptr; - Pad* pad = proxy->CreateObject(port, TypeName()); - - if (!pad) - return NULL; - - pad->Type(WT_SEGA_SEAMIC); - SeamicState* s = new SeamicState(); + SeamicState* s = new SeamicState(port); s->mic = mic; s->desc.full = &s->desc_dev; @@ -431,41 +414,31 @@ namespace usb_pad if (usb_desc_parse_config(config_descriptor, sizeof(config_descriptor), s->desc_dev) < 0) goto fail; - s->pad = pad; s->dev.speed = USB_SPEED_FULL; s->dev.klass.handle_attach = usb_desc_attach; s->dev.klass.handle_reset = pad_handle_reset; s->dev.klass.handle_control = pad_handle_control; s->dev.klass.handle_data = pad_handle_data; s->dev.klass.unrealize = pad_handle_destroy; - s->dev.klass.open = pad_open; - s->dev.klass.close = pad_close; s->dev.klass.usb_desc = &s->desc; s->dev.klass.product_desc = s->desc.str[2]; //not really used s->port = port; usb_desc_init(&s->dev); usb_ep_init(&s->dev); - pad_handle_reset((USBDevice*)s); + pad_handle_reset(&s->dev); - return (USBDevice*)s; + return &s->dev; fail: - pad_handle_destroy((USBDevice*)s); + pad_handle_destroy(&s->dev); return nullptr; } - int SeamicDevice::Configure(int port, const std::string& api, void* data) + bool SeamicDevice::Freeze(USBDevice* dev, StateWrapper& sw) const { - auto proxy = RegisterPad::instance().Proxy(api); - if (proxy) - return proxy->Configure(port, TypeName(), data); - return RESULT_CANCELED; - } - - int SeamicDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) - { - return 0; + pxFailRel("Not implemented!"); + return true; // SeamicState *s = (SeamicState *)dev; // switch (mode) // { diff --git a/pcsx2/USB/usb-printer/usb-printer.cpp b/pcsx2/USB/usb-printer/usb-printer.cpp index ec2a03f9ce..b1d981a2f2 100644 --- a/pcsx2/USB/usb-printer/usb-printer.cpp +++ b/pcsx2/USB/usb-printer/usb-printer.cpp @@ -14,50 +14,54 @@ */ #include "PrecompiledHeader.h" -#include "../qemu-usb/vl.h" -#include "../shared/inifile_usb.h" -#include "usb-printer.h" -#include "gui/AppConfig.h" - -#ifndef O_BINARY - #define O_BINARY 0 -#endif +#include "USB/qemu-usb/qusb.h" +#include "USB/qemu-usb/USBinternal.h" +#include "USB/usb-printer/usb-printer.h" +#include "USB/USB.h" +#include "common/FileSystem.h" +#include "common/Path.h" +#include "Config.h" +#include "fmt/format.h" +#include "StateWrapper.h" +#include "Host.h" +#include "IconsFontAwesome5.h" namespace usb_printer { typedef struct PrinterState { - USBDevice dev; - USBDesc desc; - USBDescDevice desc_dev; + USBDevice dev{}; + USBDesc desc{}; + USBDescDevice desc_dev{}; - int selected_printer; - int cmd_state; - uint8_t last_command[65]; - int last_command_size; - int print_file; - int width; - int height; - long stride; - int data_size; - long data_pos; + int selected_printer = 0; + int cmd_state = 0; + uint8_t last_command[65] = {}; + int last_command_size = 0; + std::string print_filename; + std::FILE* print_file = nullptr; + int width = 0; + int height = 0; + long stride = 0; + int data_size = 0; + long data_pos = 0; } PrinterState; static void usb_printer_handle_reset(USBDevice* dev) { - PrinterState* s = (PrinterState*)dev; + PrinterState* s = USB_CONTAINER_OF(dev, PrinterState, dev); s->cmd_state = 0; - if (s->print_file > 0) + if (s->print_file) { - close(s->print_file); + std::fclose(s->print_file); + s->print_file = nullptr; } - s->print_file = -1; } static void usb_printer_handle_control(USBDevice* dev, USBPacket* p, int request, int value, - int index, int length, uint8_t* data) + int index, int length, uint8_t* data) { - PrinterState* s = (PrinterState*)dev; + PrinterState* s = USB_CONTAINER_OF(dev, PrinterState, dev); int ret = 0; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -81,21 +85,23 @@ namespace usb_printer } } - void sony_open_file(PrinterState* s) + static void sony_open_file(PrinterState* s) { - char filepath[1024]; char cur_time_str[32]; const time_t cur_time = time(nullptr); strftime(cur_time_str, sizeof(cur_time_str), "%Y_%m_%d_%H_%M_%S", localtime(&cur_time)); - snprintf(filepath, sizeof(filepath), "%s/print_%s.bmp", - g_Conf->Folders.Snapshots.ToString().ToStdString().c_str(), cur_time_str); - s->print_file = open(filepath, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 666); - if (s->print_file < 0) + + s->print_filename = Path::Combine(EmuFolders::Snapshots, fmt::format("print_{}.bmp", cur_time_str)); + s->print_file = FileSystem::OpenCFile(s->print_filename.c_str(), "wb"); + if (!s->print_file) { - Console.WriteLn("Printer: Sony: Cannot open: %s", filepath); + Host::AddIconOSDMessage("USBPrinterOpen", ICON_FA_EXCLAMATION_TRIANGLE, + fmt::format("Failed to open '{}' for printing.", s->print_filename), Host::OSD_ERROR_DURATION); return; } - Console.WriteLn("Printer: Sony: Saving to... %s", filepath); + + Host::AddIconOSDMessage("USBPrinterOpen", ICON_FA_SAVE, + fmt::format("Printer saving to '{}'...", Path::GetFileName(s->print_filename)), Host::OSD_INFO_DURATION); BMPHeader header = {0}; header.magic = 0x4D42; @@ -106,23 +112,23 @@ namespace usb_printer header.height = s->height; header.planes = 1; header.bpp = 24; - if (write(s->print_file, &header, sizeof(header)) == -1) + if (std::fwrite(&header, sizeof(header), 1, s->print_file) != 1) { Console.Error("Error writing header to print file"); } s->stride = 3 * s->width + 3 - ((3 * s->width + 3) & 3); s->data_pos = 0; - lseek(s->print_file, sizeof(BMPHeader) + s->stride * s->height - 1, SEEK_SET); + FileSystem::FSeek64(s->print_file, sizeof(BMPHeader) + s->stride * s->height - 1, SEEK_SET); char zero = 0; - if (write(s->print_file, &zero, 1) == -1) + if (std::fwrite(&zero, 1, 1, s->print_file) != 1) { Console.Error("Error writing zero padding to header to print file"); } } - void sony_write_data(PrinterState* s, int size, uint8_t* data) + static void sony_write_data(PrinterState* s, int size, uint8_t* data) { for (int i = 0; i < size; i++) { @@ -134,24 +140,42 @@ namespace usb_printer Console.WriteLn("Printer: Sony: error: pos_out=0x%x", pos_out); break; } - lseek(s->print_file, sizeof(BMPHeader) + pos_out + 2 - s->data_pos % 3, SEEK_SET); - if (write(s->print_file, data + i, 1) == -1) + // print_file might be null if we're loading a state + if (s->print_file) { - Console.Error("Error writing data to print file"); + FileSystem::FSeek64(s->print_file, sizeof(BMPHeader) + pos_out + 2 - s->data_pos % 3, SEEK_SET); + + if (std::fwrite(data + i, 1, 1, s->print_file) != 1) + { + Console.Error("Error writing data to print file"); + } } - s->data_pos ++; + s->data_pos++; } } - void sony_close_file(PrinterState* s) + static void sony_close_file(PrinterState* s) { Console.WriteLn("Printer: Sony: done."); - if (s->print_file >= 0) + if (s->print_file) { - close(s->print_file); + std::fclose(s->print_file); + s->print_file = nullptr; + s->print_filename = {}; } - s->print_file = -1; + } + + static void sony_cancel_file(PrinterState* s) + { + if (!s->print_file) + return; + + Console.Warning("Removing incomplete printer file '%s'", s->print_filename.c_str()); + std::fclose(s->print_file); + s->print_file = nullptr; + FileSystem::DeleteFilePath(s->print_filename.c_str()); + s->print_filename = {}; } static void usb_printer_handle_data_sony(USBDevice* dev, USBPacket* p) @@ -162,47 +186,32 @@ namespace usb_printer uint8_t ret[256]; }; const struct req_reply commands[] = - { { - {0x1B, 0xE0, 0x00, 0x00, 0x00, 0x0E, 0x00}, - {0x0D, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x01, 0x24, 0x38, 0x03, 0xF2, 0x02, 0x74} - }, - { - {0x1B, 0xCF, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00}, - {0x00, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - }, - { - {0x1B, 0xCF, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00}, - {0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0E, 0x00, 0x00, 0x07} - }, - { - {0x1B, 0xCF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00}, - {0x00, 0x00, 0x00, 0x12, 0x03, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x00, 0x00, 0x04} - }, - { - {0x1B, 0xCF, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00}, - {0x00, 0x00, 0x00, 0x0A, 0x04, 0x00, 0x00, 0x18, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00} - }, - { - {0x1B, 0xCF, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00}, - {0x00, 0x00, 0x00, 0x0E, 0x05, 0x00, 0x02, 0x74, 0x03, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00} - }, - { - {0x1B, 0x12, 0x01, 0x00, 0x00, 0x19, 0x00}, - {0x02, 0xC0, 0x00, 0x15, 0x03, 0xF2, 0x02, 0x74, 0x03, 0xF2, 0x02, 0x74, 0x01, 0x33, 0x00, 0x00, - 0x15, 0x01, 0x03, 0x23, 0x30, 0x31, 0x2E, 0x30, 0x30} - } - }; + {{0x1B, 0xE0, 0x00, 0x00, 0x00, 0x0E, 0x00}, + {0x0D, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x01, 0x24, 0x38, 0x03, 0xF2, 0x02, 0x74}}, + {{0x1B, 0xCF, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00}, + {0x00, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x1B, 0xCF, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00}, + {0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x07}}, + {{0x1B, 0xCF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00}, + {0x00, 0x00, 0x00, 0x12, 0x03, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x04}}, + {{0x1B, 0xCF, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00}, + {0x00, 0x00, 0x00, 0x0A, 0x04, 0x00, 0x00, 0x18, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00}}, + {{0x1B, 0xCF, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00}, + {0x00, 0x00, 0x00, 0x0E, 0x05, 0x00, 0x02, 0x74, 0x03, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}}, + {{0x1B, 0x12, 0x01, 0x00, 0x00, 0x19, 0x00}, + {0x02, 0xC0, 0x00, 0x15, 0x03, 0xF2, 0x02, 0x74, 0x03, 0xF2, 0x02, 0x74, 0x01, 0x33, 0x00, 0x00, + 0x15, 0x01, 0x03, 0x23, 0x30, 0x31, 0x2E, 0x30, 0x30}}}; const uint8_t set_size[] = {0x00, 0x00, 0x00, 0x00, 0xa7, 0x00}; const uint8_t set_data[] = {0x1b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t print_compl[] = {0x1b, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}; - PrinterState* s = (PrinterState*)dev; + PrinterState* s = USB_CONTAINER_OF(dev, PrinterState, dev); //const uint8_t ep_nr = p->ep->nr; //const uint8_t ep_type = p->ep->type; @@ -275,19 +284,16 @@ namespace usb_printer static void usb_printer_handle_destroy(USBDevice* dev) { - PrinterState* s = (PrinterState*)dev; + PrinterState* s = USB_CONTAINER_OF(dev, PrinterState, dev); + sony_cancel_file(s); delete s; } - USBDevice* PrinterDevice::CreateDevice(int port) + USBDevice* PrinterDevice::CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const { PrinterState* s = new PrinterState(); - std::string api = *PrinterDevice::ListAPIs().begin(); - uint32_t subtype = GetSelectedSubtype(std::make_pair(port, TypeName())); - if (subtype >= sizeof(sPrinters) / sizeof(sPrinters[0])) { - subtype = 0; - } - s->selected_printer = subtype; + + s->selected_printer = std::min(subtype, std::size(sPrinters)); s->dev.speed = USB_SPEED_FULL; s->desc.full = &s->desc_dev; @@ -312,22 +318,57 @@ namespace usb_printer usb_desc_init(&s->dev); usb_ep_init(&s->dev); - usb_printer_handle_reset((USBDevice*)s); - return (USBDevice*)s; + usb_printer_handle_reset(&s->dev); + return &s->dev; fail: - usb_printer_handle_destroy((USBDevice*)s); + usb_printer_handle_destroy(&s->dev); return nullptr; } - int PrinterDevice::Configure(int port, const std::string& api, void* data) + const char* PrinterDevice::Name() const { - return 0; + return "Printer"; } - int PrinterDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + const char* PrinterDevice::TypeName() const { - return 0; + return "printer"; + } + + bool PrinterDevice::Freeze(USBDevice* dev, StateWrapper& sw) const + { + PrinterState* s = USB_CONTAINER_OF(dev, PrinterState, dev); + + if (!sw.DoMarker("PrinterDevice")) + return false; + + sw.Do(&s->selected_printer); + sw.Do(&s->cmd_state); + sw.DoBytes(&s->last_command, sizeof(s->last_command)); + sw.Do(&s->last_command_size); + sw.Do(&s->width); + sw.Do(&s->height); + sw.Do(&s->stride); + sw.Do(&s->data_size); + sw.Do(&s->data_pos); + + // toss any file being saved when we're loading, since we'd probably + // end up with a corrupted file otherwise + if (sw.IsReading()) + sony_cancel_file(s); + + return true; + } + + std::vector PrinterDevice::SubTypes() const + { + std::vector ret; + for (uint32_t i = 0; i < sizeof(sPrinters) / sizeof(sPrinters[0]); i++) + { + ret.push_back(sPrinters[i].commercial_name); + } + return ret; } } // namespace usb_printer diff --git a/pcsx2/USB/usb-printer/usb-printer.h b/pcsx2/USB/usb-printer/usb-printer.h index 24489cf313..e0b1b5c9ca 100644 --- a/pcsx2/USB/usb-printer/usb-printer.h +++ b/pcsx2/USB/usb-printer/usb-printer.h @@ -13,11 +13,10 @@ * If not, see . */ -#ifndef USBPRINTER_H -#define USBPRINTER_H +#pragma once -#include "../deviceproxy.h" -#include "../qemu-usb/desc.h" +#include "USB/deviceproxy.h" +#include "USB/qemu-usb/desc.h" #define GET_DEVICE_ID 0 #define GET_PORT_STATUS 1 @@ -121,40 +120,15 @@ namespace usb_printer }, }; - static const char* APINAME = "default"; - - class PrinterDevice + class PrinterDevice final : public DeviceProxy { public: - virtual ~PrinterDevice() {} - static USBDevice* CreateDevice(int port); - static const TCHAR* Name() - { - return TEXT("Printer"); - } - static const char* TypeName() - { - return "printer"; - } - static std::list ListAPIs() - { - return std::list{APINAME}; - } - static const TCHAR* LongAPIName(const std::string& name) - { - return TEXT("Default"); - } - static int Configure(int port, const std::string& api, void* data); - static int Freeze(FreezeAction mode, USBDevice* dev, void* data); - static std::vector SubTypes() - { - std::vector ret; - for (uint32_t i = 0; i < sizeof(sPrinters) / sizeof(sPrinters[0]); i++) - { - ret.push_back(sPrinters[i].commercial_name); - } - return ret; - } + USBDevice* CreateDevice(SettingsInterface& si, u32 port, u32 subtype) const override; + const char* Name() const override; + const char* TypeName() const override; + + bool Freeze(USBDevice* dev, StateWrapper& sw) const override; + std::vector SubTypes() const override; }; #pragma pack(push, 1) @@ -173,4 +147,3 @@ namespace usb_printer #pragma pack(pop) } // namespace usb_printer -#endif diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 5b142de710..c6875af46b 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -273,11 +273,18 @@ bool VMManager::Internal::InitializeGlobals() return false; } + if (USBinit() != 0) + { + Host::ReportErrorAsync("Error", "Failed to initialize USB (USBinit())"); + return false; + } + return true; } void VMManager::Internal::ReleaseGlobals() { + USBshutdown(); SPU2shutdown(); GSshutdown(); @@ -993,14 +1000,13 @@ bool VMManager::Initialize(VMBootParameters boot_params) }; Console.WriteLn("Opening USB..."); - if (USBinit() != 0 || USBopen(g_host_display->GetWindowInfo()) != 0) + if (!USBopen()) { Host::ReportErrorAsync("Startup Error", "Failed to initialize USB."); return false; } ScopedGuard close_usb = []() { USBclose(); - USBshutdown(); }; Console.WriteLn("Opening FW..."); @@ -1140,7 +1146,6 @@ void VMManager::Shutdown(bool save_resume_state) GetMTGS().WaitForClose(); } - USBshutdown(); PADshutdown(); DEV9shutdown(); @@ -1804,6 +1809,7 @@ void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config) CheckForSPU2ConfigChanges(old_config); CheckForDEV9ConfigChanges(old_config); CheckForMemoryCardConfigChanges(old_config); + USB::CheckForConfigChanges(old_config); if (EmuConfig.EnableCheats != old_config.EnableCheats || EmuConfig.EnableWideScreenPatches != old_config.EnableWideScreenPatches || diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 8b9e81b331..bccc33ad59 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -31,7 +31,7 @@ #include "GS.h" #include "CDVD/CDVD.h" -#include "USB/USB.h" +#include "USB/USBNull.h" #include "Elfheader.h" #include "Patch.h" #include "R5900Exceptions.h" diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 310c44b90b..cd52c95443 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -23,7 +23,7 @@ #include "SPU2/spu2.h" #include "SysThreads.h" #include "DEV9/DEV9.h" -#include "USB/USB.h" +#include "USB/USBNull.h" #include "PAD/Gamepad.h" #include "ConsoleLogger.h" diff --git a/pcsx2/gui/SysCoreThread.cpp b/pcsx2/gui/SysCoreThread.cpp index a1edea4946..4a62792fd5 100644 --- a/pcsx2/gui/SysCoreThread.cpp +++ b/pcsx2/gui/SysCoreThread.cpp @@ -33,7 +33,7 @@ extern WindowInfo g_gs_window_info; #include "FW.h" #include "SPU2/spu2.h" #include "DEV9/DEV9.h" -#include "USB/USB.h" +#include "USB/USBNull.h" #include "MemoryCardFile.h" #include "PAD/Gamepad.h" #include "PerformanceMetrics.h" diff --git a/pcsx2/gui/SysState.cpp b/pcsx2/gui/SysState.cpp index e89eed4ff8..ccb33980fb 100644 --- a/pcsx2/gui/SysState.cpp +++ b/pcsx2/gui/SysState.cpp @@ -23,7 +23,7 @@ #include "common/StringUtil.h" #include "SPU2/spu2.h" -#include "USB/USB.h" +#include "USB/USBNull.h" #include "PAD/Gamepad.h" #include "ConsoleLogger.h" diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index e1e6fd0e0e..3aad85624c 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -378,51 +378,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -482,6 +437,7 @@ + true @@ -893,58 +849,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -963,6 +867,7 @@ + @@ -1091,9 +996,6 @@ - - - @@ -1109,9 +1011,6 @@ {d6973076-9317-4ef2-a0b8-b7a18ac0713e} - - {47afdbef-f15f-4bc0-b436-5be443c3f80f} - {0fae817d-9a32-4830-857e-81da57246e16} false diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 92f652c3e9..3e5cfc0c48 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -208,48 +208,6 @@ {df9de75c-2272-4f73-b2a0-4f9f492ba1e9} - - {f128e163-0f30-43eb-80a1-4739a6313c8f} - - - {5a206eba-a8b6-44ef-98e7-eb0384c9a896} - - - {c63d7e06-bbf9-48ec-aa38-d122aa6648cf} - - - {aee88110-a71e-4896-8b22-73e3198eb30a} - - - {1aa4f41d-451c-4599-9aa7-e2f51f5b467d} - - - {9c8b3479-af75-4999-b265-d0c8c66d0954} - - - {e4b2d8b4-a9fb-4c6d-b247-2112afd8d099} - - - {fe77f40c-84c0-4a06-affb-b6e7fb09ce49} - - - {a478e196-ac99-4a12-bfbb-2a7a3128d6e9} - - - {96b523da-ac91-4f95-81af-8df223188783} - - - {a12c76e1-5876-4ce2-aec6-427270102eed} - - - {b067682e-741d-4414-89ef-8ffc22aef8dc} - - - {8640e8ca-7d79-4221-b1bf-35bc142a9add} - - - {343981c6-ca99-462d-9b20-0500a23ae4cd} - {e1cbcaf6-9f65-4f14-9e89-27dd0f10e047} @@ -1289,141 +1247,6 @@ System\Ps2\DEV9 - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\usb-msd - - - System\Ps2\USB\usb-pad - - - System\Ps2\USB\usb-pad - - - System\Ps2\USB\usb-pad\lg - - - System\Ps2\USB\usb-pad - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\shared - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-msd - - - System\Ps2\USB\usb-printer - - - System\Ps2\USB\shared - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\raw - - - System\Ps2\USB\usb-pad\raw - - - System\Ps2\USB\usb-pad - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\Win32 - - - System\Ps2\USB\shared - - - System\Ps2\USB\shared - - - System\Ps2\USB\shared - System\Ps2\PAD @@ -1799,6 +1622,9 @@ System\Ps2\Iop + + System\Ps2\USB + @@ -2449,162 +2275,6 @@ System\Ps2\DEV9 - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB - - - System\Ps2\USB\readerwriterqueue - - - System\Ps2\USB\readerwriterqueue - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\usb-msd - - - System\Ps2\USB\usb-pad - - - System\Ps2\USB\usb-pad - - - System\Ps2\USB\usb-pad\lg - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\shared - - - System\Ps2\USB\usb-mic - - - System\Ps2\USB\usb-printer - - - System\Ps2\USB\shared - - - System\Ps2\USB\qemu-usb - - - System\Ps2\USB\usb-hid - - - System\Ps2\USB\usb-eyetoy - - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\raw - - - System\Ps2\USB\usb-pad\raw - - - System\Ps2\USB\Win32 - - - System\Ps2\USB\shared - - - System\Ps2\USB\shared - - - System\Ps2\USB\shared - - - System\Ps2\USB\Win32 - System\Ps2\PAD @@ -2993,6 +2663,9 @@ System\Ps2\Iop + + System\Ps2\USB + @@ -3007,15 +2680,6 @@ System\Ps2\SPU2 - - System\Ps2\USB\usb-pad\dx - - - System\Ps2\USB\usb-pad\raw - - - System\Ps2\USB\Win32 - System\Ps2\PAD diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index 84ab6c4160..147e0aacd6 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -333,9 +333,34 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -666,6 +691,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index f6f3b8979b..650eac372d 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -241,6 +241,39 @@ {78c9db9c-9c7c-4385-90e7-9fa71b922f60} + + {e068b724-9319-42e5-9ea7-63d80989ea1d} + + + {f82a2be4-24a1-4dd8-9395-c53d0e1c4ddb} + + + {58074375-dbbe-4137-bbe5-54478d1a97c7} + + + {b7da726d-4e91-48d4-a987-b624e87e4481} + + + {48a0208f-5734-4e6c-bb4d-3c16e0333a81} + + + {001fa220-df32-4da7-ba7e-2a7eca5cdb78} + + + {20789ef3-7c0e-41ac-babd-03a9be9fe994} + + + {c59653b3-87d6-4b11-b9e3-f0543117ca27} + + + {cab6864f-999a-4002-bd85-e1452c62efb8} + + + {72ca905b-3eaa-425a-b77a-3fe83869af72} + + + {b8542664-8208-48ad-bd47-425a316f293c} + @@ -1217,9 +1250,6 @@ System\Ps2\SPU2 - - System\Ps2\USB - Host @@ -1317,7 +1347,87 @@ System\Ps2\Iop - + + System\Ps2\USB + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\shared + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-printer + + + System\Ps2\USB\usb-msd + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-hid + + + System\Ps2\USB\usb-pad + + + System\Ps2\USB\usb-pad + + + System\Ps2\USB\usb-pad + + + System\Ps2\USB\usb-pad\lg + + + System\Ps2\USB\usb-lightgun + + + System + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB + + + System\Ps2\USB\usb-pad + @@ -1821,9 +1931,6 @@ System\Ps2\DEV9 - - System\Ps2\USB - System\Ps2\GS @@ -2193,7 +2300,99 @@ System\Ps2\Iop - + + System\Ps2\USB + + + System\Ps2\USB + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\qemu-usb + + + System\Ps2\USB\readerwriterqueue + + + System\Ps2\USB\readerwriterqueue + + + System\Ps2\USB\shared + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-eyetoy + + + System\Ps2\USB\usb-printer + + + System\Ps2\USB\usb-msd + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-hid + + + System\Ps2\USB\usb-pad + + + System\Ps2\USB\usb-pad\lg + + + System\Ps2\USB\usb-lightgun + + + System + + + System\Ps2\USB\usb-mic + + + System\Ps2\USB\usb-pad + @@ -2218,4 +2417,4 @@ System\Ps2\Debug\rdebug - + \ No newline at end of file diff --git a/pcsx2/ps2/Iop/IopHwRead.cpp b/pcsx2/ps2/Iop/IopHwRead.cpp index 483f2f36d7..7fabd4a924 100644 --- a/pcsx2/ps2/Iop/IopHwRead.cpp +++ b/pcsx2/ps2/Iop/IopHwRead.cpp @@ -22,7 +22,11 @@ #include "FW.h" #include "SPU2/spu2.h" #include "DEV9/DEV9.h" +#ifdef PCSX2_CORE #include "USB/USB.h" +#else +#include "USB/USBNull.h" +#endif #include "IopCounters.h" #include "IopDma.h" diff --git a/pcsx2/ps2/Iop/IopHwWrite.cpp b/pcsx2/ps2/Iop/IopHwWrite.cpp index a1e0378ef8..80e400338f 100644 --- a/pcsx2/ps2/Iop/IopHwWrite.cpp +++ b/pcsx2/ps2/Iop/IopHwWrite.cpp @@ -21,7 +21,11 @@ #include "CDVD/Ps1CD.h" #include "SPU2/spu2.h" #include "DEV9/DEV9.h" +#ifdef PCSX2_CORE #include "USB/USB.h" +#else +#include "USB/USBNull.h" +#endif #include "IopCounters.h" #include "IopDma.h" #include "R3000A.h"