diff --git a/config/gimx-config.cpp b/config/gimx-config.cpp index 1680ade8..68dbbe2b 100644 --- a/config/gimx-config.cpp +++ b/config/gimx-config.cpp @@ -107,6 +107,16 @@ const long configFrame::ID_BUTTON16 = wxNewId(); const long configFrame::ID_BUTTON17 = wxNewId(); const long configFrame::ID_BUTTON18 = wxNewId(); const long configFrame::ID_PANEL7 = wxNewId(); +const long configFrame::ID_STATICTEXT13 = wxNewId(); +const long configFrame::ID_STATICTEXT20 = wxNewId(); +const long configFrame::ID_STATICTEXT25 = wxNewId(); +const long configFrame::ID_STATICTEXT49 = wxNewId(); +const long configFrame::ID_BUTTON20 = wxNewId(); +const long configFrame::ID_STATICLINE13 = wxNewId(); +const long configFrame::ID_CHECKBOX2 = wxNewId(); +const long configFrame::ID_STATICLINE15 = wxNewId(); +const long configFrame::ID_BUTTON24 = wxNewId(); +const long configFrame::ID_PANEL8 = wxNewId(); const long configFrame::ID_NOTEBOOK2 = wxNewId(); const long configFrame::ID_PANEL1 = wxNewId(); const long configFrame::ID_STATICTEXT38 = wxNewId(); @@ -557,6 +567,7 @@ configFrame::configFrame(wxString file,wxWindow* parent, wxWindowID id __attribu wxFlexGridSizer* FlexGridSizer51; wxFlexGridSizer* FlexGridSizer15; wxStaticBoxSizer* StaticBoxSizer7; + wxFlexGridSizer* FlexGridSizer56; wxFlexGridSizer* FlexGridSizer50; wxStaticBoxSizer* StaticBoxSizer5; wxFlexGridSizer* FlexGridSizer17; @@ -575,6 +586,7 @@ configFrame::configFrame(wxString file,wxWindow* parent, wxWindowID id __attribu wxFlexGridSizer* FlexGridSizer26; wxFlexGridSizer* FlexGridSizer14; wxStaticBoxSizer* StaticBoxSizer3; + wxFlexGridSizer* FlexGridSizer55; wxFlexGridSizer* FlexGridSizer6; wxFlexGridSizer* FlexGridSizer33; wxFlexGridSizer* FlexGridSizer38; @@ -586,6 +598,7 @@ configFrame::configFrame(wxString file,wxWindow* parent, wxWindowID id __attribu wxStaticBoxSizer* StaticBoxSizer8; wxFlexGridSizer* FlexGridSizer31; wxFlexGridSizer* FlexGridSizer43; + wxFlexGridSizer* FlexGridSizer39; wxStaticBoxSizer* StaticBoxSizer4; wxStaticBoxSizer* StaticBoxSizer9; wxStaticBoxSizer* StaticBoxSizer6; @@ -593,12 +606,14 @@ configFrame::configFrame(wxString file,wxWindow* parent, wxWindowID id __attribu wxFlexGridSizer* FlexGridSizer34; wxFlexGridSizer* FlexGridSizer23; wxFlexGridSizer* FlexGridSizer10; + wxStaticBoxSizer* StaticBoxSizer11; wxFlexGridSizer* FlexGridSizer13; wxMenuBar* MenuBar1; wxMenuItem* MenuItemAbout; wxFlexGridSizer* FlexGridSizer53; wxFlexGridSizer* FlexGridSizer18; wxFlexGridSizer* FlexGridSizer49; + wxFlexGridSizer* FlexGridSizer54; wxFlexGridSizer* FlexGridSizer36; wxFlexGridSizer* FlexGridSizer12; wxMenuItem* MenuItemOpen; @@ -895,10 +910,45 @@ configFrame::configFrame(wxString file,wxWindow* parent, wxWindowID id __attribu PanelJoystickCorrections->SetSizer(FlexGridSizer10); FlexGridSizer10->Fit(PanelJoystickCorrections); FlexGridSizer10->SetSizeHints(PanelJoystickCorrections); + PanelForceFeedback = new wxPanel(Notebook2, ID_PANEL8, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL8")); + FlexGridSizer39 = new wxFlexGridSizer(2, 1, 0, 0); + FlexGridSizer54 = new wxFlexGridSizer(1, 6, 0, 0); + StaticBoxSizer11 = new wxStaticBoxSizer(wxHORIZONTAL, PanelForceFeedback, _("Joystick")); + FlexGridSizer55 = new wxFlexGridSizer(1, 4, 0, 0); + FFBTweaksType = new wxStaticText(PanelForceFeedback, ID_STATICTEXT13, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT13")); + FFBTweaksType->Hide(); + FlexGridSizer55->Add(FFBTweaksType, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FFBTweaksName = new wxStaticText(PanelForceFeedback, ID_STATICTEXT20, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT20")); + FlexGridSizer55->Add(FFBTweaksName, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FFBTweaksId = new wxStaticText(PanelForceFeedback, ID_STATICTEXT25, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT25")); + FlexGridSizer55->Add(FFBTweaksId, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FFBTweaksAxis = new wxStaticText(PanelForceFeedback, ID_STATICTEXT49, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT49")); + FFBTweaksAxis->Hide(); + FlexGridSizer55->Add(FFBTweaksAxis, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + StaticBoxSizer11->Add(FlexGridSizer55, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FlexGridSizer54->Add(StaticBoxSizer11, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + ForceFeedbackAutoDetect = new wxButton(PanelForceFeedback, ID_BUTTON20, _("Auto detect"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON20")); + FlexGridSizer54->Add(ForceFeedbackAutoDetect, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + StaticLine13 = new wxStaticLine(PanelForceFeedback, ID_STATICLINE13, wxDefaultPosition, wxSize(-1,50), wxLI_VERTICAL, _T("ID_STATICLINE13")); + FlexGridSizer54->Add(StaticLine13, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FlexGridSizer56 = new wxFlexGridSizer(1, 1, 0, 0); + FFBTweaksInvert = new wxCheckBox(PanelForceFeedback, ID_CHECKBOX2, _("Invert direction"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX2")); + FFBTweaksInvert->SetValue(false); + FlexGridSizer56->Add(FFBTweaksInvert, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FlexGridSizer54->Add(FlexGridSizer56, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + StaticLine15 = new wxStaticLine(PanelForceFeedback, ID_STATICLINE15, wxDefaultPosition, wxSize(-1,50), wxLI_VERTICAL, _T("ID_STATICLINE15")); + FlexGridSizer54->Add(StaticLine15, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + ButtonFFBTweaksDelete = new wxButton(PanelForceFeedback, ID_BUTTON24, _("Delete"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON24")); + FlexGridSizer54->Add(ButtonFFBTweaksDelete, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + FlexGridSizer39->Add(FlexGridSizer54, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + PanelForceFeedback->SetSizer(FlexGridSizer39); + FlexGridSizer39->Fit(PanelForceFeedback); + FlexGridSizer39->SetSizeHints(PanelForceFeedback); Notebook2->AddPage(PanelTrigger, _("Profile trigger"), false); Notebook2->AddPage(PanelMouseOptions, _("Mouse options"), false); Notebook2->AddPage(PanelIntensity, _("Axis intensity"), false); Notebook2->AddPage(PanelJoystickCorrections, _("Joystick corrections"), false); + Notebook2->AddPage(PanelForceFeedback, _("Force Feedback Tweaks"), false); GridSizer3->Add(Notebook2, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); PanelOverall->SetSizer(GridSizer3); GridSizer3->Fit(PanelOverall); @@ -1258,6 +1308,8 @@ configFrame::configFrame(wxString file,wxWindow* parent, wxWindowID id __attribu Connect(ID_BUTTON16,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnJoystickCorrectionsAddClick); Connect(ID_BUTTON17,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnJoystickCorrectionsRemoveClick); Connect(ID_BUTTON18,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnJoystickCorrectionsModifyClick); + Connect(ID_BUTTON20,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnButtonForceFeedbackAutoDetect); + Connect(ID_BUTTON24,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnButtonFFBTweaksDelete); Connect(ID_CHOICE4,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&configFrame::OnButtonTabEventTypeSelect); Connect(ID_BUTTON8,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnButtonTabAutoDetectClick); Connect(ID_BUTTON4,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&configFrame::OnButtonAddPanelButton); @@ -2131,6 +2183,11 @@ void configFrame::save_current() string(GridJoystickCorrections->GetCellValue(i, 5).mb_str(wxConvUTF8)), string(GridJoystickCorrections->GetCellValue(i, 6).mb_str(wxConvUTF8)))); } + //Save FFB tweaks + configuration->GetForceFeedback()->GetJoystick()->SetType(reverseTranslate(string(FFBTweaksType->GetLabel().mb_str(wxConvUTF8)))); + configuration->GetForceFeedback()->GetJoystick()->SetName(ffbTweaksTabDeviceName); + configuration->GetForceFeedback()->GetJoystick()->SetId(string(FFBTweaksId->GetLabel().mb_str(wxConvUTF8))); + configuration->GetForceFeedback()->setInversion(FFBTweaksInvert->GetValue() ? "yes" : "no"); //Save ControlMappers buttonMappers = configuration->GetButtonMapperList(); buttonMappers->erase(buttonMappers->begin(), buttonMappers->end()); @@ -2341,6 +2398,19 @@ void configFrame::load_current() GridJoystickCorrections->SetCellValue(0, 6, wxString(it->GetHighCoef().c_str(),wxConvUTF8)); } GridJoystickCorrections->AutoSizeColumns(); + //Load Force Feedback Tweaks + ForceFeedback * tweaks = configuration->GetForceFeedback(); + FFBTweaksType->SetLabel(_CN(tweaks->GetJoystick()->GetType())); + ffbTweaksTabDeviceName = tweaks->GetJoystick()->GetName(); + name = ffbTweaksTabDeviceName; + if(name.size() > 20) + { + name = name.substr(0,20); + name.append("..."); + } + FFBTweaksName->SetLabel(wxString(name.c_str(),wxConvUTF8)); + FFBTweaksId->SetLabel(wxString(tweaks->GetJoystick()->GetId().c_str(),wxConvUTF8)); + FFBTweaksInvert->SetValue(tweaks->getInversion() == "yes"); //Load buttonMappers clearGrid(GridPanelButton); buttonMappers = configuration->GetButtonMapperList(); @@ -2444,6 +2514,7 @@ void configFrame::refresh_gui() PanelTrigger->Layout(); PanelMouseOptions->Layout(); PanelJoystickCorrections->Layout(); + PanelForceFeedback->Layout(); PanelIntensity->Layout(); PanelOverall->Layout(); PanelButton->Layout(); @@ -4335,3 +4406,38 @@ pair configFrame::selectEvent() return make_pair(Device(), Event()); } + +void configFrame::OnButtonForceFeedbackAutoDetect(wxCommandEvent& event __attribute__((unused))) +{ + if (evcatch->hasJoystick() == false) + { + wxMessageBox(_("No joystick found!"), _("Error"), wxICON_ERROR); + } + + ForceFeedbackAutoDetect->Enable(false); + + FFBTweaksType->SetLabel(wxT("")); + + while(FFBTweaksType->GetLabel() != _("joystick")) + { + auto_detect(FFBTweaksType, &ffbTweaksTabDeviceName, FFBTweaksName, FFBTweaksId, _("axis"), FFBTweaksAxis); + } + + ForceFeedbackAutoDetect->Enable(true); + + refresh_gui(); +} + + +void configFrame::OnButtonFFBTweaksDelete(wxCommandEvent& event __attribute__((unused))) +{ + FFBTweaksType->SetLabel(wxEmptyString); + FFBTweaksName->SetLabel(wxEmptyString); + ffbTweaksTabDeviceName.clear(); + FFBTweaksId->SetLabel(wxEmptyString); + + FFBTweaksInvert->SetValue(false); + + refresh_gui(); +} + diff --git a/config/gimx-config.h b/config/gimx-config.h index f34874d9..3339c2e5 100644 --- a/config/gimx-config.h +++ b/config/gimx-config.h @@ -110,6 +110,8 @@ class configFrame: public wxFrame void OnJoystickCorrectionsRemoveClick(wxCommandEvent& event); void OnJoystickCorrectionsModifyClick(wxCommandEvent& event); void OnJoystickCorrectionsAutoDetectClick(wxCommandEvent& event); + void OnButtonForceFeedbackAutoDetect(wxCommandEvent& event); + void OnButtonFFBTweaksDelete(wxCommandEvent& event); //*) void DeleteSelectedRows(wxGrid* grid); void DeleteLinkedRows(wxGrid* grid, int row); @@ -203,6 +205,16 @@ class configFrame: public wxFrame static const long ID_BUTTON17; static const long ID_BUTTON18; static const long ID_PANEL7; + static const long ID_STATICTEXT13; + static const long ID_STATICTEXT20; + static const long ID_STATICTEXT25; + static const long ID_STATICTEXT49; + static const long ID_BUTTON20; + static const long ID_STATICLINE13; + static const long ID_CHECKBOX2; + static const long ID_STATICLINE15; + static const long ID_BUTTON24; + static const long ID_PANEL8; static const long ID_NOTEBOOK2; static const long ID_PANEL1; static const long ID_STATICTEXT38; @@ -315,15 +327,16 @@ class configFrame: public wxFrame wxStaticText* StaticText13; wxMenuItem* MenuConfiguration7; wxChoice* ButtonTabEventType; + wxStaticText* JoystickCorrectionsName; wxNotebook* Notebook2; wxSpinCtrl* IntensitySteps; wxPanel* PanelIntensity; wxMenuItem* MenuItemCopyProfile; + wxStaticText* FFBTweaksName; wxStaticText* IntensityDeviceId; wxStatusBar* StatusBar1; wxStaticText* AxisTabDeviceId; wxGrid* GridPanelButton; - wxStaticText* JoystickCorrectionsType; wxMenuItem* MenuItemG27Ps3; wxStaticLine* StaticLine2; wxTextCtrl* MouseOptionsFilter; @@ -344,31 +357,31 @@ class configFrame: public wxFrame wxStaticLine* StaticLine8; wxMenuItem* MenuItemReplaceMouse; wxChoice* IntensityDirection; + wxTextCtrl* JoystickCorrectionsLowValue; wxStaticText* AxisTabDeviceType; wxStaticLine* StaticLine5; wxCheckBox* CheckBoxSwitchBack; wxStaticLine* StaticLine10; wxMenuItem* MenuItemG29Ps4; - wxMenuItem* MenuItemDfPs2; - wxMenuItem* MenuItemDfpPs2; - wxMenuItem* MenuItemGtfPs2; wxStaticText* IntensityButtonId; + wxStaticLine* StaticLine13; wxGrid* GridMouseOption; wxTextCtrl* MouseOptionsBuffer; wxStaticText* StaticText3; - wxTextCtrl* JoystickCorrectionsHighValue; wxButton* ButtonTabAutoDetect; wxTextCtrl* AxisTabDeadZone; wxPanel* PanelOverall; - wxStaticText* StaticText2; wxMenuItem* MenuConfiguration4; + wxStaticText* StaticText2; wxStaticText* StaticTextThresholdPanelButton; wxButton* IntensityAdd; wxButton* ButtonAutoDetect; + wxStaticText* JoystickCorrectionsType; wxMenuItem* MenuItemNew; wxStaticText* ButtonTabDeviceId; wxStaticText* ProfileTriggerDeviceName; wxMenuItem* MenuConfiguration6; + wxButton* ButtonFFBTweaksDelete; wxChoice* IntensityAxis; wxMenuItem* MenuItem360; wxButton* JoystickCorrectionAdd; @@ -376,23 +389,25 @@ class configFrame: public wxFrame wxStaticText* IntensityDeviceType; wxButton* ButtonTabModify; wxButton* AxisTabAutoDetect; + wxCheckBox* FFBTweaksInvert; wxButton* IntensityModify; wxChoice* AxisTabEventType; wxStaticText* AxisTabDeviceName; wxStaticText* ProfileTriggerButtonId; wxMenuItem* MenuController6; wxStaticText* ProfileTriggerDeviceType; + wxStaticText* JoystickCorrectionsAxis; wxMenu* MenuConfiguration; wxStaticLine* StaticLine7; wxGrid* GridJoystickCorrections; wxMenu* MenuEdit; + wxStaticText* FFBTweaksType; wxPanel* PanelJoystickCorrections; wxGrid* GridPanelAxis; wxMenu* MenuFile; wxPanel* PanelAxis; wxStaticText* IntensityDeviceName; wxStaticText* MouseOptionsName; - wxTextCtrl* JoystickCorrectionsLowValue; wxMenuItem* MenuItemDS4; wxButton* IntensityAutoDetect; wxPanel* PanelButton; @@ -409,19 +424,23 @@ class configFrame: public wxFrame wxStaticText* AxisTabEventId; wxMenuItem* MenuController3; wxStaticText* StaticTextEmptyPanelAxis; + wxStaticText* FFBTweaksAxis; wxStaticText* StaticTextDZPanelAxis; wxMenuItem* MenuItem2; wxChoice* ButtonTabButtonId; wxStaticText* StaticText8; wxMenuItem* MenuItemXOne; wxSpinCtrl* IntensityDeadZone; + wxMenuItem* MenuItemDfpPs2; wxStaticLine* StaticLine4; wxMenuItem* MenuUpdate; - wxStaticText* JoystickCorrectionsId; + wxTextCtrl* JoystickCorrectionsHighCoef; wxMenuItem* MenuConfiguration8; wxMenuItem* MenuConfiguration2; + wxPanel* PanelForceFeedback; wxButton* MouseOptionsRemove; wxGrid* GridIntensity; + wxStaticText* JoystickCorrectionsId; wxMenuItem* MenuItemReplaceMouseDPI; wxStaticLine* StaticLine6; wxPanel* PanelMouseOptions; @@ -432,6 +451,7 @@ class configFrame: public wxFrame wxButton* ButtonTabRemove; wxMenuItem* MenuItemXbox; wxMenuItem* MenuItemDS2; + wxButton* ForceFeedbackAutoDetect; wxButton* ButtonTabAdd; wxStaticText* StaticTextButtonPanelButton; wxStaticLine* StaticLine3; @@ -444,8 +464,10 @@ class configFrame: public wxFrame wxMenuItem* MenuItemSave; wxMenuItem* MenuItemReplaceKeyboard; wxMenuItem* MenuItemSetMouseDPI; + wxMenuItem* MenuItemDfPs2; wxButton* MouseOptionsModify; wxStaticText* MouseOptionsButton; + wxTextCtrl* JoystickCorrectionsHighValue; wxMenuItem* MenuItemDS3; wxTextCtrl* ButtonTabThreshold; wxStaticText* StaticText7; @@ -454,29 +476,29 @@ class configFrame: public wxFrame wxStaticText* StaticText11; wxStaticText* StaticTextShapePanelAxis; wxStaticText* MouseOptionsId; + wxMenuItem* MenuItemGtfPs2; wxStaticText* StaticText12; - wxButton* JoystickCorrectionsAutoDetect; wxStaticText* ButtonTabDeviceName; + wxStaticText* FFBTweaksId; wxStaticLine* StaticLine9; wxMenuItem* MenuItemWindowEvents; - wxStaticText* JoystickCorrectionsAxis; wxMenuItem* MenuItemMultipleMiceAndKeyboards; wxMenuItem* MenuController2; wxButton* JoystickCorrectionModify; wxStaticText* StaticTextAccelPanelAxis; wxStaticLine* StaticLine14; + wxTextCtrl* JoystickCorrectionsLowCoef; wxStaticText* StaticTextDelayPanelOverall; wxNotebook* Notebook1; + wxStaticLine* StaticLine15; wxStaticText* StaticTextSensPanelAxis; wxMenuItem* MenuController7; - wxTextCtrl* JoystickCorrectionsHighCoef; wxButton* ButtonDelete; wxButton* MouseOptionsAdd; wxMenuItem* MenuItemPasteController; wxStaticText* ProfileTriggerDeviceId; - wxTextCtrl* JoystickCorrectionsLowCoef; + wxButton* JoystickCorrectionsAutoDetect; wxMenu* MenuAdvanced; - wxStaticText* JoystickCorrectionsName; wxComboBox* ButtonTabLabel; wxMenuItem* MenuController1; wxMenuItem* MenuItemCopyController; @@ -506,6 +528,7 @@ class configFrame: public wxFrame string mouseTabDeviceName; string intensityTabDeviceName; string joystickTabDeviceName; + string ffbTweaksTabDeviceName; DECLARE_EVENT_TABLE() }; diff --git a/config/wxsmith/configframe.wxs b/config/wxsmith/configframe.wxs index 3e7a1118..640055c3 100644 --- a/config/wxsmith/configframe.wxs +++ b/config/wxsmith/configframe.wxs @@ -974,6 +974,128 @@ + + + + 1 + 2 + + + 6 + 1 + + + + + + 4 + 1 + + + + 1 + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + + 1 + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + -1,50 + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + 1 + 1 + + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + -1,50 + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + + + + + wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL 5 diff --git a/core/adapter.c b/core/adapter.c index 970920ed..cbd8874e 100644 --- a/core/adapter.c +++ b/core/adapter.c @@ -499,44 +499,16 @@ static int adapter_hid_close_cb(int adapter) } #ifndef WIN32 -void adapter_set_hid(int adapter, int hid) +static int start_hid(int adapter) { - if(adapter < 0 || adapter >= MAX_CONTROLLERS) + if (adapters[adapter].haptic.hid.id >= 0) { - fprintf(stderr, "%s: invalid controller\n", __func__); - return; + ginput_joystick_set_hid_callbacks(adapters[adapter].haptic.hid.id, adapter, adapter_hid_write_cb, adapter_hid_close_cb); } - if(adapters[adapter].haptic.hid.id < 0) - { - const s_hid_info * info = ghid_get_hid_info(hid); - if (info != NULL && ff_lg_is_logitech_wheel(info->vendor_id, info->product_id)) - { - adapters[adapter].haptic.hid.id = hid; - adapters[adapter].haptic.usb_ids.vendor = info->vendor_id; - adapters[adapter].haptic.usb_ids.product = info->product_id; - ginput_joystick_set_hid_callbacks(hid, adapter, adapter_hid_write_cb, adapter_hid_close_cb); - ncprintf("FFB device: VID=0x%04x, PID=0x%04x.\n", info->vendor_id, info->product_id); - } - } + return 0; } #else -void adapter_set_usb_ids(int adapter, int joystick_id, unsigned short vendor, unsigned short product) -{ - if(adapter < 0 || adapter >= MAX_CONTROLLERS) - { - fprintf(stderr, "%s: invalid adapter\n", __func__); - return; - } - - if(adapters[adapter].haptic.joystick == joystick_id) - { - adapters[adapter].haptic.usb_ids.vendor = vendor; - adapters[adapter].haptic.usb_ids.product = product; - ncprintf("FFB device: VID=0x%04x, PID=0x%04x.\n", vendor, product); - } -} - static int start_hid(int adapter) { adapters[adapter].haptic.hid.id = ghid_open_ids(adapters[adapter].haptic.usb_ids.vendor, adapters[adapter].haptic.usb_ids.product); @@ -990,21 +962,32 @@ int adapter_start() if (ff_lg_is_logitech_wheel(vid, pid)) { // emulated controller is a Logitech wheel with FFB support - if (ff_lg_is_logitech_wheel(adapter->haptic.usb_ids.vendor, adapter[i].haptic.usb_ids.product)) + if (gimx_params.ff_conv == 0 && ff_lg_is_logitech_wheel(adapter->haptic.usb_ids.vendor, adapter[i].haptic.usb_ids.product)) { - // default joystick is a Logitech wheel with FFB support + // default joystick is a Logitech wheel with FFB support and ff_conv is not forced + + adapter->haptic.ff_lg = 1; + + ncprintf("FFB device: %s %d (direct translation).\n", ginput_joystick_name(adapter->haptic.joystick), ginput_joystick_virtual_id(adapter->haptic.joystick)); + ff_lg_init(i, pid, adapter->haptic.usb_ids.product); -#ifdef WIN32 + start_hid(i); -#endif + // wheel range may have to be changed adapter_send_next_hid_report(i); } else if (adapter->haptic.has_ffb) { + adapter->haptic.ff_conv = 1; + + ncprintf("FFB device: %s %d (OS translation).\n", ginput_joystick_name(adapter->haptic.joystick), ginput_joystick_virtual_id(adapter->haptic.joystick)); + // default joystick is a non-Logitech wheel with FFB support ff_conv_init(i, pid); } + + adapter_set_ffb_tweaks(i); } if(adapter_send_short_command(i, BYTE_START) < 0) { @@ -1305,10 +1288,57 @@ int adapter_is_usb_auth_required(int adapter) } -void adapter_set_haptic_joystick(int adapter, int joystick) +void adapter_set_haptic(s_config_entry * entry, int force) { - if (adapters[adapter].haptic.joystick < 0) + int adapter = entry->controller_id; + + if(adapter < 0 || adapter >= MAX_CONTROLLERS) + { + fprintf(stderr, "%s: invalid adapter\n", __func__); + return; + } + + if (force == 1) + { + memset(&adapters[adapter].haptic, 0x00, sizeof(adapters[adapter].haptic)); + adapters[adapter].haptic.joystick = -1; + adapters[adapter].haptic.hid.id = -1; // no need to close, it is owned by ginput + } + + if (adapters[adapter].haptic.joystick >= 0) + { + return; + } + + adapters[adapter].haptic.joystick = entry->device.id; + +#ifndef WIN32 + int hid = entry->device.hid; + if(hid >= 0) + { + const s_hid_info * info = ghid_get_hid_info(hid); + if (info != NULL && ff_lg_is_logitech_wheel(info->vendor_id, info->product_id)) + { + adapters[adapter].haptic.hid.id = hid; + adapters[adapter].haptic.usb_ids.vendor = info->vendor_id; + adapters[adapter].haptic.usb_ids.product = info->product_id; + } + } +#else + if(entry->device.usb_ids.vendor && entry->device.usb_ids.product) + { + adapters[adapter].haptic.usb_ids.vendor = entry->device.usb_ids.vendor; + adapters[adapter].haptic.usb_ids.product = entry->device.usb_ids.product; + } +#endif +} + +void adapter_set_ffb_tweaks(int adapter) +{ + const s_ffb_tweaks * tweaks = cfg_get_ffb_tweaks(adapter); + + if (adapters[adapter].haptic.ff_conv) { - adapters[adapter].haptic.joystick = joystick; + ff_conv_set_tweaks(adapter, tweaks->invert); } } diff --git a/core/args.c b/core/args.c index c99e7c4c..68978a1e 100644 --- a/core/args.c +++ b/core/args.c @@ -83,6 +83,7 @@ static void usage() printf(" --log filename: write messages into a log file instead of the standard output.\n"); printf(" filename: The name of the log file, in the ~/.gimx/log directory (make sure this folder exists).\n"); printf(" --skip_leds: Filter out set led commands from FFB command stream (performance tweak for G27/G29 wheels on small targets).\n"); + printf(" --ff_conv: Force OS translation for FFB commands on Windows.\n"); } /* @@ -145,6 +146,7 @@ int args_read(int argc, char *argv[], s_gimx_params* params) {"debug.ff_conv", no_argument, ¶ms->debug.ff_conv, 1}, {"debug.adapter", no_argument, ¶ms->debug.adapter, 1}, {"skip_leds", no_argument, ¶ms->skip_leds, 1}, + {"ff_conv", no_argument, ¶ms->ff_conv, 1}, /* These options don't set a flag. We distinguish them by their indices. */ {"bdaddr", required_argument, 0, 'b'}, {"config", required_argument, 0, 'c'}, @@ -417,6 +419,8 @@ int args_read(int argc, char *argv[], s_gimx_params* params) printf(_("btstack flag is set\n")); if(params->skip_leds) printf(_("skip_leds flag is set\n")); + if(params->ff_conv) + printf(_("ff_conv flag is set\n")); if(!input) { diff --git a/core/config.c b/core/config.c index 7d9764cc..4440b871 100644 --- a/core/config.c +++ b/core/config.c @@ -81,6 +81,33 @@ static s_mouse_control mouse_control[MAX_DEVICES] = {}; static s_mapper_table joystick_buttons[MAX_DEVICES][MAX_CONTROLLERS][MAX_CONFIGURATIONS]; static s_mapper_table joystick_axes[MAX_DEVICES][MAX_CONTROLLERS][MAX_CONFIGURATIONS]; +/* + * FFB tweaks, for each controller and each profile. + */ +static s_ffb_tweaks ffb_tweaks[MAX_CONTROLLERS][MAX_CONFIGURATIONS]; + +void cfg_set_ffb_tweaks(const s_config_entry * entry) +{ + ffb_tweaks[entry->controller_id][entry->config_id].invert = entry->params.ffb_tweaks.invert; +} + +const s_ffb_tweaks * cfg_get_ffb_tweaks(int controller) +{ + return ffb_tweaks[controller] + cfg_controllers[controller].current->index; +} + +void cfg_init_ffb_tweaks() +{ + unsigned int i, j; + for (i = 0; i < MAX_CONTROLLERS; ++i) + { + for (j = 0; j < MAX_CONFIGURATIONS; ++j) + { + ffb_tweaks[i][j].invert = 0; + } + } +} + static struct { unsigned int nb; @@ -835,6 +862,8 @@ void cfg_config_activation() { update_stick(i, j); } + + adapter_set_ffb_tweaks(i); } cfg_controllers[i].next = NULL; @@ -1512,3 +1541,4 @@ void cfg_read_calibration() current_mouse = 0; } } + diff --git a/core/config_reader.c b/core/config_reader.c index 85a0dd35..c922b24b 100644 --- a/core/config_reader.c +++ b/core/config_reader.c @@ -32,6 +32,9 @@ static s_config_entry entry; static void reset_entry() { memset(&entry.device, 0x00, sizeof(entry.device)); +#ifndef WIN32 + entry.device.hid = -1; +#endif memset(&entry.event, 0x00, sizeof(entry.event)); memset(&entry.params, 0x00, sizeof(entry.params)); } @@ -459,18 +462,7 @@ static int ProcessEventElement(xmlNode * a_node, unsigned char mapper) && entry.params.mapper.axis_props.axis == rel_axis_0 && entry.params.mapper.axis_props.props == AXIS_PROP_CENTERED) { - adapter_set_haptic_joystick(entry.controller_id, entry.device.id); -#ifndef WIN32 - if(entry.device.hid >= 0) - { - adapter_set_hid(entry.controller_id, entry.device.hid); - } -#else - if(entry.device.usb_ids.vendor && entry.device.usb_ids.product) - { - adapter_set_usb_ids(entry.controller_id, entry.device.id, entry.device.usb_ids.vendor, entry.device.usb_ids.product); - } -#endif + adapter_set_haptic(&entry, 0); } break; default: @@ -1047,6 +1039,74 @@ static int ProcessJoystickCorrectionsListElement(xmlNode * a_node) return ret; } +static int ProcessInversionElement(xmlNode * a_node) +{ + char * val = (char*)xmlGetProp(a_node, (xmlChar*) X_ATTR_ENABLE); + + if(val == NULL) + { + printf("missing %s attribute\n", X_ATTR_ENABLE); + return -1; + } + if (strcmp(val, X_ATTR_VALUE_YES) == 0) + { + entry.params.ffb_tweaks.invert = 1; + } + + return 0; +} + +static int ProcessForceFeedbackElement(xmlNode * a_node) +{ + int ret = 0; + int has_device = 0, has_inversion = 0; + + reset_entry(); + + xmlNode* cur_node = NULL; + for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) + { + if (cur_node->type == XML_ELEMENT_NODE) + { + if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_DEVICE)) + { + has_device = 1; + ret = ProcessDeviceElement(cur_node); + } + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_INVERSION)) + { + has_inversion = 1; + ret = ProcessInversionElement(cur_node); + } + else + { + printf("unexpected element: %s\n", cur_node->name); + } + } + } + + if (has_device == 0) + { + printf("missing device element\n"); + ret = -1; + } + + if (has_inversion == 0) + { + printf("missing inversion element\n"); + ret = -1; + } + + if(ret != -1) + { + cfg_set_ffb_tweaks(&entry); + // force FFB selection for 1st profile only + adapter_set_haptic(&entry, entry.config_id == 0 ? 1 : 0); + } + + return ret; +} + static int ProcessConfigurationElement(xmlNode * a_node) { int ret = 0; @@ -1074,115 +1134,34 @@ static int ProcessConfigurationElement(xmlNode * a_node) if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_TRIGGER)) { ret = ProcessTriggerElement(cur_node); - break; } - else - { - printf("bad element name: %s", cur_node->name); - ret = -1; - } - } - } - - if (!cur_node) - { - printf("missing trigger element"); - ret = -1; - } - - for (cur_node = cur_node->next; cur_node; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_MOUSE_OPTIONS_LIST)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_MOUSE_OPTIONS_LIST)) { ret = ProcessMouseOptionsListElement(cur_node); - break; } - else - { - cur_node = cur_node->prev; - break; - } - } - } - - for (cur_node = cur_node->next; cur_node && ret != -1; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_INTENSITY_LIST)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_INTENSITY_LIST)) { ret = ProcessIntensityListElement(cur_node); - break; - } - else - { - cur_node = cur_node->prev; - break; } - } - } - - for (cur_node = cur_node->next; cur_node && ret != -1; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_BUTTON_MAP)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_BUTTON_MAP)) { ret = ProcessButtonMapElement(cur_node); - break; - } - else - { - printf("bad element name: %s", cur_node->name); - ret = -1; } - } - } - - if (!cur_node) - { - printf("missing button_map element"); - ret = -1; - } - - for (cur_node = cur_node->next; cur_node && ret != -1; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_AXIS_MAP)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_AXIS_MAP)) { ret = ProcessAxisMapElement(cur_node); - break; } - else + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_JOYSTICK_CORRECTIONS_LIST)) { - printf("bad element name: %s", cur_node->name); - ret = -1; + ret = ProcessJoystickCorrectionsListElement(cur_node); } - } - } - - if (!cur_node) - { - printf("missing axis_map element"); - ret = -1; - } - - for (cur_node = cur_node->next; cur_node && ret != -1; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_JOYSTICK_CORRECTIONS_LIST)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_FORCE_FEEDBACK)) { - ret = ProcessJoystickCorrectionsListElement(cur_node); - break; + ret = ProcessForceFeedbackElement(cur_node); } else { - printf("bad element name: %s", cur_node->name); - ret = -1; + printf("unexpected element: %s\n", cur_node->name); } } } diff --git a/core/gimx.c b/core/gimx.c index 7c2f322b..a3d96d25 100755 --- a/core/gimx.c +++ b/core/gimx.c @@ -304,6 +304,7 @@ int main(int argc, char *argv[]) cal_init(); cfg_intensity_init(); + cfg_init_ffb_tweaks(); if(read_config_file(gimx_params.config_file) < 0) { @@ -318,6 +319,7 @@ int main(int argc, char *argv[]) cal_init(); cfg_intensity_init(); + cfg_init_ffb_tweaks(); read_config_file(gimx_params.config_file); } diff --git a/core/haptic/ff_conv.c b/core/haptic/ff_conv.c index 151b4e7e..5760dc5c 100644 --- a/core/haptic/ff_conv.c +++ b/core/haptic/ff_conv.c @@ -16,8 +16,8 @@ static inline short u8_to_s16(unsigned char c) { int value = (c + CHAR_MIN) * SHRT_MAX / CHAR_MAX; - return CLAMP(SHRT_MIN, value, SHRT_MAX); -} + return CLAMP(-SHRT_MAX, value, SHRT_MAX); + } static inline unsigned short u8_to_u16(unsigned char c) { return c * USHRT_MAX / UCHAR_MAX; @@ -40,6 +40,7 @@ static struct s_cmd fifo[FIFO_SIZE]; unsigned short range; GE_Event last_event; + int invert; } ff_lg_device[MAX_CONTROLLERS] = {}; static inline int check_device(int device, const char * file, unsigned int line, const char * func) { @@ -71,6 +72,8 @@ int ff_conv_init(int device, unsigned short pid) { ff_lg_device[device].pid = pid; + ff_lg_device[device].invert = 0; + unsigned short range = ff_lg_get_wheel_range(pid); if (range != 0) { ncprintf("adjust your wheel range to %u degrees\n", range); @@ -94,7 +97,7 @@ static void dump_event(const GE_Event * event) { fflush(stdout); break; case GE_JOYDAMPERFORCE: - dprintf("< DAMPER, saturation: %u %u, coefficient: %u %u\n", + dprintf("< DAMPER, saturation: %u %u, coefficient: %d %d\n", event->jcondition.saturation.left, event->jcondition.saturation.right, event->jcondition.coefficient.left, event->jcondition.coefficient.right); fflush(stdout); @@ -106,6 +109,11 @@ static void dump_event(const GE_Event * event) { } } +#define SWAP(TYPE, V1, V2) \ + TYPE tmp = V1; \ + V1 = V2; \ + V2 = tmp; + static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { int ret = 0; @@ -119,6 +127,9 @@ static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { event->type = GE_JOYCONSTANTFORCE; if (ff_lg_device[device].slots[index].active) { event->jconstant.level = u8_to_s16(FF_LG_CONSTANT_LEVEL(force, index)); + if (ff_lg_device[device].invert) { + event->jconstant.level = -event->jconstant.level; + } } ret = 1; break; @@ -127,20 +138,30 @@ static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { if (ff_lg_device[device].slots[index].active) { static int warned = 0; if (index == 0) { - if (warned == 0 && FF_LG_VARIABLE_T1(force) && FF_LG_VARIABLE_S1(force)) { - gprintf("warning: variable force cannot be converted to constant force (l1=%hu, t1=%hu, s1=%hu, d1=%hu\n", - FF_LG_VARIABLE_L1(force), FF_LG_VARIABLE_T1(force), FF_LG_VARIABLE_S1(force), FF_LG_VARIABLE_D1(force)); - warned = 1; + if (FF_LG_VARIABLE_T1(force) && FF_LG_VARIABLE_S1(force)) { + if (warned == 0) { + gprintf("warning: variable force cannot be converted to constant force (l1=%hu, t1=%hu, s1=%hu, d1=%hu\n", + FF_LG_VARIABLE_L1(force), FF_LG_VARIABLE_T1(force), FF_LG_VARIABLE_S1(force), FF_LG_VARIABLE_D1(force)); + warned = 1; + } } else { event->jconstant.level = u8_to_s16(FF_LG_VARIABLE_L1(force)); + if (ff_lg_device[device].invert) { + event->jconstant.level = -event->jconstant.level; + } } } else if (index == 2) { - if (warned == 0 && FF_LG_VARIABLE_T2(force) && FF_LG_VARIABLE_S2(force)) { - gprintf("warning: variable force cannot be converted to constant force (l2=%hu, t2=%hu, s2=%hu, d2=%hu\n", - FF_LG_VARIABLE_L2(force), FF_LG_VARIABLE_T2(force), FF_LG_VARIABLE_S2(force), FF_LG_VARIABLE_D2(force)); - warned = 1; + if (FF_LG_VARIABLE_T2(force) && FF_LG_VARIABLE_S2(force)) { + if (warned == 0) { + gprintf("warning: variable force cannot be converted to constant force (l2=%hu, t2=%hu, s2=%hu, d2=%hu\n", + FF_LG_VARIABLE_L2(force), FF_LG_VARIABLE_T2(force), FF_LG_VARIABLE_S2(force), FF_LG_VARIABLE_D2(force)); + warned = 1; + } } else { event->jconstant.level = u8_to_s16(FF_LG_VARIABLE_L2(force)); + if (ff_lg_device[device].invert) { + event->jconstant.level = -event->jconstant.level; + } } } } @@ -157,6 +178,10 @@ static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { ff_lg_get_condition_coef(ff_lg_device[device].pid, 0, FF_LG_SPRING_K2(force), FF_LG_SPRING_S2(force)); event->jcondition.center = u8_to_s16((FF_LG_SPRING_D1(force) + FF_LG_SPRING_D2(force)) / 2); event->jcondition.deadband = u8_to_u16(FF_LG_SPRING_D2(force) - FF_LG_SPRING_D1(force)); + if (ff_lg_device[device].invert) { + SWAP(int16_t, event->jcondition.coefficient.left, event->jcondition.coefficient.right) + event->jcondition.center = -event->jcondition.center; + } } ret = 1; break; @@ -167,6 +192,9 @@ static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { ff_lg_get_condition_coef(ff_lg_device[device].pid, 0, FF_LG_DAMPER_K1(force), FF_LG_DAMPER_S1(force)); event->jcondition.coefficient.right = ff_lg_get_condition_coef(ff_lg_device[device].pid, 0, FF_LG_DAMPER_K2(force), FF_LG_DAMPER_S2(force)); + if (ff_lg_device[device].invert) { + SWAP(int16_t, event->jcondition.coefficient.left, event->jcondition.coefficient.right) + } } ret = 1; break; @@ -183,6 +211,10 @@ static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { uint16_t d1 = ff_lg_get_spring_deadband(ff_lg_device[device].pid, FF_LG_HIGHRES_SPRING_D1(force), FF_LG_HIGHRES_SPRING_D1L(force)); event->jcondition.center = u16_to_s16((d1 + d2) / 2); event->jcondition.deadband = d2 - d1; + if (ff_lg_device[device].invert) { + SWAP(int16_t, event->jcondition.coefficient.left, event->jcondition.coefficient.right) + event->jcondition.center = -event->jcondition.center; + } } ret = 1; break; @@ -197,6 +229,9 @@ static int ff_conv_lg_force(int device, unsigned int index, GE_Event * event) { ff_lg_get_condition_coef(ff_lg_device[device].pid, 1, FF_LG_HIGHRES_DAMPER_K2(force), FF_LG_HIGHRES_DAMPER_S2(force)); event->jcondition.center = 0; event->jcondition.deadband = 0; + if (ff_lg_device[device].invert) { + SWAP(int16_t, event->jcondition.coefficient.left, event->jcondition.coefficient.right) + } } ret = 1; break; @@ -287,7 +322,7 @@ void ff_conv_process_report(int device, const unsigned char data[FF_LG_OUTPUT_RE break; } } else { - unsigned short range = 0; + unsigned short range = 0; switch(data[1]) { case FF_LG_EXT_CMD_WHEEL_RANGE_200_DEGREES: range = 200; @@ -355,3 +390,14 @@ void ff_conv_ack(int device) { s_cmd cmd = fifo_peek(ff_lg_device[device].fifo); fifo_remove(ff_lg_device[device].fifo, cmd); } + +int ff_conv_set_tweaks(int device, int invert) { + + CHECK_DEVICE(device, -1) + + dprintf("FFB invert: %s\n", invert ? "yes" : "no"); + + ff_lg_device[device].invert = invert; + + return 0; +} diff --git a/core/include/adapter.h b/core/include/adapter.h index 7a5b1a43..d46056dc 100644 --- a/core/include/adapter.h +++ b/core/include/adapter.h @@ -65,6 +65,8 @@ typedef struct int write_pending; int read_pending; } hid; + int ff_lg; + int ff_conv; } haptic; unsigned char forward_out_reports; unsigned char process_ffb; @@ -84,9 +86,6 @@ int adapter_get_controller(e_device_type device_type, int device_id); #ifndef WIN32 int adapter_hid_poll(); -void adapter_set_hid(int adapter, int hid); -#else -void adapter_set_usb_ids(int adapter, int joystick_id, unsigned short vendor, unsigned short product); #endif void adapter_set_axis(unsigned char adapter, int axis, int value); @@ -96,6 +95,7 @@ int adapter_forward_interrupt_in(int adapter, unsigned char* data, unsigned char int adapter_is_usb_auth_required(int adapter); -void adapter_set_haptic_joystick(int adapter, int joystick); +void adapter_set_haptic(s_config_entry * entry, int force); +void adapter_set_ffb_tweaks(int adapter); #endif /* ADAPTER_H_ */ diff --git a/core/include/config.h b/core/include/config.h index a059f357..0c3beb50 100644 --- a/core/include/config.h +++ b/core/include/config.h @@ -135,6 +135,10 @@ typedef struct } trigger; s_mouse_options mouse_options; s_js_corr joystick_correction; + struct + { + int invert; + } ffb_tweaks; } params; }s_config_entry; @@ -170,6 +174,10 @@ typedef struct double dead_zone; }s_intensity; +typedef struct { + int invert; +} s_ffb_tweaks; + void cfg_trigger_init(); void cfg_trigger_lookup(GE_Event*); void cfg_config_activation(); @@ -190,5 +198,8 @@ s_mapper_table* cfg_get_mouse_axes(int, int, int); void cfg_clean(); void cfg_read_calibration(); void cfg_add_js_corr(uint8_t device, s_js_corr * corr); +void cfg_set_ffb_tweaks(const s_config_entry * entry); +const s_ffb_tweaks * cfg_get_ffb_tweaks(int controller); +void cfg_init_ffb_tweaks(); #endif /* CONFIG_H_ */ diff --git a/core/include/gimx.h b/core/include/gimx.h index bba6015a..9e1804b7 100644 --- a/core/include/gimx.h +++ b/core/include/gimx.h @@ -48,6 +48,7 @@ typedef struct char * logfilename; FILE * logfile; int skip_leds; + int ff_conv; } s_gimx_params; extern s_gimx_params gimx_params; diff --git a/core/include/haptic/ff_conv.h b/core/include/haptic/ff_conv.h index 7de16c05..6abf5bbd 100644 --- a/core/include/haptic/ff_conv.h +++ b/core/include/haptic/ff_conv.h @@ -12,5 +12,6 @@ int ff_conv_init(int device, unsigned short pid); void ff_conv_process_report(int device, const unsigned char data[FF_LG_OUTPUT_REPORT_SIZE]); int ff_conv_get_event(int device, GE_Event * event); void ff_conv_ack(int device); +int ff_conv_set_tweaks(int device, int invert); #endif /* FF_CONV_H_ */ diff --git a/core/include/haptic/ff_lg.h b/core/include/haptic/ff_lg.h index 3358ff68..34545d69 100644 --- a/core/include/haptic/ff_lg.h +++ b/core/include/haptic/ff_lg.h @@ -129,7 +129,7 @@ #define FF_LG_HIGHRES_DAMPER_S1(FORCE) ((FORCE->parameters)[1] & 0x01) // low side inversion (1 = inverted) #define FF_LG_HIGHRES_DAMPER_K2(FORCE) ((FORCE->parameters)[2] & 0x0f) // high (right or pull) side damper constant selector #define FF_LG_HIGHRES_DAMPER_S2(FORCE) ((FORCE->parameters)[3] & 0x01) // high side inversion (1 = inverted) -#define FF_LG_HIGHRES_DAMPER_CLIP(FORCE) (FORCE->parameters)[4] // clip level (maximum force), on either side (since Driving Force Pro) +#define FF_LG_HIGHRES_DAMPER_CLIP(FORCE) (FORCE->parameters)[4] // clip level (maximum force), on either side (only for Driving Force Pro) typedef struct PACKED { unsigned char type; diff --git a/core/include/xml_defs.h b/core/include/xml_defs.h index 08e12e3a..81d45ef2 100644 --- a/core/include/xml_defs.h +++ b/core/include/xml_defs.h @@ -65,4 +65,8 @@ #define X_ATTR_HIGH_VALUE "high_value" #define X_ATTR_HIGH_COEF "high_coef" +#define X_NODE_FORCE_FEEDBACK "force_feedback" +#define X_NODE_INVERSION "inversion" +#define X_ATTR_ENABLE "enable" + #endif /* XML_DEFS_H_ */ diff --git a/core/test/haptic/Makefile b/core/test/haptic/Makefile index 1df595d9..590c32c1 100644 --- a/core/test/haptic/Makefile +++ b/core/test/haptic/Makefile @@ -1,8 +1,8 @@ -OUT = ff_conv_test -OBJS = ff_conv_test.o -BINS = ff_conv_test -CFLAGS = -I../../include -I../../../shared/gasync/include -Wall -Wextra -g -O0 +OUT = ff_conv_test ff_lg_test +OBJS = ../../haptic/ff_conv.o ../../haptic/ff_lg.o +BINS = ff_conv_test ff_lg_test +CFLAGS = -I../../include -I../../../shared/controller/include -I../../../shared/gasync/include -Wall -Wextra -g -O0 CXXFLAGS = -Wall -Wextra -g -O0 ifeq ($(OS),Windows_NT) @@ -16,4 +16,6 @@ all: $(BINS) clean: $(RM) $(BINS) *~ *.o -ff_conv_test: ../../haptic/ff_conv.o ../../haptic/ff_lg.o $(OBJS) +ff_conv_test: $(OBJS) + +ff_lg_test: $(OBJS) diff --git a/core/test/haptic/ff_conv_test.c b/core/test/haptic/ff_conv_test.c index a91ab0c7..431da0a9 100644 --- a/core/test/haptic/ff_conv_test.c +++ b/core/test/haptic/ff_conv_test.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2016 Mathieu Laurendeau + Copyright (c) 2017 Mathieu Laurendeau License: GPLv3 */ @@ -12,7 +12,7 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; -#define CONSTANT(NAME, PRODUCT_ID, LEVEL_IN, LEVEL_OUT) \ +#define CONSTANT(NAME, PRODUCT_ID, INVERT, C_GAIN, LEVEL_IN, LEVEL_OUT) \ { \ .name = NAME, \ .product = PRODUCT_ID, \ @@ -21,6 +21,8 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; .type = FF_LG_FTYPE_CONSTANT, \ .parameters = { LEVEL_IN }, \ }, \ + .invert = INVERT, \ + .gain = { C_GAIN, 0, 0 }, \ .nb_events = 1, \ .events = { \ { \ @@ -32,7 +34,7 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; } \ } -#define VARIABLE(NAME, PRODUCT_ID, LEVEL_IN, LEVEL_OUT) \ +#define VARIABLE(NAME, PRODUCT_ID, INVERT, C_GAIN, LEVEL_IN, LEVEL_OUT) \ { \ .name = NAME, \ .product = PRODUCT_ID, \ @@ -41,6 +43,8 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; .type = FF_LG_FTYPE_VARIABLE, \ .parameters = { LEVEL_IN }, \ }, \ + .invert = INVERT, \ + .gain = { C_GAIN, 0, 0 }, \ .nb_events = 1, \ .events = { \ { \ @@ -52,7 +56,7 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; } \ } -#define HR_SPRING(NAME, PRODUCT_ID, D1, D2, K2, K1, D2L, S2, D1L, S1, CLIP, SL, SR, CL, CR, C, D) \ +#define HR_SPRING(NAME, PRODUCT_ID, INVERT, S_GAIN, D1, D2, K2, K1, D2L, S2, D1L, S1, CLIP, SL, SR, CL, CR, C, D) \ { \ .name = NAME, \ .product = PRODUCT_ID, \ @@ -61,6 +65,8 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; .type = FF_LG_FTYPE_HIGH_RESOLUTION_SPRING, \ .parameters = { D1, D2, K2 | K1, D2L | S2 | D1L | S1, CLIP } \ }, \ + .invert = INVERT, \ + .gain = { 0, S_GAIN, 0 }, \ .nb_events = 1, \ .events = { \ { \ @@ -81,7 +87,7 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; } \ } -#define HR_DAMPER(NAME, PRODUCT_ID, K1, S1, K2, S2, CLIP, SL, SR, CL, CR) \ +#define HR_DAMPER(NAME, PRODUCT_ID, INVERT, D_GAIN, K1, S1, K2, S2, CLIP, SL, SR, CL, CR) \ { \ .name = NAME, \ .product = PRODUCT_ID, \ @@ -90,6 +96,8 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; .type = FF_LG_FTYPE_HIGH_RESOLUTION_DAMPER, \ .parameters = { K1, S1, K2, S2, CLIP } \ }, \ + .invert = INVERT, \ + .gain = { 0, 0, D_GAIN }, \ .nb_events = 1, \ .events = { \ { \ @@ -108,7 +116,7 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; } \ } -#define SPRING(NAME, PRODUCT_ID, D1, D2, K2, K1, S2, S1, CLIP, SL, SR, CL, CR, C, D) \ +#define SPRING(NAME, PRODUCT_ID, INVERT, S_GAIN, D1, D2, K2, K1, S2, S1, CLIP, SL, SR, CL, CR, C, D) \ { \ .name = NAME, \ .product = PRODUCT_ID, \ @@ -117,6 +125,8 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; .type = FF_LG_FTYPE_SPRING, \ .parameters = { D1, D2, K2 | K1, S2 | S1, CLIP } \ }, \ + .invert = INVERT, \ + .gain = { 0, S_GAIN, 0 }, \ .nb_events = 1, \ .events = { \ { \ @@ -137,7 +147,7 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; } \ } -#define DAMPER(NAME, PRODUCT_ID, K1, K2, S1, S2, CL, CR) \ +#define DAMPER_TEST(NAME, PRODUCT_ID, INVERT, D_GAIN, K1, K2, S1, S2, CL, CR) \ { \ .name = NAME, \ .product = PRODUCT_ID, \ @@ -146,6 +156,8 @@ s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1, .ff_conv = 1 } }; .type = FF_LG_FTYPE_DAMPER, \ .parameters = { K1, K2, S1, S2 } \ }, \ + .invert = INVERT, \ + .gain = { 0, 0, D_GAIN }, \ .nb_events = 1, \ .events = { \ { \ @@ -176,142 +188,270 @@ static const struct { s_ff_lg_force report; }; }; + int invert; + struct { + int constant; + int spring; + int damper; + } gain; unsigned int nb_events; GE_Event events[FF_LG_FSLOTS_NB]; } test_cases[] = { - CONSTANT("G29 constant force (left)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0xff, 32767), - CONSTANT("G29 constant force (right)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, -32768), - CONSTANT("G29 constant force (null)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 128, 0), + CONSTANT("G29 constant force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0xff, 32767), + CONSTANT("G29 constant force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0, -32767), + CONSTANT("G29 constant force (null, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 128, 0), - DAMPER("G29 damper force (left)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, +// CONSTANT("G29 constant force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, 0xff, 16383), +// CONSTANT("G29 constant force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, 0, -16383), +// CONSTANT("G29 constant force (null, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, 128, 0), + + CONSTANT("G29 constant force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0xff, -32767), + CONSTANT("G29 constant force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0, 32767), + CONSTANT("G29 constant force (null, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 128, 0), + + DAMPER_TEST("G29 damper force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0x07, 0x00, 0x00, 0x00, 32767, 2047), - DAMPER("G29 damper force (right)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + DAMPER_TEST("G29 damper force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0x00, 0x00, 0x07, 0x00, 2047, 32767), - DAMPER("G29 damper force (zeroes)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + DAMPER_TEST("G29 damper force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0x00, 0x00, 0x00, 0x00, + 2047, 2047), + DAMPER_TEST("G29 damper force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0x07, 0x01, 0x07, 0x01, + -32767, -32767), + +// DAMPER_TEST("G29 damper force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x07, 0x00, 0x00, 0x00, +// 16383, 1023), +// DAMPER_TEST("G29 damper force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x00, 0x00, 0x07, 0x00, +// 1023, 16383), +// DAMPER_TEST("G29 damper force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x00, 0x00, 0x00, 0x00, +// 1023, 1023), +// DAMPER_TEST("G29 damper force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x07, 0x01, 0x07, 0x01, +// -16383, -16383), + + DAMPER_TEST("G29 damper force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 0x07, 0x00, 0x00, 0x00, + 2047, 32767), + DAMPER_TEST("G29 damper force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 0x00, 0x00, 0x07, 0x00, + 32767, 2047), + DAMPER_TEST("G29 damper force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0x00, 0x00, 0x00, 0x00, 2047, 2047), - DAMPER("G29 damper force (ones)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + DAMPER_TEST("G29 damper force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0x07, 0x01, 0x07, 0x01, - -32768, -32768), + -32767, -32767), - DAMPER("FFGP damper force (c=5)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, + DAMPER_TEST("FFGP damper force (c=5)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, 0, 100, 0x05, 0x00, 0x05, 0x00, 24575, 24575), - DAMPER("FFGP damper force (c=6)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, + DAMPER_TEST("FFGP damper force (c=6)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, 0, 100, 0x06, 0x00, 0x06, 0x00, 16383, 16383), - DAMPER("DFP damper force (c=5)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, + DAMPER_TEST("DFP damper force (c=5)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, 0, 100, 0x05, 0x00, 0x05, 0x00, 16383, 16383), - DAMPER("DFP damper force (c=6)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, + DAMPER_TEST("DFP damper force (c=6)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, 0, 100, 0x06, 0x00, 0x06, 0x00, 24575, 24575), - SPRING("G29 spring force (left)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + SPRING("G29 spring force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 128, 128, 0x00, 0x07, 0x00, 0x00, 255, 65535, 65535, 32767, 2047, 0, 0), - SPRING("G29 spring force (right)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + SPRING("G29 spring force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 128, 128, 0x70, 0x00, 0x00, 0x00, 255, 65535, 65535, 2047, 32767, 0, 0), - SPRING("G29 spring force (zeroes)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + SPRING("G29 spring force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0, 0, 0x00, 0x00, 0x00, 0x00, 0, + 0, 0, 2047, 2047, -32767, 0), + SPRING("G29 spring force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0xff, 0xff, 0x70, 0x07, 0x10, 0x01, 0xff, + 65535, 65535, -32767, -32767, 32767, 0), + +// SPRING("G29 spring force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 128, 128, 0x00, 0x07, 0x00, 0x00, 255, +// 32767, 32767, 16383, 1023, 0, 0), +// SPRING("G29 spring force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 128, 128, 0x70, 0x00, 0x00, 0x00, 255, +// 32767, 32767, 1023, 16383, 0, 0), +// SPRING("G29 spring force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0, 0, 0x00, 0x00, 0x00, 0x00, 0, +// 0, 0, 1023, 1023, -32767, 0), +// SPRING("G29 spring force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0xff, 0xff, 0x70, 0x07, 0x10, 0x01, 0xff, +// 32767, 32767, -16383, -16383, 32767, 0), + + SPRING("G29 spring force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 128, 128, 0x00, 0x07, 0x00, 0x00, 255, + 65535, 65535, 2047, 32767, 0, 0), + SPRING("G29 spring force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 128, 128, 0x70, 0x00, 0x00, 0x00, 255, + 65535, 65535, 32767, 2047, 0, 0), + SPRING("G29 spring force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0, 0, 0x00, 0x00, 0x00, 0x00, 0, - 0, 0, 2047, 2047, -32768, 0), - SPRING("G29 spring force (ones)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + 0, 0, 2047, 2047, 32767, 0), + SPRING("G29 spring force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0xff, 0xff, 0x70, 0x07, 0x10, 0x01, 0xff, - 65535, 65535, -32768, -32768, 32767, 0), + 65535, 65535, -32767, -32767, -32767, 0), - SPRING("FFGP spring force (c=5)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, + SPRING("FFGP spring force (c=5)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, 0, 100, 128, 128, 0x50, 0x05, 0x00, 0x00, 0, 0, 0, 24575, 24575, 0, 0), - SPRING("FFGP spring force (c=6)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, + SPRING("FFGP spring force (c=6)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, 0, 100, 128, 128, 0x60, 0x06, 0x00, 0x00, 0, 0, 0, 16383, 16383, 0, 0), - SPRING("DFP spring force (c=5)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, + SPRING("DFP spring force (c=5)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, 0, 100, 128, 128, 0x50, 0x05, 0x00, 0x00, 0, 0, 0, 16383, 16383, 0, 0), - SPRING("DFP spring force (c=6)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, + SPRING("DFP spring force (c=6)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, 0, 100, 128, 128, 0x60, 0x06, 0x00, 0x00, 0, 0, 0, 24575, 24575, 0, 0), - VARIABLE("G29 variable force (left)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0xff, 32767), - VARIABLE("G29 variable force (right)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, -32768), - VARIABLE("G29 variable force (null)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 128, 0), + VARIABLE("G29 variable force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0xff, 32767), + VARIABLE("G29 variable force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0, -32767), + VARIABLE("G29 variable force (null, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 128, 0), + +// VARIABLE("G29 variable force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, 0xff, 16383), +// VARIABLE("G29 variable force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, 0, -16383), +// VARIABLE("G29 variable force (null, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, 128, 0), - HR_SPRING("G29 high resolution spring force (left)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + VARIABLE("G29 variable force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0xff, -32767), + VARIABLE("G29 variable force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0, 32767), + VARIABLE("G29 variable force (null, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 128, 0), + + HR_SPRING("G29 high resolution spring force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff, + 65535, 65535, 32767, 0, 0x3FF * USHRT_MAX / 0x7FF - SHRT_MAX, 0), + HR_SPRING("G29 high resolution spring force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff, + 65535, 65535, 0, 32767, 0x3FF * USHRT_MAX / 0x7FF - SHRT_MAX, 0), + HR_SPRING("G29 high resolution spring force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0, + 0, 0, 0, 0, -32767, 0), + HR_SPRING("G29 high resolution spring force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, + 0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff, + 65535, 65535, -32767, -32767, 32767, 0), + +// HR_SPRING("G29 high resolution spring force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff, +// 32767, 32767, 16383, 0, 0x3FF * USHRT_MAX / 0x7FF - SHRT_MAX, 0), +// HR_SPRING("G29 high resolution spring force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff, +// 32767, 32767, 0, 16383, 0x3FF * USHRT_MAX / 0x7FF - SHRT_MAX, 0), +// HR_SPRING("G29 high resolution spring force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0, +// 0, 0, 0, 0, -32767, 0), +// HR_SPRING("G29 high resolution spring force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff, +// 32767, 32767, -16383, -16383, 32767, 0), + + HR_SPRING("G29 high resolution spring force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff, - 65535, 65535, 32767, 0, 0x3FF * 65535 / 0x7FF - -32768, 0), - HR_SPRING("G29 high resolution spring force (right)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + 65535, 65535, 0, 32767, - (0x3FF * USHRT_MAX / 0x7FF - SHRT_MAX), 0), + HR_SPRING("G29 high resolution spring force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff, - 65535, 65535, 0, 32767, 0x3FF * 65535 / 0x7FF - -32768, 0), - HR_SPRING("G29 high resolution spring force (zeroes)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + 65535, 65535, 32767, 0, - (0x3FF * USHRT_MAX / 0x7FF - SHRT_MAX), 0), + HR_SPRING("G29 high resolution spring force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0, - 0, 0, 0, 0, -32768, 0), - HR_SPRING("G29 high resolution spring force (ones)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + 0, 0, 0, 0, 32767, 0), + HR_SPRING("G29 high resolution spring force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, 0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff, - 65535, 65535, -32768, -32768, 32767, 0), + 65535, 65535, -32767, -32767, -32767, 0), - HR_DAMPER("G29 high resolution damper force (left)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + HR_DAMPER("G29 high resolution damper force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0x0f, 0x00, 0x00, 0x00, 0xff, 65535, 65535, 32767, 0), - HR_DAMPER("G29 high resolution damper force (right)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + HR_DAMPER("G29 high resolution damper force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0x00, 0x00, 0x0f, 0x00, 0xff, 65535, 65535, 0, 32767), - HR_DAMPER("G29 high resolution damper force (zeroes)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + HR_DAMPER("G29 high resolution damper force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0x00, 0x00, 0x00, 0x00, 0, 65535, 65535, 0, 0), - HR_DAMPER("G29 high resolution damper force (ones)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, + HR_DAMPER("G29 high resolution damper force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 100, 0x0f, 0x01, 0x0f, 0x01, 0xff, - 65535, 65535, -32768, -32768), + 65535, 65535, -32767, -32767), + +// HR_DAMPER("G29 high resolution damper force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x0f, 0x00, 0x00, 0x00, 0xff, +// 32767, 32767, 16383, 0), +// HR_DAMPER("G29 high resolution damper force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x00, 0x00, 0x0f, 0x00, 0xff, +// 32767, 32767, 0, 16383), +// HR_DAMPER("G29 high resolution damper force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x00, 0x00, 0x00, 0x00, 0, +// 32767, 32767, 0, 0), +// HR_DAMPER("G29 high resolution damper force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 0, 50, +// 0x0f, 0x01, 0x0f, 0x01, 0xff, +// 32767, 32767, -16383, -16383), + + HR_DAMPER("G29 high resolution damper force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 0x0f, 0x00, 0x00, 0x00, 0xff, + 65535, 65535, 0, 32767), + HR_DAMPER("G29 high resolution damper force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 0x00, 0x00, 0x0f, 0x00, 0xff, + 65535, 65535, 32767, 0), + HR_DAMPER("G29 high resolution damper force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 0x00, 0x00, 0x00, 0x00, 0, + 65535, 65535, 0, 0), + HR_DAMPER("G29 high resolution damper force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, 1, 100, + 0x0f, 0x01, 0x0f, 0x01, 0xff, + 65535, 65535, -32767, -32767), }; -int compare_events(const GE_Event * left, const GE_Event * right) { +int compare_forces(const GE_Event * left, const GE_Event * right) { if (left->type != right->type) { return 1; } + int ret = 0; + switch (left->type) { case GE_JOYCONSTANTFORCE: if (left->jconstant.level != right->jconstant.level) { - fprintf(stderr, "level: %d vs %d\n", left->jconstant.level, right->jconstant.level); - return 1; + fprintf(stderr, "level: %d (ref) vs %d (res)\n", left->jconstant.level, right->jconstant.level); + ret = 1; } break; case GE_JOYSPRINGFORCE: case GE_JOYDAMPERFORCE: if (left->jcondition.saturation.left != right->jcondition.saturation.left) { - fprintf(stderr, "saturation.left: %u vs %u\n", left->jcondition.saturation.left, right->jcondition.saturation.left); - return 1; + fprintf(stderr, "saturation.left: %u (ref) vs %u (res)\n", left->jcondition.saturation.left, right->jcondition.saturation.left); + ret = 1; } if (left->jcondition.saturation.right != right->jcondition.saturation.right) { - fprintf(stderr, "saturation.right: %u vs %u\n", left->jcondition.saturation.right, right->jcondition.saturation.right); - return 1; + fprintf(stderr, "saturation.right: %u (ref) vs %u (res)\n", left->jcondition.saturation.right, right->jcondition.saturation.right); + ret = 1; } if (left->jcondition.coefficient.left != right->jcondition.coefficient.left) { - fprintf(stderr, "coefficient.left: %u vs %u\n", left->jcondition.coefficient.left, right->jcondition.coefficient.left); - return 1; + fprintf(stderr, "coefficient.left: %d (ref) vs %d (res)\n", left->jcondition.coefficient.left, right->jcondition.coefficient.left); + ret = 1; } if (left->jcondition.coefficient.right != right->jcondition.coefficient.right) { - fprintf(stderr, "coefficient.right: %u vs %u\n", left->jcondition.coefficient.right, right->jcondition.coefficient.right); - return 1; + fprintf(stderr, "coefficient.right: %d (ref) vs %d (res)\n", left->jcondition.coefficient.right, right->jcondition.coefficient.right); + ret = 1; } if (left->jcondition.center != right->jcondition.center) { - fprintf(stderr, "center: %u vs %u\n", left->jcondition.center, right->jcondition.center); - return 1; + fprintf(stderr, "center: %d (ref) vs %d (res)\n", left->jcondition.center, right->jcondition.center); + ret = 1; } if (left->jcondition.deadband != right->jcondition.deadband) { - fprintf(stderr, "deadband: %u vs %u\n", left->jcondition.deadband, right->jcondition.deadband); - return 1; + fprintf(stderr, "deadband: %u (ref) vs %u (res)\n", left->jcondition.deadband, right->jcondition.deadband); + ret = 1; } break; } - return 0; + return ret; } int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { @@ -321,6 +461,8 @@ int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused)) ff_conv_init(0, test_cases[i].product); + ff_conv_set_tweaks(0, test_cases[i].invert); + ff_conv_process_report(0, test_cases[i].data); GE_Event event = {}; @@ -328,7 +470,7 @@ int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused)) printf("test case: %s: ", test_cases[i].name); - if (compare_events(test_cases[i].events, &event)) { + if (compare_forces(test_cases[i].events, &event)) { printf("failed: ff_conv returned an incorrect event\n"); continue; } @@ -343,7 +485,7 @@ int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused)) GE_Event stopEvent = { .type = event.type, .which = event.which }; - if (compare_events(&stopEvent, &event)) { + if (compare_forces(&stopEvent, &event)) { printf("failed: ff_conv returned an incorrect stop event\n"); continue; } diff --git a/core/test/haptic/ff_lg_test.c b/core/test/haptic/ff_lg_test.c index 185a12f3..5c2685c3 100644 --- a/core/test/haptic/ff_lg_test.c +++ b/core/test/haptic/ff_lg_test.c @@ -1,11 +1,445 @@ /* - Copyright (c) 2016 Mathieu Laurendeau + Copyright (c) 2017 Mathieu Laurendeau License: GPLv3 */ -int main(int argc, char * argv[]) { +#define SDL_MAIN_HANDLED +#include +#include +#include +#include +s_gimx_params gimx_params = { 0 }; // { .debug = { .ff_lg = 1 } }; + +#define CONSTANT(LEVEL) \ + .type = FF_LG_FTYPE_CONSTANT, \ + .parameters = { LEVEL }, + +#define VARIABLE(LEVEL) \ + .type = FF_LG_FTYPE_VARIABLE, \ + .parameters = { LEVEL }, + +#define LR_DAMPER(K1, S1, K2, S2) \ + .type = FF_LG_FTYPE_DAMPER, \ + .parameters = { K1, S1, K2, S2 } + +#define HR_DAMPER(K1, S1, K2, S2, CLIP) \ + .type = FF_LG_FTYPE_HIGH_RESOLUTION_DAMPER, \ + .parameters = { K1, S1, K2, S2, CLIP } + +#define LR_SPRING(D1, D2, K2, K1, S2, S1, CLIP) \ + .type = FF_LG_FTYPE_SPRING, \ + .parameters = { D1, D2, K2 | K1, S2 | S1, CLIP } + +#define HR_SPRING(D1, D2, K2, K1, D2L, S2, D1L, S1, CLIP) \ + .type = FF_LG_FTYPE_HIGH_RESOLUTION_SPRING, \ + .parameters = { D1, D2, K2 | K1, D2L | S2 | D1L | S1, CLIP } + +#define CONSTANT_TEST(NAME, SRC_PRODUCT_ID, DST_PRODUCT_ID, INVERT, C_GAIN, RANGE, F_IN, F_OUT) \ + { \ + .name = NAME, \ + .src_pid = SRC_PRODUCT_ID, \ + .in.cmd = FF_LG_FSLOT_1 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .in.force = { F_IN }, \ + .invert = INVERT, \ + .gain = { C_GAIN, 0, 0 }, \ + .dst_pid = DST_PRODUCT_ID, \ + .dst_range = RANGE, \ + .out.cmd = FF_LG_FSLOT_1 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .out.force = { F_OUT }, \ + } + +#define VARIABLE_TEST(NAME, SRC_PRODUCT_ID, DST_PRODUCT_ID, INVERT, C_GAIN, RANGE, F_IN, F_OUT) \ + { \ + .name = NAME, \ + .src_pid = SRC_PRODUCT_ID, \ + .in.cmd = FF_LG_FSLOT_1 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .in.force = { F_IN }, \ + .invert = INVERT, \ + .gain = { C_GAIN, 0, 0 }, \ + .dst_pid = DST_PRODUCT_ID, \ + .dst_range = RANGE, \ + .out.cmd = FF_LG_FSLOT_1 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .out.force = { F_OUT }, \ + } + +#define HR_SPRING_TEST(NAME, SRC_PRODUCT_ID, DST_PRODUCT_ID, INVERT, S_GAIN, RANGE, F_IN, F_OUT) \ + { \ + .name = NAME, \ + .src_pid = SRC_PRODUCT_ID, \ + .in.cmd = FF_LG_FSLOT_2 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .in.force = { F_IN }, \ + .invert = INVERT, \ + .gain = { 0, S_GAIN, 0 }, \ + .dst_pid = DST_PRODUCT_ID, \ + .dst_range = RANGE, \ + .out.cmd = FF_LG_FSLOT_2 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .out.force = { F_OUT }, \ + } + +#define HR_DAMPER_TEST(NAME, SRC_PRODUCT_ID, DST_PRODUCT_ID, INVERT, D_GAIN, RANGE, F_IN, F_OUT) \ + { \ + .name = NAME, \ + .src_pid = SRC_PRODUCT_ID, \ + .in.cmd = FF_LG_FSLOT_4 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .in.force = { F_IN }, \ + .invert = INVERT, \ + .gain = { 0, 0, D_GAIN }, \ + .dst_pid = DST_PRODUCT_ID, \ + .dst_range = RANGE, \ + .out.cmd = FF_LG_FSLOT_4 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .out.force = { F_OUT }, \ + } + +#define SPRING_TEST(NAME, SRC_PRODUCT_ID, DST_PRODUCT_ID, INVERT, S_GAIN, RANGE, F_IN, F_OUT) \ + { \ + .name = NAME, \ + .src_pid = SRC_PRODUCT_ID, \ + .in.cmd = FF_LG_FSLOT_2 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .in.force = { F_IN }, \ + .invert = INVERT, \ + .gain = { 0, S_GAIN, 0 }, \ + .dst_pid = DST_PRODUCT_ID, \ + .dst_range = RANGE, \ + .out.cmd = FF_LG_FSLOT_2 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .out.force = { F_OUT }, \ + } + + +#define DAMPER_TEST(NAME, SRC_PRODUCT_ID, DST_PRODUCT_ID, INVERT, D_GAIN, RANGE, F_IN, F_OUT) \ + { \ + .name = NAME, \ + .src_pid = SRC_PRODUCT_ID, \ + .in.cmd = FF_LG_FSLOT_2 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .in.force = { F_IN }, \ + .invert = INVERT, \ + .gain = { 0, 0, D_GAIN }, \ + .dst_pid = DST_PRODUCT_ID, \ + .dst_range = RANGE, \ + .out.cmd = FF_LG_FSLOT_2 | FF_LG_CMD_DOWNLOAD_AND_PLAY, \ + .out.force = { F_OUT }, \ + } + +static const struct { + char * name; + uint16_t src_pid; + union { + unsigned char data[FF_LG_OUTPUT_REPORT_SIZE]; + struct PACKED { + unsigned char cmd; + s_ff_lg_force force; + }; + } in; + int invert; + struct { + int constant; + int spring; + int damper; + } gain; + uint16_t dst_pid; + uint16_t dst_range; + union { + unsigned char data[FF_LG_OUTPUT_REPORT_SIZE]; + struct PACKED { + unsigned char cmd; + s_ff_lg_force force; + }; + } out; +} test_cases[] = { + +// CONSTANT_TEST("G29 constant force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, CONSTANT(0xff), CONSTANT(0xff)), +// CONSTANT_TEST("G29 constant force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, CONSTANT(0), CONSTANT(0)), +// CONSTANT_TEST("G29 constant force (null, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, CONSTANT(127), CONSTANT(127)), +// +// CONSTANT_TEST("G29 constant force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, CONSTANT(0xff), CONSTANT(0xbf)), +// CONSTANT_TEST("G29 constant force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, CONSTANT(0), CONSTANT(0x40)), +// CONSTANT_TEST("G29 constant force (null, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, CONSTANT(127), CONSTANT(127)), +// +// CONSTANT_TEST("G29 constant force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, CONSTANT(0xff), CONSTANT(0)), +// CONSTANT_TEST("G29 constant force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, CONSTANT(0), CONSTANT(0xff)), +// CONSTANT_TEST("G29 constant force (null, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, CONSTANT(127), CONSTANT(127)), +// +// DAMPER_TEST("G29 damper force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_DAMPER(0x07, 0x00, 0x00, 0x00), HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff)), +// DAMPER_TEST("G29 damper force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_DAMPER(0x00, 0x00, 0x07, 0x00), HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff)), +// DAMPER_TEST("G29 damper force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_DAMPER(0x00, 0x00, 0x00, 0x00), HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0xff)), +// DAMPER_TEST("G29 damper force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_DAMPER(0x07, 0x01, 0x07, 0x01), HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff)), +// +// DAMPER_TEST("G29 damper force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_DAMPER(0x07, 0x00, 0x00, 0x00), HR_DAMPER(0x07, 0x00, 0x00, 0x00, 0x7f)), +// DAMPER_TEST("G29 damper force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_DAMPER(0x00, 0x00, 0x07, 0x00), HR_DAMPER(0x00, 0x00, 0x07, 0x00, 0x7f)), +// DAMPER_TEST("G29 damper force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_DAMPER(0x00, 0x00, 0x00, 0x00), HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0x7f)), +// DAMPER_TEST("G29 damper force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_DAMPER(0x07, 0x01, 0x07, 0x01), HR_DAMPER(0x07, 0x01, 0x07, 0x01, 0x7f)), +// +// DAMPER_TEST("G29 damper force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_DAMPER(0x07, 0x00, 0x00, 0x00), HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff)), +// DAMPER_TEST("G29 damper force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_DAMPER(0x00, 0x00, 0x07, 0x00), HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff)), +// DAMPER_TEST("G29 damper force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_DAMPER(0x00, 0x00, 0x00, 0x00), HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0xff)), +// DAMPER_TEST("G29 damper force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_DAMPER(0x07, 0x01, 0x07, 0x01), HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff)), +// +// DAMPER_TEST("FFGP damper force (c=5)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 200, +// LR_DAMPER(0x05, 0x00, 0x05, 0x00), HR_DAMPER(0x0b, 0x00, 0x0b, 0x00, 0xff)), +// DAMPER_TEST("FFGP damper force (c=6)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 200, +// LR_DAMPER(0x06, 0x00, 0x06, 0x00), HR_DAMPER(0x07, 0x00, 0x07, 0x00, 0xff)), +// +// DAMPER_TEST("DFP damper force (c=5)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_DAMPER(0x05, 0x00, 0x05, 0x00), HR_DAMPER(0x07, 0x00, 0x07, 0x00, 0xff)), +// DAMPER_TEST("DFP damper force (c=6)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_DAMPER(0x06, 0x00, 0x06, 0x00), HR_DAMPER(0x0b, 0x00, 0x0b, 0x00, 0xff)), +// +// SPRING_TEST("G29 spring force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_SPRING(0x7f, 0x7f, 0x00, 0x07, 0x00, 0x00, 255), HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0x60, 0x00, 0x06, 0x00, 255)), +// SPRING_TEST("G29 spring force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_SPRING(0x7f, 0x7f, 0x70, 0x00, 0x00, 0x00, 255), HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0x60, 0x00, 0x06, 0x00, 255)), +// SPRING_TEST("G29 spring force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_SPRING(0, 0, 0x00, 0x00, 0x00, 0x00, 0), HR_SPRING(0, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0)), +// SPRING_TEST("G29 spring force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_SPRING(0xff, 0xff, 0x70, 0x07, 0x10, 0x01, 0xff), HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xf0, 0x00, 0x0f, 0x00, 0xff)), +// +// SPRING_TEST("G29 spring force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_SPRING(0x7f, 0x7f, 0x00, 0x07, 0x00, 0x00, 255), HR_SPRING(0x7f, 0x7f, 0x00, 0x07, 0x60, 0x00, 0x06, 0x00, 0x7f)), +// SPRING_TEST("G29 spring force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_SPRING(0x7f, 0x7f, 0x70, 0x00, 0x00, 0x00, 255), HR_SPRING(0x7f, 0x7f, 0x70, 0x00, 0x60, 0x00, 0x06, 0x00, 0x7f)), +// SPRING_TEST("G29 spring force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_SPRING(0, 0, 0x00, 0x00, 0x00, 0x00, 0), HR_SPRING(0, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0)), +// SPRING_TEST("G29 spring force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// LR_SPRING(0xff, 0xff, 0x70, 0x07, 0x10, 0x01, 0xff), HR_SPRING(0xff, 0xff, 0x70, 0x07, 0xe0, 0x10, 0x0e, 0x01, 0x7f)), +// +// SPRING_TEST("G29 spring force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_SPRING(0x7f, 0x7f, 0x00, 0x07, 0x00, 0x00, 255), HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0x60, 0x00, 0x06, 0x00, 255)), +// SPRING_TEST("G29 spring force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_SPRING(0x7f, 0x7f, 0x70, 0x00, 0x00, 0x00, 255), HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0x60, 0x00, 0x06, 0x00, 255)), +// SPRING_TEST("G29 spring force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_SPRING(0, 0, 0x00, 0x00, 0x00, 0x00, 0), HR_SPRING(0, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0)), +// SPRING_TEST("G29 spring force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// LR_SPRING(0xff, 0xff, 0x70, 0x07, 0x10, 0x01, 0xff), HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff)), +// +// SPRING_TEST("FFGP spring force (c=5)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 200, +// LR_SPRING(0x7f, 0x7f, 0x50, 0x05, 0x00, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0xb0, 0x0b, 0x60, 0x00, 0x06, 0x00, 0xff)), +// SPRING_TEST("FFGP spring force (c=6)", USB_PRODUCT_ID_LOGITECH_FORMULA_FORCE_GP, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 200, +// LR_SPRING(0x7f, 0x7f, 0x60, 0x06, 0x00, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0x70, 0x07, 0x60, 0x00, 0x06, 0x00, 0xff)), +// +// SPRING_TEST("DFP spring force (c=5)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_SPRING(0x7f, 0x7f, 0x50, 0x05, 0x00, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0x70, 0x07, 0x60, 0x00, 0x06, 0x00, 0xff)), +// SPRING_TEST("DFP spring force (c=6)", USB_PRODUCT_ID_LOGITECH_DFP_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// LR_SPRING(0x7f, 0x7f, 0x60, 0x06, 0x00, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0xb0, 0x0b, 0x60, 0x00, 0x06, 0x00, 0xff)), +// +// VARIABLE_TEST("G29 variable force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, VARIABLE(0xff), CONSTANT(0xff)), +// VARIABLE_TEST("G29 variable force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, VARIABLE(0), CONSTANT(0)), +// VARIABLE_TEST("G29 variable force (null, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, VARIABLE(127), CONSTANT(127)), +// +// VARIABLE_TEST("G29 variable force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, VARIABLE(0xff), CONSTANT(0xbf)), +// VARIABLE_TEST("G29 variable force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, VARIABLE(0), CONSTANT(0x40)), +// VARIABLE_TEST("G29 variable force (null, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, VARIABLE(127), CONSTANT(127)), +// +// VARIABLE_TEST("G29 variable force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, VARIABLE(0xff), CONSTANT(0)), +// VARIABLE_TEST("G29 variable force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, VARIABLE(0), CONSTANT(0xff)), +// VARIABLE_TEST("G29 variable force (null, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, VARIABLE(127), CONSTANT(127)), +// +// HR_SPRING_TEST("G29 high resolution spring force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff)), +// HR_SPRING_TEST("G29 high resolution spring force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff)), +// HR_SPRING_TEST("G29 high resolution spring force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_SPRING(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0), HR_SPRING(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0)), +// HR_SPRING_TEST("G29 high resolution spring force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff), HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff)), +// +// HR_SPRING_TEST("G29 high resolution spring force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0x00, 0x07, 0xe0, 0x00, 0x0e, 0x00, 0x7f)), +// HR_SPRING_TEST("G29 high resolution spring force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0x70, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x7f)), +// HR_SPRING_TEST("G29 high resolution spring force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_SPRING(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0), HR_SPRING(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0)), +// HR_SPRING_TEST("G29 high resolution spring force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff), HR_SPRING(0xff, 0xff, 0x70, 0x07, 0xe0, 0x10, 0x0e, 0x01, 0x7f)), +// +// HR_SPRING_TEST("G29 high resolution spring force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff)), +// HR_SPRING_TEST("G29 high resolution spring force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_SPRING(0x7f, 0x7f, 0xf0, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xff), HR_SPRING(0x7f, 0x7f, 0x00, 0x0f, 0xe0, 0x00, 0x0e, 0x00, 0xff)), +// HR_SPRING_TEST("G29 high resolution spring force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_SPRING(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0), HR_SPRING(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0)), +// HR_SPRING_TEST("G29 high resolution spring force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff), HR_SPRING(0xff, 0xff, 0xf0, 0x0f, 0xe0, 0x10, 0x0e, 0x01, 0xff)), +// +// HR_DAMPER_TEST("G29 high resolution damper force (left, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff), HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff)), +// HR_DAMPER_TEST("G29 high resolution damper force (right, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff), HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff)), +// HR_DAMPER_TEST("G29 high resolution damper force (zeroes, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0), HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0xff)), +// HR_DAMPER_TEST("G29 high resolution damper force (ones, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 100, 0, +// HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff), HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff)), +// +// HR_DAMPER_TEST("G29 high resolution damper force (left, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff), HR_DAMPER(0x07, 0x00, 0x00, 0x00, 0x7f)), +// HR_DAMPER_TEST("G29 high resolution damper force (right, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff), HR_DAMPER(0x00, 0x00, 0x07, 0x00, 0x7f)), +// HR_DAMPER_TEST("G29 high resolution damper force (zeroes, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0), HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0x7f)), +// HR_DAMPER_TEST("G29 high resolution damper force (ones, 50%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 0, 50, 0, +// HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff), HR_DAMPER(0x07, 0x01, 0x07, 0x01, 0x7f)), +// +// HR_DAMPER_TEST("G29 high resolution damper force (left, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff), HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff)), +// HR_DAMPER_TEST("G29 high resolution damper force (right, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_DAMPER(0x00, 0x00, 0x0f, 0x00, 0xff), HR_DAMPER(0x0f, 0x00, 0x00, 0x00, 0xff)), +// HR_DAMPER_TEST("G29 high resolution damper force (zeroes, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0), HR_DAMPER(0x00, 0x00, 0x00, 0x00, 0xff)), +// HR_DAMPER_TEST("G29 high resolution damper force (ones, inverted, 100%)", USB_PRODUCT_ID_LOGITECH_G29_WHEEL, USB_PRODUCT_ID_LOGITECH_DFGT_WHEEL, 1, 100, 0, +// HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff), HR_DAMPER(0x0f, 0x01, 0x0f, 0x01, 0xff)), +}; + +#define CHECK_PARAM(PARAM) \ + if (PARAM(fref) != PARAM(fres)) \ + { \ + fprintf(stderr, #PARAM ": %u (ref) vs %u (res)\n", PARAM(fref), PARAM(fres)); \ + ret = 1; \ + } + + +int compare_forces(const unsigned char * ref, const unsigned char * res) { + + if (ref[0] != res[0]) { + fprintf(stderr, "cmd: %u vs %u\n", ref[0], res[0]); + return 1; + } + + const s_ff_lg_force * fref = (const s_ff_lg_force *)(ref + 1); + const s_ff_lg_force * fres = (const s_ff_lg_force *)(res + 1); + + if (fref->type != fres->type) { + fprintf(stderr, "type: %u vs %u\n", fref->type, fres->type); + return 1; + } + + int ret = 0; + + switch (fref->type) { + case FF_LG_FTYPE_CONSTANT: + if (FF_LG_CONSTANT_LEVEL(fref, 0) != FF_LG_CONSTANT_LEVEL(fres, 0)) + { + fprintf(stderr, "level: %u (ref) vs %u (res)\n", FF_LG_CONSTANT_LEVEL(fref, 0), FF_LG_CONSTANT_LEVEL(fres, 0)); + ret = 1; + } + break; + case FF_LG_FTYPE_VARIABLE: + CHECK_PARAM(FF_LG_VARIABLE_L1) + CHECK_PARAM(FF_LG_VARIABLE_L2) + break; + case FF_LG_FTYPE_SPRING: + CHECK_PARAM(FF_LG_SPRING_K1) + CHECK_PARAM(FF_LG_SPRING_S1) + CHECK_PARAM(FF_LG_SPRING_D1) + CHECK_PARAM(FF_LG_SPRING_K2) + CHECK_PARAM(FF_LG_SPRING_S2) + CHECK_PARAM(FF_LG_SPRING_D2) + CHECK_PARAM(FF_LG_SPRING_CLIP) + break; + case FF_LG_FTYPE_DAMPER: + CHECK_PARAM(FF_LG_DAMPER_K1) + CHECK_PARAM(FF_LG_DAMPER_S1) + CHECK_PARAM(FF_LG_DAMPER_K2) + CHECK_PARAM(FF_LG_DAMPER_S2) + break; + case FF_LG_FTYPE_HIGH_RESOLUTION_SPRING: + CHECK_PARAM(FF_LG_HIGHRES_SPRING_K1) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_S1) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_D1) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_D1L) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_K2) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_S2) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_D2) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_D2L) + CHECK_PARAM(FF_LG_HIGHRES_SPRING_CLIP) + break; + case FF_LG_FTYPE_HIGH_RESOLUTION_DAMPER: + CHECK_PARAM(FF_LG_HIGHRES_DAMPER_K1) + CHECK_PARAM(FF_LG_HIGHRES_DAMPER_S1) + CHECK_PARAM(FF_LG_HIGHRES_DAMPER_K2) + CHECK_PARAM(FF_LG_HIGHRES_DAMPER_S2) + CHECK_PARAM(FF_LG_HIGHRES_DAMPER_CLIP) + break; + default: + if (memcmp(fref->parameters, fres->parameters, sizeof(fref->parameters))) { + fprintf(stderr, "unmodified report\n"); + return 1; + } + break; + } + + return ret; +} + +int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { + + unsigned int i; + for (i = 0; i < sizeof(test_cases) / sizeof(*test_cases); ++i) { + + ff_lg_init(0, test_cases[i].src_pid, test_cases[i].dst_pid); + + ff_lg_process_report(0, test_cases[i].in.data); + + s_ff_lg_report report; + ff_lg_get_report(0, &report); + + printf("test case: %s: ", test_cases[i].name); + + if (test_cases[i].dst_range) { + if (report.data[1] != FF_LG_CMD_EXTENDED_COMMAND) { + fprintf(stderr, "cmd: %u vs %u\n", FF_LG_CMD_EXTENDED_COMMAND, report.data[1]); + printf("failed: ff_lg_get_report returned an incorrect event\n"); + continue; + } + if (report.data[2] != FF_LG_EXT_CMD_CHANGE_WHEEL_RANGE) { + fprintf(stderr, "ext: %u vs %u\n", FF_LG_EXT_CMD_CHANGE_WHEEL_RANGE, report.data[2]); + printf("failed: ff_lg_get_report returned an incorrect event\n"); + continue; + } + uint16_t range = (report.data[4] << 8) | report.data[3]; + if (range != test_cases[i].dst_range) { + fprintf(stderr, "range: %u vs %u\n", test_cases[i].dst_range, range); + printf("failed: ff_lg_get_report returned an incorrect event\n"); + continue; + } + + ff_lg_ack(0); + ff_lg_get_report(0, &report); + } + + if (compare_forces(test_cases[i].out.data, report.data + 1)) { + printf("failed: ff_lg_get_report returned an incorrect event\n"); + continue; + } + + ff_lg_ack(0); + + s_ff_lg_report stop = { .data = { (test_cases[i].in.data[0] & FF_LG_FSLOT_MASK) | FF_LG_CMD_STOP } }; + + ff_lg_process_report(0, stop.data); + + ff_lg_get_report(0, &report); + + if (compare_forces(stop.data, report.data + 1)) { + printf("failed: ff_lg returned an incorrect stop event\n"); + continue; + } + + ff_lg_ack(0); + + printf("success\n"); + } return 0; } diff --git a/shared/configeditor/include/Configuration.h b/shared/configeditor/include/Configuration.h index 6cb01214..f23d101a 100644 --- a/shared/configeditor/include/Configuration.h +++ b/shared/configeditor/include/Configuration.h @@ -4,6 +4,7 @@ */ #ifndef CONFIGURATION_H + #define CONFIGURATION_H #include @@ -13,6 +14,7 @@ #include #include #include +#include "ForceFeedback.h" #include class Configuration @@ -30,6 +32,8 @@ class Configuration void SetMouseOptionsList(std::list val) { m_MouseOptionsList = val; } std::list* GetJoystickCorrectionsList() { return &m_JoystickCorrectionsList; } void SetJoystickCorrectionsList(std::list val) { m_JoystickCorrectionsList = val; } + ForceFeedback* GetForceFeedback() { return &m_ForceFeedback; } + void SetForceFeedback(ForceFeedback val) { m_ForceFeedback = val; } std::list* GetButtonMapperList() { return &m_ButtonMappers; } std::list* GetAxisMapperList() { return &m_AxisMappers; } void SetButtonMappers(std::list bml) { m_ButtonMappers = bml; } @@ -43,6 +47,7 @@ class Configuration std::list m_ButtonMappers; std::list m_AxisMappers; std::list m_JoystickCorrectionsList; + ForceFeedback m_ForceFeedback; }; #endif // CONFIGURATION_H diff --git a/shared/configeditor/include/ForceFeedback.h b/shared/configeditor/include/ForceFeedback.h new file mode 100644 index 00000000..92b90e29 --- /dev/null +++ b/shared/configeditor/include/ForceFeedback.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2017 Mathieu Laurendeau + License: GPLv3 + */ + +#ifndef FORCEFEEDBACK_H +#define FORCEFEEDBACK_H + +#include + +class ForceFeedback +{ + public: + ForceFeedback(); + ForceFeedback(string deviceName, string deviceId, + string inversion, string constantGain, string damperGain, string springGain); + virtual ~ForceFeedback(); + ForceFeedback(const ForceFeedback& other); + ForceFeedback& operator=(const ForceFeedback& other); + bool operator==(const ForceFeedback &other) const; + Device* GetJoystick() { return &m_Joystick; } + void SetJoystick(Device val) { m_Joystick = val; } + string getInversion() const { return m_Inversion; } + void setInversion(string inversion) { m_Inversion = inversion; } + string getConstantGain() const { return m_ConstantGain; } + void setConstantGain(string constantGain) { m_ConstantGain = constantGain; } + string getDamperGain() const { return m_DamperGain; } + void setDamperGain(string damperGain) { m_DamperGain = damperGain; } + string getSpringGain() const { return m_SpringGain; } + void setSpringGain(string springGain) { m_SpringGain = springGain; } + protected: + private: + Device m_Joystick; + string m_Inversion; + string m_ConstantGain; + string m_DamperGain; + string m_SpringGain; +}; + +#endif // FORCEFEEDBACK_H diff --git a/shared/configeditor/include/XmlReader.h b/shared/configeditor/include/XmlReader.h index a5517eb6..8e80639d 100644 --- a/shared/configeditor/include/XmlReader.h +++ b/shared/configeditor/include/XmlReader.h @@ -63,6 +63,13 @@ #define X_ATTR_HIGH_VALUE "high_value" #define X_ATTR_HIGH_COEF "high_coef" +#define X_NODE_FORCE_FEEDBACK "force_feedback" +#define X_NODE_INVERSION "inversion" +#define X_ATTR_ENABLE "enable" + +#define X_ATTR_VALUE_YES "yes" +#define X_ATTR_VALUE_NO "no" + class XmlReader { public: @@ -86,6 +93,7 @@ class XmlReader void ProcessIntensityElement(xmlNode * a_node); void ProcessIntensityListElement(xmlNode * a_node); void ProcessJoystickCorrectionsListElement(xmlNode * a_node); + void ProcessForceFeedbackElement(xmlNode * a_node); void ProcessCorrectionElement(xmlNode * a_node); void ProcessButtonMapElement(xmlNode * a_node); void ProcessAxisMapElement(xmlNode * a_node); @@ -94,6 +102,7 @@ class XmlReader void ProcessDeviceElement(xmlNode * a_node); void CheckDevice(string type, string name, string id); void ProcessEventElement(xmlNode * a_node); + void ProcessInversionElement(xmlNode * a_node); Event m_TempEvent; Device m_TempDevice; ControlMapper m_TempButtonMapper; @@ -101,6 +110,7 @@ class XmlReader Trigger m_TempTrigger; MouseOptions m_TempMouseOptions; JoystickCorrection m_TempJoystickCorrection; + ForceFeedback m_TempForceFeedback; Intensity m_TempIntensity; Configuration m_TempConfiguration; Controller m_TempController; diff --git a/shared/configeditor/include/XmlWritter.h b/shared/configeditor/include/XmlWritter.h index 2cd68e45..5cc5fa6a 100644 --- a/shared/configeditor/include/XmlWritter.h +++ b/shared/configeditor/include/XmlWritter.h @@ -27,6 +27,8 @@ class XmlWritter void CreateConfigurationNodes(xmlNodePtr parent_node); void CreateControllerNodes(xmlNodePtr parent_node); void CreateJoystickCorrectionsNodes(xmlNodePtr parent_node); + void CreateForceFeedbackNode(xmlNodePtr parent_node); + void CreateInversionNode(xmlNodePtr parent_node, ForceFeedback* ffb); unsigned int m_CurrentConfiguration; unsigned int m_CurrentController; ConfigurationFile* m_ConfigurationFile; diff --git a/shared/configeditor/include/event_catcher.h b/shared/configeditor/include/event_catcher.h index 8a93f646..59261bef 100644 --- a/shared/configeditor/include/event_catcher.h +++ b/shared/configeditor/include/event_catcher.h @@ -37,6 +37,9 @@ class event_catcher return _singleton; } + bool hasJoystick(); + bool hasMouse(); + bool hasKeyboard(); private: event_catcher(); virtual ~event_catcher(); diff --git a/shared/configeditor/src/Configuration.cpp b/shared/configeditor/src/Configuration.cpp index d1388904..afdf869f 100644 --- a/shared/configeditor/src/Configuration.cpp +++ b/shared/configeditor/src/Configuration.cpp @@ -17,7 +17,7 @@ Configuration::~Configuration() Configuration::Configuration(const Configuration& other):m_Trigger(other.m_Trigger), m_IntensityList(other.m_IntensityList), m_MouseOptionsList(other.m_MouseOptionsList), m_ButtonMappers(other.m_ButtonMappers), m_AxisMappers(other.m_AxisMappers), - m_JoystickCorrectionsList(other.m_JoystickCorrectionsList) + m_JoystickCorrectionsList(other.m_JoystickCorrectionsList), m_ForceFeedback(other.m_ForceFeedback) { //copy ctor } @@ -31,6 +31,7 @@ Configuration& Configuration::operator=(const Configuration& rhs) m_ButtonMappers = rhs.m_ButtonMappers; m_AxisMappers = rhs.m_AxisMappers; m_JoystickCorrectionsList = rhs.m_JoystickCorrectionsList; + m_ForceFeedback = rhs.m_ForceFeedback; return *this; } @@ -60,5 +61,9 @@ bool Configuration::IsEmpty() { return false; } + if(m_ForceFeedback.GetJoystick()->GetType().size()) + { + return false; + } return true; } diff --git a/shared/configeditor/src/ConfigurationFile.cpp b/shared/configeditor/src/ConfigurationFile.cpp index 928aa1f1..b04d03d6 100644 --- a/shared/configeditor/src/ConfigurationFile.cpp +++ b/shared/configeditor/src/ConfigurationFile.cpp @@ -16,6 +16,7 @@ ConfigurationFile::ConfigurationFile() { //ctor + m_multipleMK = false; m_checkDevices = true; } diff --git a/shared/configeditor/src/ForceFeedback.cpp b/shared/configeditor/src/ForceFeedback.cpp new file mode 100644 index 00000000..c1a52972 --- /dev/null +++ b/shared/configeditor/src/ForceFeedback.cpp @@ -0,0 +1,54 @@ +/* + Copyright (c) 2017 Mathieu Laurendeau + License: GPLv3 + */ + +#include + +ForceFeedback::ForceFeedback() +{ + //ctor +} + +ForceFeedback::ForceFeedback(string deviceName, string deviceId, + string inversion, string constantGain, string damperGain, string springGain): + m_Joystick("joystick", deviceName, deviceId), m_Inversion(inversion), + m_ConstantGain(constantGain), m_DamperGain(damperGain), m_SpringGain(springGain) +{ + //ctor +} + +ForceFeedback::~ForceFeedback() +{ + //dtor +} + +ForceFeedback::ForceFeedback(const ForceFeedback& other): + m_Joystick(other.m_Joystick), + m_Inversion(other.m_Inversion), + m_ConstantGain(other.m_ConstantGain), + m_DamperGain(other.m_DamperGain), + m_SpringGain(other.m_SpringGain) +{ + //copy ctor +} + +ForceFeedback& ForceFeedback::operator=(const ForceFeedback& rhs) +{ + if (this == &rhs) return *this; // handle self assignment + m_Joystick = rhs.m_Joystick; + m_Inversion = rhs.m_Inversion; + m_ConstantGain = rhs.m_ConstantGain; + m_DamperGain = rhs.m_DamperGain; + m_SpringGain = rhs.m_SpringGain; + return *this; +} + +bool ForceFeedback::operator==(const ForceFeedback &other) const +{ + return m_Joystick == other.m_Joystick + && m_Inversion == other.m_Inversion + && m_ConstantGain == other.m_ConstantGain + && m_DamperGain == other.m_DamperGain + && m_SpringGain == other.m_SpringGain; +} diff --git a/shared/configeditor/src/XmlReader.cpp b/shared/configeditor/src/XmlReader.cpp index 42a5098c..5e8cadcc 100644 --- a/shared/configeditor/src/XmlReader.cpp +++ b/shared/configeditor/src/XmlReader.cpp @@ -19,6 +19,8 @@ XmlReader::XmlReader() //ctor m_ConfigurationFile = NULL; m_evtcatch = event_catcher::getInstance(); + m_name_empty = false; + m_name_nempty = false; m_checkDevices = true; } @@ -27,6 +29,8 @@ XmlReader::XmlReader(ConfigurationFile* configFile) //ctor m_ConfigurationFile = configFile; m_evtcatch = event_catcher::getInstance(); + m_name_empty = false; + m_name_nempty = false; m_checkDevices = false; } @@ -335,7 +339,6 @@ void XmlReader::ProcessButtonElement(xmlNode * a_node) void XmlReader::ProcessAxisMapElement(xmlNode * a_node) { xmlNode* cur_node = NULL; - m_TempConfiguration.GetAxisMapperList()->clear(); for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { @@ -357,7 +360,6 @@ void XmlReader::ProcessAxisMapElement(xmlNode * a_node) void XmlReader::ProcessButtonMapElement(xmlNode * a_node) { xmlNode* cur_node = NULL; - m_TempConfiguration.GetButtonMapperList()->clear(); for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { @@ -679,10 +681,55 @@ void XmlReader::ProcessJoystickCorrectionsListElement(xmlNode * a_node) } } +void XmlReader::ProcessInversionElement(xmlNode * a_node) +{ + char * prop = (char*)xmlGetProp(a_node, (xmlChar*) X_ATTR_ENABLE); + m_TempForceFeedback.setInversion(string(prop ? prop : "no")); +} + +void XmlReader::ProcessForceFeedbackElement(xmlNode * a_node) +{ + xmlNode* cur_node = NULL; + + int hasDevice = 0, hasInversion = 0; + + for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) + { + if (cur_node->type == XML_ELEMENT_NODE) + { + if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_DEVICE)) + { + hasDevice = 1; + ProcessDeviceElement(cur_node); + } + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_INVERSION)) + { + hasInversion = 1; + ProcessInversionElement(cur_node); + } + } + } + + if (hasDevice == 0) + { + string message("missing device element"); + throw invalid_argument(message); + } + + if (hasInversion == 0) + { + string message("missing inversion element"); + throw invalid_argument(message); + } + + m_TempForceFeedback.SetJoystick(m_TempDevice); + + m_TempConfiguration.SetForceFeedback(m_TempForceFeedback); +} + void XmlReader::ProcessConfigurationElement(xmlNode * a_node) { xmlNode* cur_node = NULL; - bool found; unsigned int config_index; stringstream ss; string id; @@ -702,143 +749,39 @@ void XmlReader::ProcessConfigurationElement(xmlNode * a_node) throw invalid_argument(message); } - found = false; + m_TempConfiguration = Configuration(); // clear everything - for (cur_node = a_node->children; cur_node && !found; cur_node = cur_node->next) + for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_TRIGGER)) { ProcessTriggerElement(cur_node); - found = true; - } - else - { - break; } - } - } - - // trigger element is mandatory - if(!found) - { - string message("missing trigger element"); - throw invalid_argument(message); - } - - m_TempConfiguration.GetMouseOptionsList()->clear(); - - found = false; - - for ( ; cur_node && !found; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_MOUSEOPTIONS_LIST)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_MOUSEOPTIONS_LIST)) { ProcessMouseOptionsListElement(cur_node); - found = true; } - else - { - // mouse_options_list is optional - // => keep cur_node for the next element processing - break; - } - } - } - - m_TempConfiguration.GetIntensityList()->clear(); - - found = false; - - for ( ; cur_node && !found; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_INTENSITY_LIST)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_INTENSITY_LIST)) { ProcessIntensityListElement(cur_node); - found = true; - } - else - { - // intensity_list is optional - // => keep cur_node for the next element processing - break; } - } - } - - found = false; - - for ( ; cur_node && !found; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_BUTTON_MAP)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_BUTTON_MAP)) { ProcessButtonMapElement(cur_node); - found = true; - } - else - { - break;//not found } - } - } - - // button_map is mandatory - if(!found) - { - string message(string("missing button_map element")); - throw invalid_argument(message); - } - - found = false; - - for ( ; cur_node && !found; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_AXIS_MAP)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_AXIS_MAP)) { ProcessAxisMapElement(cur_node); - found = true; - } - else - { - break;//not found } - } - } - - // axis_map is mandatory - if(!found) - { - string message(string("missing axis_map element")); - throw invalid_argument(message); - } - - found = false; - - m_TempConfiguration.GetJoystickCorrectionsList()->clear(); - - for ( ; cur_node && !found; cur_node = cur_node->next) - { - if (cur_node->type == XML_ELEMENT_NODE) - { - if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_JOYSTICK_CORRECTIONS_LIST)) + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_JOYSTICK_CORRECTIONS_LIST)) { ProcessJoystickCorrectionsListElement(cur_node); - found = true; } - else + else if (xmlStrEqual(cur_node->name, (xmlChar*) X_NODE_FORCE_FEEDBACK)) { - // this node is optional - // => keep cur_node for the next element processing - break; + ProcessForceFeedbackElement(cur_node); } } } diff --git a/shared/configeditor/src/XmlWritter.cpp b/shared/configeditor/src/XmlWritter.cpp index 7e5de529..b34fde8c 100644 --- a/shared/configeditor/src/XmlWritter.cpp +++ b/shared/configeditor/src/XmlWritter.cpp @@ -20,6 +20,8 @@ XmlWritter::XmlWritter() { //ctor m_ConfigurationFile = NULL; + m_CurrentController = 0; + m_CurrentConfiguration = 0; } XmlWritter::XmlWritter(ConfigurationFile* configFile) @@ -171,6 +173,25 @@ void XmlWritter::CreateJoystickCorrectionsNodes(xmlNodePtr parent_node) } } +void XmlWritter::CreateInversionNode(xmlNodePtr parent_node, ForceFeedback* ffb) +{ + xmlNodePtr d_node = xmlNewChild(parent_node, NULL, BAD_CAST X_NODE_INVERSION, NULL); + const char * value = ffb->getInversion().empty() ? "no" : ffb->getInversion().c_str(); + xmlNewProp(d_node, BAD_CAST X_ATTR_ENABLE, BAD_CAST value); +} + +void XmlWritter::CreateForceFeedbackNode(xmlNodePtr parent_node) +{ + ForceFeedback * tweaks = m_ConfigurationFile->GetController(m_CurrentController)->GetConfiguration(m_CurrentConfiguration)->GetForceFeedback(); + + if (!tweaks->GetJoystick()->GetName().empty()) + { + xmlNodePtr node = xmlNewChild(parent_node, NULL, BAD_CAST X_NODE_FORCE_FEEDBACK, NULL); + CreateDeviceNode(node, tweaks->GetJoystick()); + CreateInversionNode(node, tweaks); + } +} + void XmlWritter::CreateIntensityNodes(xmlNodePtr parent_node) { char steps[4]; @@ -262,6 +283,8 @@ void XmlWritter::CreateConfigurationNodes(xmlNodePtr parent_node) CreateAxisMapNode(node); CreateJoystickCorrectionsNodes(node); + + CreateForceFeedbackNode(node); } } diff --git a/shared/configeditor/src/event_catcher.cpp b/shared/configeditor/src/event_catcher.cpp index a19fd478..33ec179a 100755 --- a/shared/configeditor/src/event_catcher.cpp +++ b/shared/configeditor/src/event_catcher.cpp @@ -451,3 +451,51 @@ void event_catcher::AddEvent(Device device, Event event) m_Events.push_back(make_pair(device, event)); } } + +bool event_catcher::hasJoystick() +{ + bool result = false; + + init(); + + if (ginput_joystick_name(0) != NULL) + { + result = true; + } + + clean(); + + return result; +} + +bool event_catcher::hasMouse() +{ + bool result = false; + + init(); + + if (ginput_mouse_name(0) != NULL) + { + result = true; + } + + clean(); + + return result; +} + +bool event_catcher::hasKeyboard() +{ + bool result = false; + + init(); + + if (ginput_keyboard_name(0) != NULL) + { + result = true; + } + + clean(); + + return result; +}