diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 01c8e8e301821..743fd2a8f7567 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -403,8 +403,6 @@ set(pcsx2USBSources USB/shared/ringbuffer.cpp USB/icon_buzz_24.cpp USB/usb-msd/usb-msd-gtk.cpp - USB/usb-pad/joydev/joydev.cpp - USB/usb-pad/joydev/joydev-gtk.cpp USB/usb-pad/evdev/shared-gtk.cpp USB/usb-pad/evdev/evdev-ff.cpp USB/usb-pad/evdev/evdev.cpp @@ -468,7 +466,6 @@ set(pcsx2USBHeaders USB/linux/util.h USB/gtk.h USB/icon_buzz_24.h - USB/usb-pad/joydev/joydev.h USB/usb-pad/evdev/shared.h USB/usb-pad/evdev/evdev.h USB/usb-pad/evdev/evdev-ff.h diff --git a/pcsx2/USB/device_init.cpp b/pcsx2/USB/device_init.cpp index 5b6e93e5e0124..b8afa97121045 100644 --- a/pcsx2/USB/device_init.cpp +++ b/pcsx2/USB/device_init.cpp @@ -39,6 +39,7 @@ void RegisterDevice::Register() inst.Add(DEVTYPE_EYETOY, new DeviceProxy()); inst.Add(DEVTYPE_BEATMANIA_DADADA, new DeviceProxy()); inst.Add(DEVTYPE_SEGA_SEAMIC, new DeviceProxy()); + inst.Add(DEVTYPE_KEYBOARDMANIA, new DeviceProxy()); RegisterAPIs(); } diff --git a/pcsx2/USB/deviceproxy.h b/pcsx2/USB/deviceproxy.h index 1e8e658f9331f..50f7ee4cab3b2 100644 --- a/pcsx2/USB/deviceproxy.h +++ b/pcsx2/USB/deviceproxy.h @@ -46,6 +46,7 @@ enum DeviceType DEVTYPE_EYETOY, DEVTYPE_BEATMANIA_DADADA, DEVTYPE_SEGA_SEAMIC, + DEVTYPE_KEYBOARDMANIA, }; struct SelectDeviceName @@ -210,7 +211,7 @@ class RegisterDevice return *registerDevice; } - ~RegisterDevice() { } + ~RegisterDevice() {} static void Register(); void Unregister(); diff --git a/pcsx2/USB/gtk.h b/pcsx2/USB/gtk.h index d5bd47646ce03..c8748b83e412e 100644 --- a/pcsx2/USB/gtk.h +++ b/pcsx2/USB/gtk.h @@ -12,6 +12,8 @@ * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ +#pragma once -#define GLIB_DISABLE_DEPRECATION_WARNINGS #include + +GtkWidget* new_combobox(const char* label, GtkWidget* vbox, bool scrollable = false); // linux/config-gtk.cpp diff --git a/pcsx2/USB/linux/config-gtk.cpp b/pcsx2/USB/linux/config-gtk.cpp index 3700621844535..176e4f9841562 100644 --- a/pcsx2/USB/linux/config-gtk.cpp +++ b/pcsx2/USB/linux/config-gtk.cpp @@ -189,7 +189,7 @@ static void configureApi(GtkWidget* widget, gpointer data) } } -GtkWidget* new_combobox(const char* label, GtkWidget* vbox) +GtkWidget* new_combobox(const char* label, GtkWidget* vbox, bool scrollable) { GtkWidget *rs_hbox, *rs_label, *rs_cb; @@ -199,10 +199,16 @@ GtkWidget* new_combobox(const char* label, GtkWidget* vbox) 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); - gtk_misc_set_alignment(GTK_MISC(rs_label), 1, 0.5); rs_cb = gtk_combo_box_text_new(); - gtk_box_pack_start(GTK_BOX(rs_hbox), rs_cb, TRUE, TRUE, 5); + 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; } @@ -235,7 +241,6 @@ void USBconfigure() const char* players[] = {"Player 1:", "Player 2:"}; GtkWidget *rs_cb, *vbox; - uint32_t sel_idx = 0; // Create the dialog window GtkWidget* dlg = gtk_dialog_new_with_buttons( diff --git a/pcsx2/USB/usb-eyetoy/cam-linux.cpp b/pcsx2/USB/usb-eyetoy/cam-linux.cpp index ea0b066d3dde9..19482e20f4ca3 100644 --- a/pcsx2/USB/usb-eyetoy/cam-linux.cpp +++ b/pcsx2/USB/usb-eyetoy/cam-linux.cpp @@ -35,8 +35,6 @@ #include -GtkWidget* new_combobox(const char* label, GtkWidget* vbox); // src/linux/config-gtk.cpp - #define CLEAR(x) memset(&(x), 0, sizeof(x)) namespace usb_eyetoy diff --git a/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp b/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp index 4bca91cb01c7c..6aa906676fc82 100644 --- a/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp +++ b/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp @@ -20,8 +20,6 @@ #include #include -GtkWidget* new_combobox(const char* label, GtkWidget* vbox); // src/linux/config-gtk.cpp - namespace usb_hid { namespace evdev diff --git a/pcsx2/USB/usb-mic/audiodev-pulse.cpp b/pcsx2/USB/usb-mic/audiodev-pulse.cpp index 9149298e45ac0..44f94299345c7 100644 --- a/pcsx2/USB/usb-mic/audiodev-pulse.cpp +++ b/pcsx2/USB/usb-mic/audiodev-pulse.cpp @@ -19,8 +19,6 @@ #include "USB/dynlink/pulse.h" #endif -GtkWidget* new_combobox(const char* label, GtkWidget* vbox); // src/linux/config-gtk.cpp - namespace usb_mic { namespace audiodev_pulse diff --git a/pcsx2/USB/usb-pad/api_init_linux.cpp b/pcsx2/USB/usb-pad/api_init_linux.cpp index d6c8184730c93..07ca03fe06ef4 100644 --- a/pcsx2/USB/usb-pad/api_init_linux.cpp +++ b/pcsx2/USB/usb-pad/api_init_linux.cpp @@ -15,11 +15,9 @@ #include "padproxy.h" #include "evdev/evdev.h" -#include "joydev/joydev.h" void usb_pad::RegisterPad::Register() { auto& inst = RegisterPad::instance(); inst.Add("evdev", new PadProxy()); - inst.Add("joydev", new PadProxy()); } diff --git a/pcsx2/USB/usb-pad/dx/dinput-config.cpp b/pcsx2/USB/usb-pad/dx/dinput-config.cpp index eff1e45ba69d0..5a21c1ada27e3 100644 --- a/pcsx2/USB/usb-pad/dx/dinput-config.cpp +++ b/pcsx2/USB/usb-pad/dx/dinput-config.cpp @@ -108,6 +108,17 @@ namespace usb_pad 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 @@ -990,6 +1001,14 @@ namespace usb_pad } } break; + case IDC_DELALL: + { + for (int i = 0; i < CID_COUNT; i++) + { + DeleteControl(s->port, (ControlID)i); + } + } + break; case IDC_ASS0: { @@ -1091,6 +1110,61 @@ namespace usb_pad 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); @@ -1242,7 +1316,7 @@ namespace usb_pad 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); + FFMULTI[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_GETPOS, 0, 0); } void SaveDInputConfig(int port, const char* dev_type) @@ -1386,10 +1460,14 @@ namespace usb_pad struct DXDlgSettings s; s.port = port; s.dev_type = dev_type; - if (strcmp(dev_type, "buzz_device") == 0) + 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); + } return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DIALOG1), h.hWnd, DxDialogProc, (LPARAM)&s); } diff --git a/pcsx2/USB/usb-pad/dx/dx.h b/pcsx2/USB/usb-pad/dx/dx.h index 84bb27491f986..060c0a31dc46b 100644 --- a/pcsx2/USB/usb-pad/dx/dx.h +++ b/pcsx2/USB/usb-pad/dx/dx.h @@ -74,6 +74,17 @@ namespace usb_pad 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, }; diff --git a/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp b/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp index a5c59b165b5c7..5468098a38dbe 100644 --- a/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp +++ b/pcsx2/USB/usb-pad/dx/usb-pad-dx.cpp @@ -58,6 +58,19 @@ namespace usb_pad return 5; } + 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) ; diff --git a/pcsx2/USB/usb-pad/dx/versionproxy.h b/pcsx2/USB/usb-pad/dx/versionproxy.h index 9b7f4200df23a..f8f74b4ebe063 100644 --- a/pcsx2/USB/usb-pad/dx/versionproxy.h +++ b/pcsx2/USB/usb-pad/dx/versionproxy.h @@ -5,6 +5,7 @@ #define IDC_STATIC -1 #define IDOK2 2 #define IDD_DLG_BUZZ 114 +#define IDD_DLG_KEYBOARDMANIA 115 #define IDD_DIALOG1 202 #define IDC_DEL0 1001 #define IDC_ASS0 1002 @@ -165,6 +166,29 @@ #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 // diff --git a/pcsx2/USB/usb-pad/dx/versionproxy.rc b/pcsx2/USB/usb-pad/dx/versionproxy.rc index b326ff49b5770..96a466892d6aa 100644 --- a/pcsx2/USB/usb-pad/dx/versionproxy.rc +++ b/pcsx2/USB/usb-pad/dx/versionproxy.rc @@ -256,6 +256,77 @@ BEGIN 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 + ///////////////////////////////////////////////////////////////////////////// // @@ -276,6 +347,10 @@ BEGIN IDD_DLG_BUZZ, DIALOG BEGIN END + + IDD_DLG_KEYBOARDMANIA, DIALOG + BEGIN + END END #endif // APSTUDIO_INVOKED @@ -295,6 +370,11 @@ BEGIN 0 END +IDD_DLG_KEYBOARDMANIA AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp b/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp index 28a5e965df64f..e4d6718966160 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp +++ b/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp @@ -279,8 +279,8 @@ namespace usb_pad "BRIGHTNESS_ZERO", /* linux:244 (KEY_BRIGHTNESS_ZERO) */ "DISPLAY_OFF", /* linux:245 (KEY_DISPLAY_OFF) */ "WIMAX", /* linux:246 (KEY_WIMAX) */ - "247", /* linux:247 (unnamed) */ - "248", /* linux:248 (unnamed) */ + "RFKILL", /* linux:247 (KEY_RFKILL) */ + "MICMUTE", /* linux:248 (KEY_MICMUTE) */ "249", /* linux:249 (unnamed) */ "250", /* linux:250 (unnamed) */ "251", /* linux:251 (unnamed) */ @@ -556,15 +556,15 @@ namespace usb_pad "NUMERIC_9", /* linux:521 (KEY_NUMERIC_9) */ "NUMERIC_STAR", /* linux:522 (KEY_NUMERIC_STAR) */ "NUMERIC_POUND", /* linux:523 (KEY_NUMERIC_POUND) */ - "RFKILL", /* linux:524 (KEY_RFKILL) */ + "KEY_NUMERIC_A", /* linux:524 (KEY_NUMERIC_A) */ }; - static bool GetEventName(const char* dev_type, int map, int event, const char** name) + static bool GetEventName(const char* dev_type, int map, int event, bool is_button, const char** name) { if (!name) return false; - if (map < JOY_STEERING || !strcmp(dev_type, BuzzDevice::TypeName())) + if (is_button) { if (event < (int)key_to_str.size()) { @@ -593,7 +593,7 @@ namespace usb_pad }; AxisValue axisVal[ABS_MAX + 1]{}; unsigned long absbit[NBITS(ABS_MAX)]{}; - struct axis_correct abs_correct[ABS_MAX]{}; + axis_correct abs_correct[ABS_MAX]{}; inverted = false; @@ -616,9 +616,7 @@ namespace usb_pad while ((len = read(js.second.fd, &event, sizeof(event))) > 0) ; - struct timeval timeout - { - }; + timeval timeout{}; timeout.tv_sec = 5; int result = select(maxfd + 1, &fdset, NULL, NULL, &timeout); @@ -744,6 +742,8 @@ namespace usb_pad 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; diff --git a/pcsx2/USB/usb-pad/evdev/evdev.cpp b/pcsx2/USB/usb-pad/evdev/evdev.cpp index 766764a7a7c57..5ea033db8d1b4 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev.cpp +++ b/pcsx2/USB/usb-pad/evdev/evdev.cpp @@ -108,7 +108,6 @@ namespace usb_pad } } } - //quit: closedir(dirp); return false; } @@ -151,7 +150,7 @@ namespace usb_pad str.clear(); str.str(""); str << EVDEV_DIR << dp->d_name; - std::string path = str.str(); + const std::string path = str.str(); auto it = std::find_if(list_cache.begin(), list_cache.end(), [&path](evdev_device& dev) { @@ -168,14 +167,14 @@ namespace usb_pad continue; } - //list_cache.push_back(std::make_pair(std::string(dp->d_name), path)); - res = ioctl(fd, EVIOCGNAME(sizeof(buf)), buf); if (res < 0) Console.Warning("EVIOCGNAME"); else { - list_cache.push_back({buf, dp->d_name, path}); + evdev_device dev{buf, dp->d_name, path, {}}; + res = ioctl(fd, EVIOCGID, &dev.input_id); + list_cache.push_back(dev); } close(fd); @@ -183,7 +182,6 @@ namespace usb_pad } list.assign(list_cache.begin(), list_cache.end()); - //quit: closedir(dirp); } @@ -214,34 +212,12 @@ namespace usb_pad switch (code) { case 0x80 | JOY_STEERING: - case ABS_X: mWheelData.steering = device.cfg.inverted[0] ? range - NORM(value, range) : NORM(value, range); break; - //case ABS_Y: mWheelData.clutch = NORM(value, 0xFF); break; //no wheel on PS2 has one, afaik - //case ABS_RX: mWheelData.axis_rx = NORM(event.value, 0xFF); break; - case ABS_RY: - //treat_me_like_ABS_RY: - mWheelData.throttle = 0xFF; - mWheelData.brake = 0xFF; - if (value < 0) - mWheelData.throttle = NORM2(value, 0xFF); - else - mWheelData.brake = NORM2(-value, 0xFF); - break; case 0x80 | JOY_THROTTLE: - case ABS_Z: - /*if (mIsGamepad) - mWheelData.brake = 0xFF - NORM(value, 0xFF); - else*/ mWheelData.throttle = device.cfg.inverted[1] ? NORM(value, 0xFF) : 0xFF - NORM(value, 0xFF); break; case 0x80 | JOY_BRAKE: - case ABS_RZ: - /*if (mIsGamepad) - mWheelData.throttle = 0xFF - NORM(value, 0xFF); - else if (mIsDualAnalog) - goto treat_me_like_ABS_RY; - else*/ mWheelData.brake = device.cfg.inverted[2] ? NORM(value, 0xFF) : 0xFF - NORM(value, 0xFF); break; @@ -314,7 +290,6 @@ namespace usb_pad for (int i = 0; i < len; i++) { input_event& event = events[i]; - int code, value; switch (event.type) { case EV_ABS: @@ -322,102 +297,24 @@ namespace usb_pad if (mType == WT_BUZZ_CONTROLLER) break; - value = AxisCorrect(device.abs_correct[event.code], event.value); - /*if (event.code == 0) - event.code, device.axis_map[event.code] & ~0x80, event.value, value); - */ + 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: { - code = device.btn_map[event.code] != (uint16_t)-1 ? device.btn_map[event.code] : event.code; - - if (mType == WT_BUZZ_CONTROLLER) - { - if (device.btn_map[event.code] != (uint16_t)-1) - { - if (event.value) - mWheelData.buttons |= 1 << (code & ~0x8000); //on - else - mWheelData.buttons &= ~(1 << (code & ~0x8000)); //off - } - + uint16_t button = device.btn_map[event.code]; + if (button == (uint16_t)-1 || !(button & 0x8000)) break; - } - PS2Buttons button = PAD_BUTTON_COUNT; - if (code >= (0x8000 | JOY_CROSS) && // user mapped - code <= (0x8000 | JOY_L3)) - { - button = (PS2Buttons)(code & ~0x8000); - } - else if (code >= BTN_TRIGGER && code < BTN_BASE5) // try to guess - { - button = (PS2Buttons)((code - BTN_TRIGGER) & ~0x8000); - } - else - { -#if 0 - // Map to xbox360ish controller - switch (code) - { - // Digital hatswitch - case 0x8000 | JOY_LEFT: - mWheelData.hat_horz = (!event.value ? PAD_HAT_COUNT : PAD_HAT_W); - break; - case 0x8000 | JOY_RIGHT: - mWheelData.hat_horz = (!event.value ? PAD_HAT_COUNT : PAD_HAT_E); - break; - case 0x8000 | JOY_UP: - mWheelData.hat_vert = (!event.value ? PAD_HAT_COUNT : PAD_HAT_N); - break; - case 0x8000 | JOY_DOWN: - mWheelData.hat_vert = (!event.value ? PAD_HAT_COUNT : PAD_HAT_S); - break; - case BTN_WEST: - button = PAD_SQUARE; - break; - case BTN_NORTH: - button = PAD_TRIANGLE; - break; - case BTN_EAST: - button = PAD_CIRCLE; - break; - case BTN_SOUTH: - button = PAD_CROSS; - break; - case BTN_SELECT: - button = PAD_SELECT; - break; - case BTN_START: - button = PAD_START; - break; - case BTN_TR: - button = PAD_R1; - break; - case BTN_TL: - button = PAD_L1; - break; - case BTN_TR2: - button = PAD_R2; - break; - case BTN_TL2: - button = PAD_L2; - break; - default: - break; - } -#endif - } + button = button & ~0x8000; - //if (button != PAD_BUTTON_COUNT) - { - if (event.value) - mWheelData.buttons |= 1 << convert_wt_btn(mType, button); //on - else - mWheelData.buttons &= ~(1 << convert_wt_btn(mType, button)); //off - } + 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? @@ -487,13 +384,15 @@ namespace usb_pad { if (mUseRawFF) { - - 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 + 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}; @@ -506,9 +405,12 @@ namespace usb_pad return 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); + 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; } @@ -568,7 +470,7 @@ namespace usb_pad } if (joypath.empty() || !file_exists(joypath)) - goto quit; + return 1; int fd = -1; if ((fd = open(joypath.c_str(), O_RDWR | O_NONBLOCK)) < 0) @@ -584,16 +486,6 @@ namespace usb_pad int pid, vid; if ((mUseRawFF = FindHidraw(evphys, hid_dev, &vid, &pid))) { - - // For safety, only allow Logitech (classic ffb) devices - if (vid != 0x046D /* Logitech */ /*|| info.bustype != BUS_USB*/ - || pid == 0xc262 /* G920 hid mode */ - || pid == 0xc261 /* G920 xbox mode */ - ) - { - mUseRawFF = 0; - } - // check if still using hidraw and run the thread if (mUseRawFF && !mWriterThreadIsRunning) { @@ -637,22 +529,24 @@ namespace usb_pad continue; } - /*unsigned int version; - if (ioctl(mHandle, EVIOCGVERSION, &version) < 0) - { - SysMessage("%s: Get version failed: %s\n", APINAME, strerror(errno)); - return false; - }*/ - - int max_buttons = JOY_STEERING; + // 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: - LoadMappings(mDevType, mPort, it.id, device.cfg); + 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)) @@ -683,30 +577,19 @@ namespace usb_pad continue; } - - //device.axis_map[i] = device.axes; - // convert values into 16 bit range CalcAxisCorr(device.abs_correct[i], absinfo); - //TODO joystick/gamepad is dual analog? - if (i == ABS_RZ) - { - //absinfo.value = AxisCorrect(mAbsCorrect[i], absinfo.value); - if (std::abs(absinfo.value) < 200) /* 200 is random, allows for some dead zone */ - device.is_dualanalog = true; - } - // FIXME axes as buttons - for (int k = max_buttons /*JOY_STEERING*/; k < JOY_MAPS_COUNT; k++) + for (int k = 0; k < max_axes; k++) { - if (i == device.cfg.controls[k]) + if (i == device.cfg.controls[k + max_buttons]) { has_mappings = true; - device.axis_map[i] = 0x80 | k; + 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 == JOY_STEERING && !mFFdev && !mUseRawFF) + if (k == 0 && !mFFdev && !mUseRawFF) { mFFdev = new EvdevFF(device.cfg.fd, b_gain, gain, b_ac, ac); } @@ -715,46 +598,16 @@ namespace usb_pad } } -#ifndef NDEBUG - for (int i = 0; i < ABS_MAX; ++i) - { - } -#endif - - for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) + for (int k = 0; k < max_buttons; k++) { - if (test_bit(i, keybit)) + auto i = device.cfg.controls[k]; + if (i >= 0 && i <= KEY_MAX) { - device.btn_map[i] = -1; //device.buttons; - if (i == BTN_GAMEPAD) - { - device.is_gamepad = true; - } - for (int k = 0; k < max_buttons; k++) - { - if (i == device.cfg.controls[k]) - { - has_mappings = true; - device.btn_map[i] = 0x8000 | k; - } - } - } - } - for (int i = 0; i < BTN_JOYSTICK; ++i) - { - if (test_bit(i, keybit)) - { - device.btn_map[i] = -1; //device.buttons; - for (int k = 0; k < max_buttons; k++) - { - if (i == device.cfg.controls[k]) - { - has_mappings = true; - device.btn_map[i] = 0x8000 | k; - } - } + has_mappings = true; + device.btn_map[i] = 0x8000 | k; } } + if (!has_mappings) { close(device.cfg.fd); @@ -763,10 +616,6 @@ namespace usb_pad } return 0; - - quit: - Close(); - return 1; } int EvDevPad::Close() @@ -776,9 +625,12 @@ namespace usb_pad if (mHidHandle != -1) { - uint8_t reset[7] = {0}; - reset[0] = 0xF3; //stop forces - write(mHidHandle, reset, sizeof(reset)); + if (mType <= WT_GT_FORCE) + { + uint8_t reset[7] = {0}; + reset[0] = 0xF3; //stop forces + write(mHidHandle, reset, sizeof(reset)); + } close(mHidHandle); } diff --git a/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp b/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp index d6e2d88a46afd..b10710a610c21 100644 --- a/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp +++ b/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp @@ -15,6 +15,7 @@ #include "shared.h" #include "USB/icon_buzz_24.h" +#include "Utilities/Console.h" #include #include @@ -29,37 +30,59 @@ namespace usb_pad using sys_clock = std::chrono::system_clock; using ms = std::chrono::milliseconds; -#define JOYTYPE "joytype" -#define CFG "cfg" + constexpr auto CONTROL = "control"; + constexpr auto CFG = "cfg"; - bool LoadMappings(const char* dev_type, int port, const std::string& joyname, ConfigMapping& 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) { - assert(JOY_MAPS_COUNT == countof(JoystickMapNames)); std::stringstream str; + const bool use_control_names = !strcmp(dev_type, PadDevice::TypeName()); if (joyname.empty()) return false; - int j = 0; - cfg.controls.resize(JOY_MAPS_COUNT); - for (auto& i : cfg.controls) + cfg.controls.resize(max_buttons + max_axes); + for (u32 i = 0; i < max_buttons + max_axes; i++) { str.clear(); str.str(""); - str << "map_" << JoystickMapNames[j++]; + 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)) - i = var; + cfg.controls[i] = var; else - i = -1; + cfg.controls[i] = -1; } for (int i = 0; i < 3; i++) { str.clear(); str.str(""); - str << "inverted_" << JoystickMapNames[JOY_STEERING + i]; + 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])) @@ -68,7 +91,12 @@ namespace usb_pad str.clear(); str.str(""); - str << "initial_" << JoystickMapNames[JOY_STEERING + i]; + 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])) @@ -78,29 +106,58 @@ namespace usb_pad return true; } - bool SaveMappings(const char* dev_type, int port, const std::string& joyname, const ConfigMapping& cfg) + bool SaveMappings(const char* dev_type, int port, const std::string& joyname, u32 max_buttons, u32 max_axes, const ConfigMapping& cfg) { - assert(JOY_MAPS_COUNT == countof(JoystickMapNames)); - if (joyname.empty() || cfg.controls.size() != JOY_MAPS_COUNT) + if (joyname.empty() || cfg.controls.size() != max_buttons + max_axes) return false; RemoveSection(dev_type, port, joyname); std::stringstream str; - for (int i = 0; i < JOY_MAPS_COUNT; i++) + 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(""); - str << "map_" << JoystickMapNames[i]; + 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 && !SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) - return false; + 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 (int i = 0; i < 3; i++) + for (u32 i = 0; i < 3 && has_axes; i++) { str.clear(); str.str(""); - str << "inverted_" << JoystickMapNames[JOY_STEERING + i]; + 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])) @@ -109,7 +166,12 @@ namespace usb_pad str.clear(); str.str(""); - str << "initial_" << JoystickMapNames[JOY_STEERING + i]; + 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])) @@ -169,48 +231,50 @@ namespace usb_pad static void refresh_store(ConfigData* cfg) { GtkTreeIter iter; + std::string name; gtk_list_store_clear(cfg->store); for (auto& it : cfg->jsconf) { - for (int i = 0; /*i < JOY_MAPS_COUNT && */ i < (int)it.second.controls.size(); i++) + for (size_t i = 0; i < it.second.controls.size(); i++) { if (it.second.controls[i] < 0) continue; const char* pc_name = "Unknown"; - cfg->cb->get_event_name(cfg->dev_type, i, it.second.controls[i], &pc_name); + 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)]; - - std::string 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_COLUMN_WIDTH, 50, - COL_BINDING, i, - -1); + 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 { - gtk_list_store_set(cfg->store, &iter, - COL_NAME, it.first.c_str(), - COL_PS2, JoystickMapNames[i], - COL_PC, pc_name, - COL_COLUMN_WIDTH, 50, - COL_BINDING, i, - -1); + 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); } } } @@ -219,90 +283,52 @@ namespace usb_pad { gint idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); //int port = reinterpret_cast(data); - ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); + ConfigData* cfg = reinterpret_cast(g_object_get_data(G_OBJECT(widget), CFG)); if (!cfg) return; if (idx > -1) - { - std::string name = (cfg->joysticks.begin() + idx)->name; cfg->js_iter = (cfg->joysticks.begin() + idx); - } } - static void button_clicked(GtkComboBox* widget, gpointer data) + static void button_clicked(GtkWidget* widget, gpointer data) { - int type = reinterpret_cast(g_object_get_data(G_OBJECT(widget), JOYTYPE)); - ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); - - if (cfg /*&& type < cfg->mappings.size() && cfg->js_iter != cfg->joysticks.end()*/) - { - int value, initial = 0; - std::string dev_name; - bool inverted = false; - bool is_axis = (type >= JOY_STEERING && type <= JOY_BRAKE); - - 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); + 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; - 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; - }); + int value, initial = 0; + std::string dev_name; + bool inverted = false; + bool is_axis = (control >= cfg->max_buttons); - if (it != cfg->jsconf.end() && type < (int)it->second.controls.size()) - { - it->second.controls[type] = value; - if (is_axis) - { - it->second.inverted[type - JOY_STEERING] = inverted; - it->second.initial[type - JOY_STEERING] = initial; - } - refresh_store(cfg); - } - } - gtk_label_set_text(GTK_LABEL(cfg->label), ""); - } - } + gtk_label_set_text(GTK_LABEL(cfg->label), "Polling for input for 5 seconds..."); - static void button_clicked_buzz(GtkComboBox* widget, gpointer data) - { - int type = reinterpret_cast(g_object_get_data(G_OBJECT(widget), JOYTYPE)); - ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); + // let label change its text + while (gtk_events_pending()) + gtk_main_iteration_do(FALSE); - if (cfg /*&& type < cfg->mappings.size() && cfg->js_iter != cfg->joysticks.end()*/) + if (cfg->cb->poll(cfg->jsconf, dev_name, is_axis, value, inverted, initial)) { - int value, initial = 0; - std::string dev_name; - bool inverted = false; - - 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); + auto it = std::find_if(cfg->jsconf.begin(), cfg->jsconf.end(), + [&dev_name](MappingPair& i) -> bool { + return i.first == dev_name; + }); - if (cfg->cb->poll(cfg->jsconf, dev_name, false, value, inverted, initial)) + if (it != cfg->jsconf.end() && control < (u32)it->second.controls.size()) { - 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() && type < (int)it->second.controls.size()) + it->second.controls[control] = value; + if (is_axis && control - cfg->max_buttons < countof(it->second.inverted)) { - it->second.controls[type] = value; - refresh_store(cfg); + 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), ""); } + gtk_label_set_text(GTK_LABEL(cfg->label), ""); } // save references to row paths, automatically updated when store changes @@ -349,13 +375,6 @@ namespace usb_pad gtk_tree_selection_selected_foreach(sel, view_selected_foreach_func, &rr_list); - /* // single row selection - GtkTreeIter iter; - - if (gtk_tree_selection_get_selected(sel, &model, &iter)) { - view_selected_foreach_func(model, nullptr, &iter, cfg); - }*/ - 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) @@ -364,14 +383,12 @@ namespace usb_pad if (path) { GtkTreeIter iter; - if (gtk_tree_model_get_iter(model, &iter, path)) { view_remove_binding(model, &iter, cfg); - //gtk_list_store_remove(GTK_LIST_STORE(model), &iter); } } - }; + } g_list_free_full(rr_list, (GDestroyNotify)gtk_tree_row_reference_free); g_list_free_full(list, (GDestroyNotify)gtk_tree_path_free); @@ -394,74 +411,50 @@ namespace usb_pad } } - int GtkPadConfigure(int port, const char* dev_type, const char* apititle, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs) + static GtkWidget* make_dialog(GtkWindow* parent, const std::string& title, int w = 1200, int h = 700) { - GtkWidget *ro_frame, *rs_cb; - GtkWidget *main_hbox, *right_vbox, *left_vbox, *treeview; - GtkWidget* button; - - 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; - - for (const auto& it : cfg.joysticks) - { - if ((fd = open(it.path.c_str(), O_RDONLY | O_NONBLOCK)) < 0) - { - continue; - } - - ConfigMapping c(fd); - LoadMappings(cfg.dev_type, port, it.id, c); - cfg.jsconf.push_back(std::make_pair(it.id, c)); - } - - refresh_store(&cfg); - - 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) //TODO idk about joydev - { - LoadSetting(dev_type, port, apiname, N_HIDRAW_FF_PT, cfg.use_hidraw_ff_pt); - } - - // --------------------------- - std::string title = (port ? "Player One " : "Player Two "); - title += apititle; - - GtkWidget* dlg = gtk_dialog_new_with_buttons( + 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), 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); + 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); - 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); + 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 + } - // --------------------------- - treeview = gtk_tree_view_new(); + 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); @@ -469,7 +462,7 @@ namespace usb_pad GtkCellRenderer* render = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), - -1, "Name", render, "text", COL_NAME, "width", COL_COLUMN_WIDTH, NULL); + -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), @@ -486,28 +479,85 @@ namespace usb_pad GtkWidget* scwin = gtk_scrolled_window_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(scwin), treeview); - //gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(scwin), 200); gtk_widget_set_size_request(GTK_WIDGET(scwin), 200, 100); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, - GTK_POLICY_ALWAYS); - gtk_box_pack_start(GTK_BOX(left_vbox), scwin, TRUE, TRUE, 5); + 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(left_vbox), button, FALSE, FALSE, 5); + 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(left_vbox), button, FALSE, FALSE, 5); + 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 *ro_frame, *rs_cb; + 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); + 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); @@ -551,7 +601,7 @@ namespace usb_pad 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), JOYTYPE, reinterpret_cast(button_pos[i].type)); + 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, @@ -563,21 +613,21 @@ namespace usb_pad GtkWidget* hbox = gtk_hbox_new(false, 5); gtk_container_add(GTK_CONTAINER(right_vbox), hbox); - button = gtk_button_new_with_label("Steering"); + 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), JOYTYPE, reinterpret_cast(JOY_STEERING)); + 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), JOYTYPE, reinterpret_cast(JOY_THROTTLE)); + 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), JOYTYPE, reinterpret_cast(JOY_BRAKE)); + 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)); @@ -594,7 +644,7 @@ namespace usb_pad GtkWidget* ff_scales[2]; int32_t ff_enabled[2]; - GtkWidget* table = gtk_table_new(3, 2, true); + 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 @@ -654,14 +704,18 @@ namespace usb_pad 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); - rs_cb = new_combobox("Device:", frame_vbox); + 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 (!strcmp(apiname, "evdev") && !it.id.empty()) + if (!it.id.empty()) str << " [" << it.id << "]"; gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), str.str().c_str()); @@ -688,7 +742,7 @@ namespace usb_pad } for (auto& it : cfg.jsconf) - SaveMappings(dev_type, port, it.first, it.second); + SaveMappings(dev_type, port, it.first, cfg.max_buttons, cfg.max_axes, it.second); if (is_evdev) { @@ -731,11 +785,10 @@ namespace usb_pad 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, *treeview; - GtkWidget* button; + GtkWidget *main_hbox, *right_vbox, *left_vbox; int fd; - ConfigData cfg; + ConfigData cfg{}; apicbs.populate(cfg.joysticks); @@ -745,6 +798,8 @@ namespace usb_pad 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) { @@ -762,17 +817,8 @@ namespace usb_pad refresh_store(&cfg); // --------------------------- - std::string title = "Buzz "; - title += apititle; - - GtkWidget* 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), 320, 240); + 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)); @@ -781,56 +827,21 @@ namespace usb_pad 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), left_vbox, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 5); - // --------------------------- - 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); + make_mappings_treeview(port, cfg, left_vbox); - GtkCellRenderer* render = gtk_cell_renderer_text_new(); - - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), - -1, "Name", render, "text", COL_NAME, "width", COL_COLUMN_WIDTH, 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_ALWAYS); - gtk_box_pack_start(GTK_BOX(left_vbox), scwin, TRUE, TRUE, 5); - - button = gtk_button_new_with_label("Clear binding"); - gtk_box_pack_start(GTK_BOX(left_vbox), 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(left_vbox), 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)); +#if GTK_CHECK_VERSION(3, 0, 0) + gtk_widget_set_vexpand(left_vbox, TRUE); +#endif // --------------------------- // Remapping { - GtkWidget* table = gtk_table_new(5, 4, true); + GtkWidget* table = gtk_table_new(5, 4, TRUE); gtk_container_add(GTK_CONTAINER(right_vbox), table); GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default @@ -877,9 +888,9 @@ namespace usb_pad 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_buzz), reinterpret_cast(port)); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast(port)); - g_object_set_data(G_OBJECT(button), JOYTYPE, reinterpret_cast(j * countof(buzz_btns) + buzz_btns[i])); + 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, @@ -930,5 +941,178 @@ namespace usb_pad 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 index 5b3919447a90e..65309a235da08 100644 --- a/pcsx2/USB/usb-pad/evdev/shared.h +++ b/pcsx2/USB/usb-pad/evdev/shared.h @@ -16,6 +16,7 @@ #pragma once #include #include +#include "Pcsx2Types.h" #include "USB/gtk.h" #include "USB/usb-pad/padproxy.h" #include "USB/configuration.h" @@ -31,10 +32,15 @@ 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; -GtkWidget* new_combobox(const char* label, GtkWidget* vbox); namespace usb_pad { @@ -46,7 +52,6 @@ namespace usb_pad COL_NAME = 0, COL_PS2, COL_PC, - COL_COLUMN_WIDTH, COL_BINDING, NUM_COLS }; @@ -76,7 +81,7 @@ namespace usb_pad JOY_MAPS_COUNT }; - static constexpr const char* JoystickMapNames[] = { + constexpr const char* JoystickMapNames[]{ "cross", "square", "circle", @@ -95,9 +100,10 @@ namespace usb_pad "right", "steering", "throttle", - "brake"}; + "brake", + }; - static constexpr const char* buzz_map_names[] = { + constexpr const char* buzz_map_names[]{ "red", "yellow", "green", @@ -105,6 +111,40 @@ namespace usb_pad "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; @@ -128,7 +168,7 @@ namespace usb_pad struct ApiCallbacks { - bool (*get_event_name)(const char* dev_type, int map, int event, const char** name); + 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); }; @@ -145,6 +185,7 @@ namespace usb_pad ApiCallbacks* cb; int use_hidraw_ff_pt; const char* dev_type; + u32 max_axes, max_buttons; }; struct axis_correct @@ -160,15 +201,13 @@ namespace usb_pad uint8_t axis_map[ABS_MAX + 1]; uint16_t btn_map[KEY_MAX + 1]; struct axis_correct abs_correct[ABS_MAX]; - bool is_gamepad; //xboxish gamepad - bool is_dualanalog; // tricky, have to read the AXIS_RZ somehow and - // determine if its unpressed value is zero }; 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); - bool LoadMappings(const char* dev_type, int port, const std::string& joyname, ConfigMapping& cfg); - bool SaveMappings(const char* dev_type, int port, const std::string& joyname, const ConfigMapping& cfg); + 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 diff --git a/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp b/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp deleted file mode 100644 index ede5c01b290dd..0000000000000 --- a/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp +++ /dev/null @@ -1,178 +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 "joydev.h" - -#include -#include -#include -#include -#include "USB/gtk.h" - -namespace usb_pad -{ - namespace joydev - { - - using sys_clock = std::chrono::system_clock; - using ms = std::chrono::milliseconds; - -#define JOYTYPE "joytype" -#define CFG "cfg" - - static bool GetEventName(const char* dev_type, int map, int event, const char** name) - { - static char buf[256] = {0}; - if (map < evdev::JOY_STEERING) - { - snprintf(buf, sizeof(buf), "Button %d", event); - } - else - { - // assuming that PS2 axes are always mapped to PC axes - snprintf(buf, sizeof(buf), "Axis %d", event); - } - *name = buf; - 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; - struct js_event event; - - 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; - } - - inverted = false; - - // empty event queues - for (const auto& js : fds) - while ((len = read(js.second.fd, &event, sizeof(event))) > 0) - ; - - struct axis_value - { - int16_t value; - bool initial; - }; - axis_value axisVal[ABS_MAX + 1] = {0}; - - struct 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; - - 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 ((len = read(event_fd, &event, sizeof(event))) > -1 && (len == sizeof(event))) - { - if (isaxis && event.type == JS_EVENT_AXIS) - { - auto& val = axisVal[event.number]; - - if (!val.initial) - { - val.value = event.value; - val.initial = true; - } - else - { - int diff = event.value - val.value; - if (std::abs(diff) > 2047) - { - value = event.number; - inverted = (diff < 0); - initial = val.value; - break; - } - } - } - else if (!isaxis && event.type == JS_EVENT_BUTTON) - { - if (event.value) - { - value = event.number; - 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 JoyDevPad::Configure(int port, const char* dev_type, void* data) - { - if (!strcmp(dev_type, BuzzDevice::TypeName())) - return RESULT_CANCELED; - - evdev::ApiCallbacks apicbs{GetEventName, EnumerateDevices, PollInput}; - int ret = evdev::GtkPadConfigure(port, dev_type, "Joydev Settings", joydev::APINAME, GTK_WINDOW(data), apicbs); - return ret; - } - - } // namespace joydev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/joydev/joydev.cpp b/pcsx2/USB/usb-pad/joydev/joydev.cpp deleted file mode 100644 index c9a9dfe59d11a..0000000000000 --- a/pcsx2/USB/usb-pad/joydev/joydev.cpp +++ /dev/null @@ -1,497 +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 "joydev.h" -#include "USB/linux/util.h" -#include "Utilities/Console.h" -#include -#include - -namespace usb_pad -{ - namespace joydev - { - - using namespace evdev; - -#define NORM(x, n) (((uint32_t)(32768 + x) * n) / 0xFFFF) -#define NORM2(x, n) (((uint32_t)(32768 + x) * n) / 0x7FFF) - - void EnumerateDevices(device_list& list) - { - int fd; - int res; - char buf[256]; - - std::stringstream str; - struct dirent* dp; - - DIR* dirp = opendir("/dev/input/"); - if (!dirp) - { - Console.Warning("Error opening /dev/input/"); - return; - } - - while ((dp = readdir(dirp))) - { - if (strncmp(dp->d_name, "js", 2) == 0) - { - - str.clear(); - str.str(""); - str << "/dev/input/" << dp->d_name; - const std::string& path = str.str(); - fd = open(path.c_str(), O_RDONLY | O_NONBLOCK); - - if (fd < 0) - { - Console.Warning("Joydev: Unable to open device: %s", path.c_str()); - continue; - } - - res = ioctl(fd, JSIOCGNAME(sizeof(buf)), buf); - if (res < 0) - Console.Warning("JSIOCGNAME"); - else - { - list.push_back({buf, buf, path}); - } - - close(fd); - } - } - //quit: - closedir(dirp); - } - - int JoyDevPad::TokenIn(uint8_t* buf, int buflen) - { - ssize_t len; - struct js_event events[32]; - fd_set fds; - int maxfd; - - int range = range_max(mType); - - 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++) - { - js_event& event = events[i]; - if ((event.type & ~JS_EVENT_INIT) == JS_EVENT_AXIS) - { - switch (device.axis_map[event.number]) - { - case 0x80 | JOY_STEERING: - case ABS_X: - mWheelData.steering = device.cfg.inverted[0] ? range - NORM(event.value, range) : NORM(event.value, range); - break; - case ABS_Y: - mWheelData.clutch = NORM(event.value, 0xFF); - break; - //case ABS_RX: mWheelData.axis_rx = NORM(event.value, 0xFF); break; - case ABS_RY: - treat_me_like_ABS_RY: - mWheelData.throttle = 0xFF; - mWheelData.brake = 0xFF; - if (event.value < 0) - mWheelData.throttle = NORM2(event.value, 0xFF); - else - mWheelData.brake = NORM2(-event.value, 0xFF); - break; - case 0x80 | JOY_THROTTLE: - case ABS_Z: - if (device.is_gamepad) - mWheelData.brake = 0xFF - NORM(event.value, 0xFF); - else - mWheelData.throttle = device.cfg.inverted[1] ? NORM(event.value, 0xFF) : 0xFF - NORM(event.value, 0xFF); - break; - case 0x80 | JOY_BRAKE: - case ABS_RZ: - if (device.is_gamepad) - mWheelData.throttle = 0xFF - NORM(event.value, 0xFF); - else if (device.is_dualanalog) - goto treat_me_like_ABS_RY; - else - mWheelData.brake = device.cfg.inverted[2] ? NORM(event.value, 0xFF) : 0xFF - NORM(event.value, 0xFF); - break; - - //FIXME hatswitch mapping maybe - case ABS_HAT0X: - case ABS_HAT1X: - case ABS_HAT2X: - case ABS_HAT3X: - if (event.value < 0) //left usually - mWheelData.hat_horz = PAD_HAT_W; - else if (event.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 (event.value < 0) //up usually - mWheelData.hat_vert = PAD_HAT_N; - else if (event.value > 0) //down - mWheelData.hat_vert = PAD_HAT_S; - else - mWheelData.hat_vert = PAD_HAT_COUNT; - break; - default: - break; - } - } - else if ((event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) - { - PS2Buttons button = PAD_BUTTON_COUNT; - if (device.btn_map[event.number] >= (0x8000 | JOY_CROSS) && - device.btn_map[event.number] <= (0x8000 | JOY_L3)) - { - button = (PS2Buttons)(device.btn_map[event.number] & ~0x8000); - } - - else if (device.btn_map[event.number] >= BTN_TRIGGER && - device.btn_map[event.number] < BTN_BASE5) - { - button = (PS2Buttons)(device.btn_map[event.number] - BTN_TRIGGER); - } - else - { - // Map to xbox360ish controller - switch (device.btn_map[event.number]) - { - // Digital hatswitch - case 0x8000 | JOY_LEFT: - mWheelData.hat_horz = PAD_HAT_W; - break; - case 0x8000 | JOY_RIGHT: - mWheelData.hat_horz = PAD_HAT_E; - break; - case 0x8000 | JOY_UP: - mWheelData.hat_vert = PAD_HAT_N; - break; - case 0x8000 | JOY_DOWN: - mWheelData.hat_vert = PAD_HAT_S; - break; - case BTN_WEST: - button = PAD_SQUARE; - break; - case BTN_NORTH: - button = PAD_TRIANGLE; - break; - case BTN_EAST: - button = PAD_CIRCLE; - break; - case BTN_SOUTH: - button = PAD_CROSS; - break; - case BTN_SELECT: - button = PAD_SELECT; - break; - case BTN_START: - button = PAD_START; - break; - case BTN_TR: - button = PAD_R1; - break; - case BTN_TL: - button = PAD_L1; - break; - case BTN_THUMBR: - button = PAD_R2; - break; - case BTN_THUMBL: - button = PAD_L2; - break; - default: - break; - } - } - - //if (button != PAD_BUTTON_COUNT) - { - if (event.value) - mWheelData.buttons |= 1 << convert_wt_btn(mType, button); //on - else - mWheelData.buttons &= ~(1 << convert_wt_btn(mType, button)); //off - } - } - } - - 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 JoyDevPad::TokenOut(const uint8_t* data, int len) - { - const ff_data* ffdata = (const ff_data*)data; - bool hires = (mType == WT_DRIVING_FORCE_PRO); - ParseFFData(ffdata, hires); - - return len; - } - - int JoyDevPad::Open() - { - device_list device_list; - bool has_steering; - int count; - int32_t b_gain, gain, b_ac, ac; - memset(&mWheelData, 0, sizeof(wheel_data_t)); - - // 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; - - mHandleFF = -1; - - std::string joypath; - /*if (!LoadSetting(mDevType, mPort, APINAME, N_JOYSTICK, joypath)) - { - return 1; - }*/ - - EnumerateDevices(device_list); - - 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; - - for (const auto& it : device_list) - { - has_steering = 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 flags = fcntl(device.fd, F_GETFL, 0); - //fcntl(device.fd, F_SETFL, flags | O_NONBLOCK); - - unsigned int version; - if (ioctl(device.cfg.fd, JSIOCGVERSION, &version) < 0) - { - SysMessage("%s: Get version failed: %s\n", APINAME, strerror(errno)); - continue; - } - - if (version < 0x010000) - { - SysMessage("%s: Driver version 0x%X is too old\n", APINAME, version); - continue; - } - - LoadMappings(mDevType, mPort, device.name, device.cfg); - - // Axis Mapping - if (ioctl(device.cfg.fd, JSIOCGAXMAP, device.axis_map) < 0) - { - SysMessage("%s: Axis mapping failed: %s\n", APINAME, strerror(errno)); - continue; - } - else - { - if (ioctl(device.cfg.fd, JSIOCGAXES, &(count)) >= 0) - { - for (int i = 0; i < count; ++i) - - for (int k = 0; k < count; k++) - { - for (int i = JOY_STEERING; i < JOY_MAPS_COUNT; i++) - { - if (k == device.cfg.controls[i]) - { - device.axis_map[k] = 0x80 | i; - if (i == JOY_STEERING) - has_steering = true; - } - } - } - } - } - - // Button Mapping - if (ioctl(device.cfg.fd, JSIOCGBTNMAP, device.btn_map) < 0) - { - SysMessage("%s: Button mapping failed: %s\n", APINAME, strerror(errno)); - continue; - } - else - { - if (ioctl(device.cfg.fd, JSIOCGBUTTONS, &(count)) >= 0) - { - for (int i = 0; i < count; ++i) - { - if (device.btn_map[i] == BTN_GAMEPAD) - device.is_gamepad = true; - } - - if (!device.is_gamepad) //TODO Don't remap if gamepad? - for (int k = 0; k < count; k++) - { - for (int i = 0; i < JOY_STEERING; i++) - { - if (k == device.cfg.controls[i]) - device.btn_map[k] = 0x8000 | i; - } - } - } - } - - std::stringstream event; - int index = 0; - const char* tmp = it.path.c_str(); - while (*tmp && !isdigit(*tmp)) - tmp++; - - sscanf(tmp, "%d", &index); - - //TODO kernel limit is 32? - for (int j = 0; j <= 99; j++) - { - event.clear(); - event.str(std::string()); - /* Try to discover the corresponding event number */ - event << "/sys/class/input/js" << index << "/device/event" << j; - if (dir_exists(event.str())) - { - - event.clear(); - event.str(std::string()); - event << "/dev/input/event" << j; - break; - } - } - - if (!mFFdev && has_steering) - { - if ((mHandleFF = open(event.str().c_str(), /*O_WRONLY*/ O_RDWR)) < 0) - { - } - else - mFFdev = new evdev::EvdevFF(mHandleFF, b_gain, gain, b_ac, ac); - } - } - - return 0; - } - - int JoyDevPad::Close() - { - delete mFFdev; - mFFdev = nullptr; - - if (mHandleFF != -1) - close(mHandleFF); - - mHandleFF = -1; - for (auto& it : mDevices) - { - close(it.cfg.fd); - it.cfg.fd = -1; - } - return 0; - } - - } // namespace joydev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/joydev/joydev.h b/pcsx2/USB/usb-pad/joydev/joydev.h deleted file mode 100644 index acd64efa888f7..0000000000000 --- a/pcsx2/USB/usb-pad/joydev/joydev.h +++ /dev/null @@ -1,84 +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/linux/util.h" -#include "USB/usb-pad/evdev/evdev-ff.h" -#include "USB/usb-pad/evdev/shared.h" -#include "Utilities/Console.h" - -namespace usb_pad -{ - namespace joydev - { - - void EnumerateDevices(device_list& list); - - static constexpr const char* APINAME = "joydev"; - - class JoyDevPad : public Pad - { - public: - JoyDevPad(int port, const char* dev_type) - : Pad(port, dev_type) - { - } - - ~JoyDevPad() { 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 "Joydev"; - } - - static int Configure(int port, const char* dev_type, void* data); - - protected: - int mHandleFF = -1; - struct wheel_data_t mWheelData - { - }; - std::vector mDevices; - }; - - template - bool GetJoystickName(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, JSIOCGNAME(_Size), name) < -1) - { - Console.Warning("Cannot get controller's name\n"); - close(fd); - return false; - } - close(fd); - return true; - } - return false; - } - - } // namespace joydev -} // namespace usb_pad diff --git a/pcsx2/USB/usb-pad/raw/raw-config.cpp b/pcsx2/USB/usb-pad/raw/raw-config.cpp index b124d07286bc8..bc47eb95ae8c3 100644 --- a/pcsx2/USB/usb-pad/raw/raw-config.cpp +++ b/pcsx2/USB/usb-pad/raw/raw-config.cpp @@ -313,7 +313,7 @@ namespace usb_pad HidP_GetCaps(pPreparsedData, &caps); if (caps.UsagePage == HID_USAGE_PAGE_GENERIC && - caps.Usage == HID_USAGE_GENERIC_JOYSTICK) + (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); diff --git a/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp b/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp index 032ffe5a57f5d..4cfd9d579d28f 100644 --- a/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp +++ b/pcsx2/USB/usb-pad/raw/usb-pad-raw.cpp @@ -443,7 +443,10 @@ namespace usb_pad mOLWrite.hEvent = CreateEvent(0, 0, 0, 0); HidD_GetAttributes(mUsbHandle, &(attr)); - if (attr.VendorID != PAD_VID || attr.ProductID == 0xC262) + + 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; diff --git a/pcsx2/USB/usb-pad/usb-pad-ff.cpp b/pcsx2/USB/usb-pad/usb-pad-ff.cpp index b5e31e93db539..89924cbace399 100644 --- a/pcsx2/USB/usb-pad/usb-pad-ff.cpp +++ b/pcsx2/USB/usb-pad/usb-pad-ff.cpp @@ -102,7 +102,7 @@ namespace usb_pad static int warned = 0; int caps = 0; - DevCon.WriteLn("FFB %02X, %02X, %02X, %02X : %02X, %02X, %02X, %02X\n", + 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); diff --git a/pcsx2/USB/usb-pad/usb-pad.cpp b/pcsx2/USB/usb-pad/usb-pad.cpp index a75c3922cc598..5b0e603eeeadb 100644 --- a/pcsx2/USB/usb-pad/usb-pad.cpp +++ b/pcsx2/USB/usb-pad/usb-pad.cpp @@ -53,6 +53,12 @@ namespace usb_pad "", "Logitech"}; + static const USBDescStrings kbm_desc_strings = { + "", + "USB Multipurpose Controller", + "", + "KONAMI"}; + std::list PadDevice::ListAPIs() { return RegisterPad::instance().Names(); @@ -86,6 +92,16 @@ namespace usb_pad return PadDevice::LongAPIName(name); } + std::list KeyboardmaniaDevice::ListAPIs() + { + return PadDevice::ListAPIs(); + } + + const TCHAR* KeyboardmaniaDevice::LongAPIName(const std::string& name) + { + return PadDevice::LongAPIName(name); + } + #ifdef _DEBUG void PrintBits(void* data, int size) { @@ -100,7 +116,6 @@ namespace usb_pad *(ptrB++) = ' '; } *ptrB = '\0'; - } #else @@ -240,6 +255,11 @@ namespace usb_pad ret = sizeof(pad_gtforce_hid_report_descriptor); memcpy(data, pad_gtforce_hid_report_descriptor, ret); } + else if (t == WT_KEYBOARDMANIA_CONTROLLER) + { + ret = sizeof(kbm_hid_report_descriptor); + memcpy(data, kbm_hid_report_descriptor, ret); + } else { ret = sizeof(pad_driving_force_hid_separate_report_descriptor); @@ -300,7 +320,6 @@ namespace usb_pad s->pad->Close(); } - void pad_reset_data(generic_data_t* d) { memset(d, 0, sizeof(generic_data_t)); @@ -409,6 +428,14 @@ namespace usb_pad 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; } @@ -513,6 +540,28 @@ namespace usb_pad #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); + } + USBDevice* PadDevice::CreateDevice(int port) { std::string varApi; @@ -527,13 +576,13 @@ namespace usb_pad if (!proxy) { Console.WriteLn("USB: PAD: Invalid input API.\n"); - return NULL; + return nullptr; } Pad* pad = proxy->CreateObject(port, TypeName()); if (!pad) - return NULL; + return nullptr; pad->Type((PS2WheelTypes)GetSelectedSubtype(std::make_pair(port, TypeName()))); PADState* s = new PADState(); @@ -583,23 +632,7 @@ namespace usb_pad if (usb_desc_parse_config(config_desc, config_desc_len, s->desc_dev) < 0) goto fail; - s->f.dev_subtype = pad->Type(); - 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_init(s, port, pad); return (USBDevice*)s; @@ -655,13 +688,13 @@ namespace usb_pad if (!proxy) { Console.WriteLn("RBDK: Invalid input API.\n"); - return NULL; + return nullptr; } Pad* pad = proxy->CreateObject(port, TypeName()); if (!pad) - return NULL; + return nullptr; pad->Type(WT_ROCKBAND1_DRUMKIT); PADState* s = new PADState(); @@ -674,23 +707,7 @@ namespace usb_pad if (usb_desc_parse_config(rb1_config_descriptor, sizeof(rb1_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); + pad_init(s, port, pad); return (USBDevice*)s; @@ -747,23 +764,7 @@ namespace usb_pad if (usb_desc_parse_config(buzz_config_descriptor, sizeof(buzz_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); + pad_init(s, port, pad); return (USBDevice*)s; @@ -785,4 +786,61 @@ namespace usb_pad return PadDevice::Freeze(mode, dev, data); } + // ---- Keyboardmania ---- + + USBDevice* KeyboardmaniaDevice::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("usb-pad: %s: Invalid input API.", TypeName()); + return nullptr; + } + + Pad* pad = proxy->CreateObject(port, TypeName()); + + if (!pad) + return nullptr; + + pad->Type(WT_KEYBOARDMANIA_CONTROLLER); + PADState* s = new PADState(); + + s->desc.full = &s->desc_dev; + s->desc.str = kbm_desc_strings; + + if (usb_desc_parse_dev(kbm_dev_descriptor, sizeof(kbm_dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(kbm_config_descriptor, sizeof(kbm_config_descriptor), s->desc_dev) < 0) + goto fail; + + pad_init(s, port, pad); + + return (USBDevice*)s; + + fail: + pad_handle_destroy((USBDevice*)s); + 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(int 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 74f73bf9b297d..6f3f789084cec 100644 --- a/pcsx2/USB/usb-pad/usb-pad.h +++ b/pcsx2/USB/usb-pad/usb-pad.h @@ -45,7 +45,7 @@ namespace usb_pad class PadDevice { public: - virtual ~PadDevice() { } + virtual ~PadDevice() {} static USBDevice* CreateDevice(int port); static const TCHAR* Name() { @@ -109,7 +109,6 @@ namespace usb_pad { return {}; } - static void Initialize(); }; class SeamicDevice @@ -135,6 +134,29 @@ namespace usb_pad } }; + class KeyboardmaniaDevice + { + 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(int mode, USBDevice* dev, void* data); + static std::vector SubTypes() + { + return {}; + } + }; + // Most likely as seen on https://github.com/matlo/GIMX #define CMD_DOWNLOAD 0x00 #define CMD_DOWNLOAD_AND_PLAY 0x01 @@ -184,6 +206,7 @@ namespace usb_pad WT_ROCKBAND1_DRUMKIT, WT_BUZZ_CONTROLLER, WT_SEGA_SEAMIC, + WT_KEYBOARDMANIA_CONTROLLER, }; inline int range_max(PS2WheelTypes type) @@ -451,7 +474,7 @@ namespace usb_pad static const int HATS_8TO4[] = {PAD_HAT_N, PAD_HAT_E, PAD_HAT_S, PAD_HAT_W}; #define PAD_VID 0x046D -#define PAD_PID 0xCA03 //black MOMO +#define PAD_MOMO 0xCA03 //black MOMO #define GENERIC_PID 0xC294 //actually Driving Force aka PID that most logitech wheels initially report #define PID_DF 0xC294 #define PID_DFP 0xC298 //SELECT + R3 + RIGHT SHIFT PADDLE (R1) ??? @@ -461,17 +484,20 @@ namespace usb_pad #define PID_FFGP 0xC293 // Formula Force GP #define PID_GTF 0xC293 // as is Formula Force GP #define PID_G25 0xC299 // OutRun 2 (jp) supports it apparently +#define PID_G27 0xC29B #define MAX_BUTTONS 32 #define MAX_AXES 7 //random 7: axes + hatswitch #define MAX_JOYS 32 +#define PAD_LG_FFB_WHITELIST \ + PAD_MOMO, PID_DF, PID_DFP, PID_DFGT, PID_FORMULA, PID_FGP, PID_FFGP, PID_GTF, PID_G25, PID_G27 /** linux hid-lg4ff.c http://www.spinics.net/lists/linux-input/msg16570.html - Every Logitech wheel reports itself as generic Logitech Driving Force wheel (VID 046d, PID c294). This is done to ensure that the - wheel will work on every USB HID-aware system even when no Logitech driver is available. It however limits the capabilities of the - wheel - range is limited to 200 degrees, G25/G27 don't report the clutch pedal and there is only one combined axis for throttle and - brake. The switch to native mode is done via hardware-specific command which is different for each wheel. When the wheel + Every Logitech wheel reports itself as generic Logitech Driving Force wheel (VID 046d, PID c294). This is done to ensure that the + wheel will work on every USB HID-aware system even when no Logitech driver is available. It however limits the capabilities of the + wheel - range is limited to 200 degrees, G25/G27 don't report the clutch pedal and there is only one combined axis for throttle and + brake. The switch to native mode is done via hardware-specific command which is different for each wheel. When the wheel receives such command, it simulates reconnect and reports to the OS with its actual PID. Currently not emulating reattachment. Any games that expect to? **/ @@ -1121,7 +1147,7 @@ namespace usb_pad 0x03, // bmAttributes (Interrupt) 0x40, 0x00, // wMaxPacketSize 64 0x0A, // bInterval 10 (unit depends on device speed) - // 41 bytes + // 41 bytes }; //Wii Rock Band drum kit @@ -1294,6 +1320,141 @@ namespace usb_pad // 78 bytes }; + /////////////////// + // Keyboardmania // + /////////////////// + static const uint8_t kbm_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 + 0x07, 0x05, // idVendor 0x0507 + 0x10, 0x00, // idProduct 0x0010 + 0x00, 0x01, // bcdDevice 01.00 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + + static const uint8_t kbm_config_descriptor[] = { + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x22, 0x00, // wTotalLength 34 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0x80, // bmAttributes + 0x19, // bMaxPower 50mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints 1 + 0x03, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x02, // iInterface (String Index) + + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x10, 0x01, // bcdHID 1.11 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0x96, 0x00, // wDescriptorLength[0] 150 + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x81, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) + 0x08, 0x00, // wMaxPacketSize 8 + 0x04, // bInterval 4 (unit depends on device speed) + }; + + static const uint8_t kbm_hid_report_descriptor[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x05, // USAGE (Game Pad) + 0xA1, 0x01, // COLLECTION (Application) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x3A, // USAGE_MINIMUM (Button 58) + 0x29, 0x3F, // USAGE_MAXIMUM (Button 63) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x06, // REPORT_COUNT (6) + 0x81, 0x02, // INPUT (Data,Variable,Absolute,NoWrap,Linear,PrefState,NoNull,NonVolatile,Bitmap) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x07, // USAGE_MAXIMUM (Button 7) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x07, // REPORT_COUNT (7) + 0x81, 0x02, // INPUT (Data,Variable,Absolute,NoWrap,Linear,PrefState,NoNull,NonVolatile,Bitmap) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x08, // USAGE_MINIMUM (Button 8) + 0x29, 0x0E, // USAGE_MAXIMUM (Button 14) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x07, // REPORT_COUNT (7) + 0x81, 0x02, // INPUT (Data,Variable,Absolute,NoWrap,Linear,PrefState,NoNull,NonVolatile,Bitmap) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x0F, // USAGE_MINIMUM (Button 15) + 0x29, 0x15, // USAGE_MAXIMUM (Button 21) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x07, // REPORT_COUNT (7) + 0x81, 0x02, // INPUT (Data,Variable,Absolute,NoWrap,Linear,PrefState,NoNull,NonVolatile,Bitmap) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x16, // USAGE_MINIMUM (Button 22) + 0x29, 0x1C, // USAGE_MAXIMUM (Button 28) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x07, // REPORT_COUNT (7) + 0x81, 0x02, // INPUT (Data,Variable,Absolute,NoWrap,Linear,PrefState,NoNull,NonVolatile,Bitmap) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x01, // USAGE (Pointer) + 0xA1, 0x00, // COLLECTION (Physical) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x15, 0xFF, // LOGICAL_MINIMUM (-1) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x02, // REPORT_COUNT (2) + 0x75, 0x02, // REPORT_SIZE (2) + 0x81, 0x02, // INPUT (Data,Variable,Absolute,NoWrap,Linear,PrefState,NoNull,NonVolatile,Bitmap) + 0x95, 0x04, // REPORT_COUNT (4) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0xC0, // END_COLLECTION + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x01, // INPUT (Constant,Array,Absolute) + 0xc0 // END_COLLECTION + }; + struct dfp_buttons_t { uint16_t cross : 1;