From facfc3557cd8847d9be104d24ef8c6c69334a32a Mon Sep 17 00:00:00 2001 From: Matlo Date: Sun, 8 May 2016 20:28:50 +0200 Subject: [PATCH] Logitech GT Force emulation #381 --- config/gimx-config.cpp | 11 +++ config/gimx-config.h | 2 + config/wxsmith/configframe.wxs | 5 ++ shared/controller/include/defs.h | 1 + shared/controller/include/gtf_ps2.h | 38 +++++++++ shared/controller/include/report.h | 2 + shared/controller/src/gtf_ps2.c | 117 ++++++++++++++++++++++++++++ 7 files changed, 176 insertions(+) create mode 100644 shared/controller/include/gtf_ps2.h create mode 100644 shared/controller/src/gtf_ps2.c diff --git a/config/gimx-config.cpp b/config/gimx-config.cpp index c5581842..ace42e44 100644 --- a/config/gimx-config.cpp +++ b/config/gimx-config.cpp @@ -192,6 +192,7 @@ const long configFrame::ID_MENUITEMG27PS3 = wxNewId(); const long configFrame::ID_MENUITEMG29PS4 = wxNewId(); const long configFrame::ID_MENUITEMDFPS2 = wxNewId(); const long configFrame::ID_MENUITEMDFPPS2 = wxNewId(); +const long configFrame::ID_MENUITEMGTFPS2 = wxNewId(); const long configFrame::ID_MENUITEM8 = wxNewId(); const long configFrame::ID_MENUITEM9 = wxNewId(); const long configFrame::ID_MENUITEM10 = wxNewId(); @@ -1190,6 +1191,8 @@ configFrame::configFrame(wxString file,wxWindow* parent,wxWindowID id) MenuType->Append(MenuItemDfPs2); MenuItemDfpPs2 = new wxMenuItem(MenuType, ID_MENUITEMDFPPS2, _("Driving Force Pro PS2"), wxEmptyString, wxITEM_RADIO); MenuType->Append(MenuItemDfpPs2); + MenuItemGtfPs2 = new wxMenuItem(MenuType, ID_MENUITEMDFPPS2, _("GT Force PS2"), wxEmptyString, wxITEM_RADIO); + MenuType->Append(MenuItemGtfPs2); MenuBar1->Append(MenuType, _("Type")); MenuConfiguration = new wxMenu(); MenuConfiguration1 = new wxMenuItem(MenuConfiguration, ID_MENUITEM8, _("1"), wxEmptyString, wxITEM_RADIO); @@ -1305,6 +1308,7 @@ configFrame::configFrame(wxString file,wxWindow* parent,wxWindowID id) Connect(ID_MENUITEMG29PS4,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuTypeItemSelected); Connect(ID_MENUITEMDFPS2,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuTypeItemSelected); Connect(ID_MENUITEMDFPPS2,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuTypeItemSelected); + Connect(ID_MENUITEMGTFPS2,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuTypeItemSelected); Connect(ID_MENUITEM8,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuItemConfiguration); Connect(ID_MENUITEM9,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuItemConfiguration); Connect(ID_MENUITEM10,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&configFrame::OnMenuItemConfiguration); @@ -2205,6 +2209,9 @@ void configFrame::LoadControllerType() case C_TYPE_DFP_PS2: MenuType->Check(ID_MENUITEMDFPPS2, true); break; + case C_TYPE_GTF_PS2: + MenuType->Check(ID_MENUITEMGTFPS2, true); + break; case C_TYPE_NONE: break; } @@ -4087,6 +4094,10 @@ void configFrame::OnMenuTypeItemSelected(wxCommandEvent& event) { newType = C_TYPE_DFP_PS2; } + else if(MenuItemGtfPs2->IsChecked()) + { + newType = C_TYPE_GTF_PS2; + } Controller* controller = configFile.GetController(currentController); diff --git a/config/gimx-config.h b/config/gimx-config.h index 61421d30..d9aa1721 100644 --- a/config/gimx-config.h +++ b/config/gimx-config.h @@ -286,6 +286,7 @@ class configFrame: public wxFrame static const long ID_MENUITEMG29PS4; static const long ID_MENUITEMDFPS2; static const long ID_MENUITEMDFPPS2; + static const long ID_MENUITEMGTFPS2; static const long ID_MENUITEM8; static const long ID_MENUITEM9; static const long ID_MENUITEM10; @@ -349,6 +350,7 @@ class configFrame: public wxFrame wxMenuItem* MenuItemG29Ps4; wxMenuItem* MenuItemDfPs2; wxMenuItem* MenuItemDfpPs2; + wxMenuItem* MenuItemGtfPs2; wxStaticText* IntensityButtonId; wxGrid* GridMouseOption; wxTextCtrl* MouseOptionsBuffer; diff --git a/config/wxsmith/configframe.wxs b/config/wxsmith/configframe.wxs index 52d2e90b..089e14bf 100644 --- a/config/wxsmith/configframe.wxs +++ b/config/wxsmith/configframe.wxs @@ -1822,6 +1822,11 @@ 1 + + + + 1 + diff --git a/shared/controller/include/defs.h b/shared/controller/include/defs.h index 5b18360f..b4bc5b49 100644 --- a/shared/controller/include/defs.h +++ b/shared/controller/include/defs.h @@ -36,6 +36,7 @@ typedef enum C_TYPE_G29_PS4, C_TYPE_DF_PS2, C_TYPE_DFP_PS2, + C_TYPE_GTF_PS2, // <- add new types here C_TYPE_NONE, C_TYPE_MAX = C_TYPE_NONE, diff --git a/shared/controller/include/gtf_ps2.h b/shared/controller/include/gtf_ps2.h new file mode 100644 index 00000000..67f7399d --- /dev/null +++ b/shared/controller/include/gtf_ps2.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2016 Mathieu Laurendeau + License: GPLv3 + */ + +#ifndef GTF_PS2_H_ +#define GTF_PS2_H_ + +#include +#include + +typedef enum +{ + gtfPs2a_wheel = rel_axis_lstick_x, + gtfPs2a_triangle = abs_axis_7, + gtfPs2a_circle = abs_axis_8, + gtfPs2a_cross = abs_axis_9, + gtfPs2a_square = abs_axis_10, + gtfPs2a_l1 = abs_axis_11, + gtfPs2a_r1 = abs_axis_12, + gtfPs2a_gasPedal = abs_axis_17, + gtfPs2a_brakePedal = abs_axis_18, +} e_gtfPs2_axis_index; + +/* + * + */ +typedef struct GIMX_PACKED +{ + uint16_t buttonsAndWheel; // 10 LSB = wheel, 6 MSB = buttons + uint8_t : 8; + uint8_t pedals; // combined pedals + uint8_t gasPedal; + uint8_t brakePedal; + uint8_t : 8; +} s_report_gtfPs2; + +#endif /* GTF_PS2_H_ */ diff --git a/shared/controller/include/report.h b/shared/controller/include/report.h index a7bcf81c..54a2e80a 100644 --- a/shared/controller/include/report.h +++ b/shared/controller/include/report.h @@ -18,6 +18,7 @@ #include #include #include +#include #define MAX_REPORTS 2 @@ -35,6 +36,7 @@ typedef union s_report_g29Ps4 g29Ps4; s_report_dfPs2 dfPs2; s_report_dfpPs2 dfpPs2; + s_report_gtfPs2 gtfPs2; } s_report; typedef struct GIMX_PACKED diff --git a/shared/controller/src/gtf_ps2.c b/shared/controller/src/gtf_ps2.c new file mode 100644 index 00000000..45a99b21 --- /dev/null +++ b/shared/controller/src/gtf_ps2.c @@ -0,0 +1,117 @@ +/* + Copyright (c) 2016 Mathieu Laurendeau + License: GPLv3 + */ + +#include +#include +#include +#include + +#define GTF_SQUARE_MASK 0x0800 +#define GTF_CROSS_MASK 0x0400 +#define GTF_TRIANGLE_MASK 0x2000 +#define GTF_CIRCLE_MASK 0x1000 +#define GTF_R1_MASK 0x4000 +#define GTF_L1_MASK 0x8000 + + +static s_axis axes[AXIS_MAX] = +{ + [gtfPs2a_wheel] = { .name = "wheel", .max_unsigned_value = MAX_AXIS_VALUE_10BITS }, + + [gtfPs2a_gasPedal] = { .name = "gas", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_brakePedal] = { .name = "brake", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_triangle] = { .name = "triangle", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_circle] = { .name = "circle", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_cross] = { .name = "cross", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_square] = { .name = "square", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_l1] = { .name = "l1", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + [gtfPs2a_r1] = { .name = "r1", .max_unsigned_value = MAX_AXIS_VALUE_8BITS }, + +}; + +static s_axis_name_dir axis_name_dirs[] = +{ + {.name = "wheel", {.axis = gtfPs2a_wheel, .props = AXIS_PROP_CENTERED}}, + + {.name = "gas", {.axis = gtfPs2a_gasPedal, .props = AXIS_PROP_POSITIVE}}, + {.name = "brake", {.axis = gtfPs2a_brakePedal, .props = AXIS_PROP_POSITIVE}}, + + {.name = "r1", {.axis = gtfPs2a_r1, .props = AXIS_PROP_TOGGLE}}, + {.name = "l1", {.axis = gtfPs2a_l1, .props = AXIS_PROP_TOGGLE}}, + {.name = "circle", {.axis = gtfPs2a_circle, .props = AXIS_PROP_TOGGLE}}, + {.name = "square", {.axis = gtfPs2a_square, .props = AXIS_PROP_TOGGLE}}, + {.name = "cross", {.axis = gtfPs2a_cross, .props = AXIS_PROP_TOGGLE}}, + {.name = "triangle", {.axis = gtfPs2a_triangle, .props = AXIS_PROP_TOGGLE}}, +}; + +static s_report_gtfPs2 default_report = +{ + .buttonsAndWheel = CENTER_AXIS_VALUE_10BITS, + .pedals = CENTER_AXIS_VALUE_8BITS, + .gasPedal = MAX_AXIS_VALUE_8BITS, + .brakePedal = MAX_AXIS_VALUE_8BITS, +}; + +static void init_report(s_report * report) +{ + memcpy(report, &default_report, sizeof(default_report)); +} + +static unsigned int build_report(int axis[AXIS_MAX], s_report_packet report[MAX_REPORTS]) +{ + unsigned int index = 0; + report[index].length = sizeof(s_report_gtfPs2); + s_report_gtfPs2* gtfPs2 = &report[index].value.gtfPs2; + + gtfPs2->buttonsAndWheel = clamp(0, axis[gtfPs2a_wheel] + CENTER_AXIS_VALUE_10BITS, MAX_AXIS_VALUE_10BITS); + gtfPs2->gasPedal = clamp(0, MAX_AXIS_VALUE_8BITS - axis[gtfPs2a_gasPedal], MAX_AXIS_VALUE_8BITS); + gtfPs2->brakePedal = clamp(0, MAX_AXIS_VALUE_8BITS - axis[gtfPs2a_brakePedal], MAX_AXIS_VALUE_8BITS); + + gtfPs2->pedals = clamp(0, (axis[gtfPs2a_gasPedal] - axis[gtfPs2a_brakePedal]) / 2 + CENTER_AXIS_VALUE_8BITS, MAX_AXIS_VALUE_8BITS); + + if (axis[gtfPs2a_square]) + { + gtfPs2->buttonsAndWheel |= GTF_SQUARE_MASK; + } + if (axis[gtfPs2a_cross]) + { + gtfPs2->buttonsAndWheel |= GTF_CROSS_MASK; + } + if (axis[gtfPs2a_triangle]) + { + gtfPs2->buttonsAndWheel |= GTF_TRIANGLE_MASK; + } + if (axis[gtfPs2a_circle]) + { + gtfPs2->buttonsAndWheel |= GTF_CIRCLE_MASK; + } + if (axis[gtfPs2a_r1]) + { + gtfPs2->buttonsAndWheel |= GTF_R1_MASK; + } + if (axis[gtfPs2a_l1]) + { + gtfPs2->buttonsAndWheel |= GTF_L1_MASK; + } + + return index; +} + +static s_controller controller = +{ + .name = "GTF PS2", + .refresh_period = { .min_value = 10000, .default_value = 10000 }, + .axes = axes, + .axis_name_dirs = { .nb = sizeof(axis_name_dirs)/sizeof(*axis_name_dirs), .values = axis_name_dirs }, + .fp_build_report = build_report, + .fp_init_report = init_report, +}; + +void gtfPs2_init(void) __attribute__((constructor (101))); +void gtfPs2_init(void) +{ + controller_register(C_TYPE_GTF_PS2, &controller); +} +