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 7ea571af2223e..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; } 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/dx/dinput-config.cpp b/pcsx2/USB/usb-pad/dx/dinput-config.cpp index 9bbec4b696744..5a21c1ada27e3 100644 --- a/pcsx2/USB/usb-pad/dx/dinput-config.cpp +++ b/pcsx2/USB/usb-pad/dx/dinput-config.cpp @@ -1316,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) @@ -1460,11 +1460,11 @@ 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, "keyboardmania") == 0) + if (strcmp(dev_type, KeyboardmaniaDevice::TypeName()) == 0) { return DialogBoxParam(h.hInst, MAKEINTRESOURCE(IDD_DLG_KEYBOARDMANIA), h.hWnd, DxDialogProc, (LPARAM)&s); } 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..706d3909fb6a3 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); } @@ -220,7 +218,7 @@ namespace usb_pad //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: + //treat_me_like_ABS_RY: mWheelData.throttle = 0xFF; mWheelData.brake = 0xFF; if (value < 0) @@ -230,18 +228,10 @@ namespace usb_pad 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; @@ -323,9 +313,8 @@ namespace usb_pad 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); - */ + //if (event.code == 0) + // event.code, device.axis_map[event.code] & ~0x80, event.value, value); SetAxis(device, event.code, value); } break; @@ -333,7 +322,7 @@ namespace usb_pad { code = device.btn_map[event.code] != (uint16_t)-1 ? device.btn_map[event.code] : event.code; - if (mType == WT_BUZZ_CONTROLLER) + if (mType == WT_BUZZ_CONTROLLER || mType == WT_KEYBOARDMANIA_CONTROLLER) { if (device.btn_map[event.code] != (uint16_t)-1) { @@ -568,7 +557,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 +573,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,13 +616,6 @@ 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; switch (mType) { @@ -651,8 +623,13 @@ namespace usb_pad 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; + 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)) @@ -715,12 +692,6 @@ namespace usb_pad } } -#ifndef NDEBUG - for (int i = 0; i < ABS_MAX; ++i) - { - } -#endif - for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) { if (test_bit(i, keybit)) @@ -763,10 +734,6 @@ namespace usb_pad } return 0; - - quit: - Close(); - return 1; } int EvDevPad::Close() @@ -776,9 +743,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..1fcd838bd332d 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..."); + 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; - // let label change its text - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); + int value, initial = 0; + std::string dev_name; + bool inverted = false; + bool is_axis = (control >= cfg->max_buttons); - 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; - }); + gtk_label_set_text(GTK_LABEL(cfg->label), "Polling for input for 5 seconds..."); - 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), ""); - } - } + // let label change its text + while (gtk_events_pending()) + gtk_main_iteration_do(FALSE); - 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); - - 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..."); + auto it = std::find_if(cfg->jsconf.begin(), cfg->jsconf.end(), + [&dev_name](MappingPair& i) -> bool { + return i.first == dev_name; + }); - // let label change its text - while (gtk_events_pending()) - gtk_main_iteration_do(FALSE); - - 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,86 @@ 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; + GtkWidget* button; + + 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) //TODO idk about joydev + { + 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 +602,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, @@ -565,19 +616,19 @@ namespace usb_pad 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 +645,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,11 +705,15 @@ 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()) @@ -688,7 +743,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 +786,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 +799,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 +818,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 +828,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 +889,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 +942,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..87db09113c784 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 @@ -167,8 +208,9 @@ namespace usb_pad 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 index ede5c01b290dd..b849d56eb6613 100644 --- a/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp +++ b/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp @@ -32,10 +32,10 @@ namespace usb_pad #define JOYTYPE "joytype" #define CFG "cfg" - 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) { static char buf[256] = {0}; - if (map < evdev::JOY_STEERING) + if (is_button) { snprintf(buf, sizeof(buf), "Button %d", event); } diff --git a/pcsx2/USB/usb-pad/joydev/joydev.cpp b/pcsx2/USB/usb-pad/joydev/joydev.cpp index c9a9dfe59d11a..b08ee05c814f5 100644 --- a/pcsx2/USB/usb-pad/joydev/joydev.cpp +++ b/pcsx2/USB/usb-pad/joydev/joydev.cpp @@ -73,7 +73,6 @@ namespace usb_pad close(fd); } } - //quit: closedir(dirp); } @@ -381,7 +380,7 @@ namespace usb_pad continue; } - LoadMappings(mDevType, mPort, device.name, device.cfg); + LoadMappings(mDevType, mPort, device.name, 3, 16, device.cfg); // Axis Mapping if (ioctl(device.cfg.fd, JSIOCGAXMAP, device.axis_map) < 0) @@ -394,16 +393,17 @@ namespace usb_pad 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++) + for (int k = 0; k < count; k++) { - if (k == device.cfg.controls[i]) + for (int i = JOY_STEERING; i < JOY_MAPS_COUNT; i++) { - device.axis_map[k] = 0x80 | i; - if (i == JOY_STEERING) - has_steering = true; + if (k == device.cfg.controls[i]) + { + device.axis_map[k] = 0x80 | i; + if (i == JOY_STEERING) + has_steering = true; + } } } } diff --git a/pcsx2/USB/usb-pad/usb-pad.cpp b/pcsx2/USB/usb-pad/usb-pad.cpp index 5d3ade30e2f5c..a1edaf9a1efa0 100644 --- a/pcsx2/USB/usb-pad/usb-pad.cpp +++ b/pcsx2/USB/usb-pad/usb-pad.cpp @@ -52,6 +52,7 @@ namespace usb_pad "Logitech Buzz(tm) Controller V1", "", "Logitech"}; + static const USBDescStrings kbm_desc_strings = { "", "USB Multipurpose Controller", @@ -93,7 +94,7 @@ namespace usb_pad std::list KeyboardmaniaDevice::ListAPIs() { - return PadDevice::ListAPIs(); + return {"evdev"}; } const TCHAR* KeyboardmaniaDevice::LongAPIName(const std::string& name) @@ -319,7 +320,6 @@ namespace usb_pad s->pad->Close(); } - void pad_reset_data(generic_data_t* d) { memset(d, 0, sizeof(generic_data_t)); @@ -540,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; @@ -554,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(); @@ -610,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; @@ -682,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(); @@ -701,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; @@ -774,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; @@ -828,13 +802,13 @@ namespace usb_pad if (!proxy) { Console.WriteLn("usb-pad: %s: Invalid input API.", TypeName()); - return NULL; + return nullptr; } Pad* pad = proxy->CreateObject(port, TypeName()); if (!pad) - return NULL; + return nullptr; pad->Type(WT_KEYBOARDMANIA_CONTROLLER); PADState* s = new PADState(); @@ -847,23 +821,7 @@ namespace usb_pad if (usb_desc_parse_config(kbm_config_descriptor, sizeof(kbm_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; diff --git a/pcsx2/USB/usb-pad/usb-pad.h b/pcsx2/USB/usb-pad/usb-pad.h index d9b599efedad1..6f3f789084cec 100644 --- a/pcsx2/USB/usb-pad/usb-pad.h +++ b/pcsx2/USB/usb-pad/usb-pad.h @@ -109,7 +109,6 @@ namespace usb_pad { return {}; } - static void Initialize(); }; class SeamicDevice @@ -475,27 +474,30 @@ 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) ??? #define PID_DFGT 0xC29A #define PID_FORMULA 0xC202 //Yellow Wingman Formula -#define PID_FGP 0xC20E //Formula GP (maybe GT FORCE LPRC-1000) -#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_FGP 0xC20E //Formula GP (maybe GT FORCE LPRC-1000) +#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? **/