USB: Add evdev support for keyboardmania

This commit is contained in:
jackun 2021-01-03 16:04:28 +02:00
parent 238da17196
commit d0ada6b40a
14 changed files with 585 additions and 426 deletions

View File

@ -12,6 +12,8 @@
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include <gtk/gtk.h>
GtkWidget* new_combobox(const char* label, GtkWidget* vbox, bool scrollable = false); // linux/config-gtk.cpp

View File

@ -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;
}

View File

@ -35,8 +35,6 @@
#include <linux/videodev2.h>
GtkWidget* new_combobox(const char* label, GtkWidget* vbox); // src/linux/config-gtk.cpp
#define CLEAR(x) memset(&(x), 0, sizeof(x))
namespace usb_eyetoy

View File

@ -20,8 +20,6 @@
#include <cstdio>
#include <sstream>
GtkWidget* new_combobox(const char* label, GtkWidget* vbox); // src/linux/config-gtk.cpp
namespace usb_hid
{
namespace evdev

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -15,6 +15,7 @@
#include "shared.h"
#include "USB/icon_buzz_24.h"
#include "Utilities/Console.h"
#include <chrono>
#include <thread>
@ -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<int32_t>(cfg.controls[i])))
return false;
if (cfg.controls[i] >= 0)
{
if (!SaveSetting(dev_type, port, joyname, name.c_str(), static_cast<int32_t>(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<uintptr_t>(data);
ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG);
ConfigData* cfg = reinterpret_cast<ConfigData*>(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<uintptr_t>(g_object_get_data(G_OBJECT(widget), JOYTYPE));
ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG);
u32 control = reinterpret_cast<uintptr_t>(g_object_get_data(G_OBJECT(widget), CONTROL));
ConfigData* cfg = reinterpret_cast<ConfigData*>(g_object_get_data(G_OBJECT(widget), CFG));
if (!cfg)
return;
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 = (control >= cfg->max_buttons);
gtk_label_set_text(GTK_LABEL(cfg->label), "Polling for input for 5 seconds...");
// let label change its text
while (gtk_events_pending())
gtk_main_iteration_do(FALSE);
if (cfg->cb->poll(cfg->jsconf, dev_name, is_axis, value, inverted, initial))
{
int value, initial = 0;
std::string dev_name;
bool inverted = false;
bool is_axis = (type >= JOY_STEERING && type <= JOY_BRAKE);
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...");
// let label change its text
while (gtk_events_pending())
gtk_main_iteration_do(FALSE);
if (cfg->cb->poll(cfg->jsconf, dev_name, is_axis, value, inverted, initial))
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;
if (is_axis)
{
it->second.inverted[type - JOY_STEERING] = inverted;
it->second.initial[type - JOY_STEERING] = initial;
}
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), "");
}
}
static void button_clicked_buzz(GtkComboBox* widget, gpointer data)
{
int type = reinterpret_cast<uintptr_t>(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;
gtk_label_set_text(GTK_LABEL(cfg->label), "Polling for input for 5 seconds...");
// let label change its text
while (gtk_events_pending())
gtk_main_iteration_do(FALSE);
if (cfg->cb->poll(cfg->jsconf, dev_name, false, value, inverted, initial))
{
auto it = std::find_if(cfg->jsconf.begin(), cfg->jsconf.end(),
[&dev_name](MappingPair& i) -> bool {
return i.first == dev_name;
});
if (it != cfg->jsconf.end() && type < (int)it->second.controls.size())
{
it->second.controls[type] = value;
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);
// ---------------------------
treeview = gtk_tree_view_new();
#if 0
GtkWidget* paned = gtk_hpaned_new();
gtk_container_add(GTK_CONTAINER(container), paned);
gtk_paned_add1(GTK_PANED(paned), left_vbox);
GtkWidget* sc_win = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(sc_win), right_vbox);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sc_win), GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_paned_add2(GTK_PANED(paned), sc_win);
#else
GtkWidget* hbox = gtk_hbox_new(FALSE, 5);
gtk_container_add(GTK_CONTAINER(container), hbox);
gtk_box_pack_start(GTK_BOX(hbox), left_vbox, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox), right_vbox, TRUE, TRUE, 5);
#endif
#if GTK_CHECK_VERSION(3, 0, 0)
gtk_widget_set_vexpand(left_vbox, TRUE);
gtk_widget_set_valign(right_vbox, GTK_ALIGN_START);
#endif
}
static GtkWidget* make_mappings_treeview(int port, ConfigData& cfg, GtkWidget* container)
{
GtkWidget* button;
auto treeview = gtk_tree_view_new();
cfg.treeview = GTK_TREE_VIEW(treeview);
auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
@ -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<gpointer>(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<gpointer>(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<gpointer>(port));
g_object_set_data(G_OBJECT(button), JOYTYPE, reinterpret_cast<gpointer>(button_pos[i].type));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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<gpointer>(JOY_STEERING));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(JOY_STEERING));
g_object_set_data(G_OBJECT(button), CFG, &cfg);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast<gpointer>(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<gpointer>(JOY_THROTTLE));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(JOY_THROTTLE));
g_object_set_data(G_OBJECT(button), CFG, &cfg);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast<gpointer>(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<gpointer>(JOY_BRAKE));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(JOY_BRAKE));
g_object_set_data(G_OBJECT(button), CFG, &cfg);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast<gpointer>(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<gboolean*>(&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<uint16_t> 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<gpointer>(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<gpointer>(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<gpointer>(port));
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), reinterpret_cast<gpointer>(port));
g_object_set_data(G_OBJECT(button), JOYTYPE, reinterpret_cast<gpointer>(j * countof(buzz_btns) + buzz_btns[i]));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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<gpointer>(port));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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<gpointer>(port));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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<gpointer>(port));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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<gpointer>(port));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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<gpointer>(port));
g_object_set_data(G_OBJECT(button), CONTROL, reinterpret_cast<gpointer>(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

View File

@ -16,6 +16,7 @@
#pragma once
#include <linux/joystick.h>
#include <unistd.h>
#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<evdev_device> 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<std::pair<std::string, ConfigMapping>>& 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

View File

@ -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);
}

View File

@ -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;
}
}
}
}

View File

@ -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<std::string> 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;

View File

@ -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?
**/