feat: New advanced joystick mode on radio, improvements to Companion and Simulator joystick support (#3111)
* Allow switches on the connected radio to control switches in the simulator via the joystick interface. * Merge configurable radio joystick interface from banyaszg. * Add two more 'axis' channels to companion. * Fix 6POS from USB joystick to simulator. * Reduce data size. * Fix build error on B&W radios. * Add 'Companion' button mode to work with the button logic in the Companion app. * Build Companion joystick setup layout at runtime. Change #define names for simulator joystick. Allow 15 axes and 32 buttons. Initial support for trim sliders. * Remove custom joystick button type for companion - use 3 buttons per switch in companion to control simulator switch state. * Fix joystick constructor calls parameters. Connect some radio joystick trims to simulator widgets. * Restore 'companion' button type to reduce number of buttons needed. Make trims work via buttons in USB joystick. * Connect joystick trim buttons to simulator widgets. * Use companion source names for trims. * Add encode / decode of usb joystick custom settings to Companion. * Convert to lvgl layouts. Update language files. * Fix generated YAML storage files. * Fix initialisation error when editing button channel. Cleanup code. * Fix rebase. * Fix refresh of joystick channel list view after editing an entry. Companion joystick config window uses two columns. * Fix vertical joystick inversion bug. Change stick axis labels. --------- Co-authored-by: Phil Mitchell <phil.a.mitchell@gmail.com>
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "usb_joystick.h"
|
||||
|
||||
#define _STR_MAX(x) "/" #x
|
||||
#define STR_MAX(x) _STR_MAX(x)
|
||||
|
||||
#if LCD_W >= 212
|
||||
#define MODE_POS 5*FW
|
||||
#define SUBMODE_POS MODE_POS+2*FW
|
||||
#define USBJ_ONE_2ND_COLUMN (12*FW)
|
||||
#define BTNMODE_POS SUBMODE_POS+5*FW
|
||||
#define BTNNUM_POS BTNMODE_POS+12*FW
|
||||
#else
|
||||
#define MODE_POS 5*FW
|
||||
#define SUBMODE_POS MODE_POS+2*FW
|
||||
#define USBJ_ONE_2ND_COLUMN (12*FW)
|
||||
#define BTNMODE_POS SUBMODE_POS+5*FW
|
||||
#define BTNNUM_POS LCD_W
|
||||
#endif
|
||||
|
||||
enum USBJFields {
|
||||
USBJ_FIELD_MODE,
|
||||
USBJ_FIELD_INVERSION,
|
||||
USBJ_FIELD_SUBMODE,
|
||||
USBJ_FIELD_BTNPOS,
|
||||
USBJ_FIELD_BTNNUM,
|
||||
USBJ_FIELD_MAX
|
||||
};
|
||||
|
||||
void menuModelUSBJoystickOne(event_t event)
|
||||
{
|
||||
#if defined(KEYS_GPIO_REG_MDL)
|
||||
if (event == EVT_KEY_FIRST(KEY_MODEL)) {
|
||||
pushMenu(menuChannelsView);
|
||||
killEvents(event);
|
||||
}
|
||||
#elif defined(NAVIGATION_X7)
|
||||
if (event == EVT_KEY_FIRST(KEY_MENU)) {
|
||||
pushMenu(menuChannelsView);
|
||||
killEvents(event);
|
||||
}
|
||||
#elif defined(NAVIGATION_XLITE)
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER) && IS_SHIFT_PRESSED()) {
|
||||
pushMenu(menuChannelsView);
|
||||
killEvents(event);
|
||||
}
|
||||
#endif
|
||||
USBJoystickChData * cch = usbJChAddress(s_currIdx);
|
||||
putsChn(12*FW, 0, s_currIdx+1, 0);
|
||||
lcdDrawNumber(20*FW, 0, channelOutputs[s_currIdx], RIGHT);
|
||||
|
||||
SUBMENU(STR_USBJOYSTICK_LABEL, USBJ_FIELD_MAX, {
|
||||
0,
|
||||
(cch->mode != USBJOYS_CH_NONE) ? (uint8_t)0 : (uint8_t)HIDDEN_ROW,
|
||||
(cch->mode != USBJOYS_CH_NONE) ? (uint8_t)0 : (uint8_t)HIDDEN_ROW,
|
||||
(cch->mode == USBJOYS_CH_BUTTON) ?
|
||||
(((cch->param == USBJOYS_BTN_MODE_SW_EMU) || (cch->param == USBJOYS_BTN_MODE_DELTA)) ? (uint8_t)READONLY_ROW : (uint8_t)0)
|
||||
: (uint8_t)HIDDEN_ROW,
|
||||
(cch->mode == USBJOYS_CH_BUTTON) ? (uint8_t)0 : (uint8_t)HIDDEN_ROW });
|
||||
|
||||
int8_t sub = menuVerticalPosition;
|
||||
|
||||
char str[20];
|
||||
|
||||
for (uint8_t i=0; i<(NUM_BODY_LINES-1); i++) {
|
||||
coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
|
||||
uint8_t k = i + menuVerticalOffset;
|
||||
for (int j=0; j<=k; ++j) {
|
||||
if (j<(int)DIM(mstate_tab) && mstate_tab[j] == HIDDEN_ROW) {
|
||||
++k;
|
||||
}
|
||||
}
|
||||
LcdFlags attr = (sub==k ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
|
||||
|
||||
switch (k) {
|
||||
case USBJ_FIELD_MODE:
|
||||
cch->mode = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_MODE, STR_VUSBJOYSTICK_CH_MODE, cch->mode, 0, USBJOYS_CH_LAST, attr, event);
|
||||
break;
|
||||
|
||||
case USBJ_FIELD_INVERSION:
|
||||
lcdDrawTextAlignedLeft(y, TR_USBJOYSTICK_CH_INVERSION);
|
||||
drawCheckBox(USBJ_ONE_2ND_COLUMN, y, cch->inversion, attr);
|
||||
if (attr) cch->inversion = checkIncDecModel(event, cch->inversion, 0, 1);
|
||||
break;
|
||||
|
||||
case USBJ_FIELD_SUBMODE:
|
||||
if(cch->mode == USBJOYS_CH_BUTTON) {
|
||||
cch->param = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_BTNMODE, STR_VUSBJOYSTICK_CH_BTNMODE, cch->param, 0, USBJOYS_BTN_MODE_LAST, attr, event);
|
||||
if(cch->param == USBJOYS_BTN_MODE_SW_EMU) cch->switch_npos = 0;
|
||||
else if(cch->param == USBJOYS_BTN_MODE_DELTA) cch->switch_npos = 1;
|
||||
}
|
||||
else if(cch->mode == USBJOYS_CH_AXIS) {
|
||||
cch->param = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_AXIS, STR_VUSBJOYSTICK_CH_AXIS, cch->param, 0, USBJOYS_AXIS_LAST, attr, event);
|
||||
}
|
||||
else if(cch->mode == USBJOYS_CH_SIM) {
|
||||
cch->param = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_SIM, STR_VUSBJOYSTICK_CH_SIM, cch->param, 0, USBJOYS_SIM_LAST, attr, event);
|
||||
}
|
||||
break;
|
||||
|
||||
case USBJ_FIELD_BTNPOS:
|
||||
if(cch->param == USBJOYS_BTN_MODE_SW_EMU) {
|
||||
lcdDrawTextAlignedLeft(y, STR_USBJOYSTICK_CH_SWPOS);
|
||||
lcdDrawText(USBJ_ONE_2ND_COLUMN, y, STR_VUSBJOYSTICK_CH_SWPOS[0], attr);
|
||||
}
|
||||
else if(cch->param == USBJOYS_BTN_MODE_DELTA) {
|
||||
lcdDrawTextAlignedLeft(y, STR_USBJOYSTICK_CH_SWPOS);
|
||||
lcdDrawText(USBJ_ONE_2ND_COLUMN, y, STR_VUSBJOYSTICK_CH_SWPOS[1], attr);
|
||||
}
|
||||
else {
|
||||
cch->switch_npos = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_SWPOS, STR_VUSBJOYSTICK_CH_SWPOS, cch->switch_npos, 0, 7, attr, event);
|
||||
}
|
||||
break;
|
||||
|
||||
case USBJ_FIELD_BTNNUM:
|
||||
lcdDrawTextAlignedLeft(y, TR_USBJOYSTICK_CH_BTNNUM);
|
||||
uint8_t last = cch->lastBtnNumNoCLip();
|
||||
uint8_t limited = 0;
|
||||
if(last >= USBJ_BUTTON_SIZE) {
|
||||
limited = 1;
|
||||
last = USBJ_BUTTON_SIZE-1;
|
||||
}
|
||||
if (last > cch->btn_num)
|
||||
snprintf(str, 20, "%u..%u%c", cch->btn_num, last, limited ? '!' : ' ');
|
||||
else
|
||||
snprintf(str, 20, "%u%c", cch->btn_num, limited ? '!' : ' ');
|
||||
lcdDrawText(USBJ_ONE_2ND_COLUMN, y, str, attr);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_MODELVAR(event, cch->btn_num, 0, USBJ_BUTTON_SIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cch->mode == USBJOYS_CH_BUTTON) {
|
||||
if (isUSBBtnNumCollision(s_currIdx)) {
|
||||
lcdDrawText(LCD_W/2, LCD_H-FH, STR_USBJOYSTICK_BTN_COLLISION, INVERS | CENTERED);
|
||||
}
|
||||
}
|
||||
else if (cch->mode == USBJOYS_CH_AXIS) {
|
||||
if (isUSBAxisCollision(s_currIdx)) {
|
||||
lcdDrawText(LCD_W/2, LCD_H-FH, STR_USBJOYSTICK_AXIS_COLLISION, INVERS | CENTERED);
|
||||
}
|
||||
}
|
||||
else if (cch->mode == USBJOYS_CH_SIM) {
|
||||
if (isUSBSimCollision(s_currIdx)) {
|
||||
lcdDrawText(LCD_W/2, LCD_H-FH, STR_USBJOYSTICK_AXIS_COLLISION, INVERS | CENTERED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onUSBJoystickMenu(const char *result)
|
||||
{
|
||||
int8_t sub = menuVerticalPosition - HEADER_LINE;
|
||||
USBJoystickChData * cs = usbJChAddress(sub);
|
||||
|
||||
if (result == STR_EDIT) {
|
||||
s_currIdx = sub;
|
||||
pushMenu(menuModelUSBJoystickOne);
|
||||
}
|
||||
else if (result == STR_CLEAR) {
|
||||
memset(cs, 0, sizeof(USBJoystickChData));
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
void menuModelUSBJoystick(event_t event)
|
||||
{
|
||||
check_submenu_simple(event, USBJ_MAX_JOYSTICK_CHANNELS - HEADER_LINE);
|
||||
title(STR_USBJOYSTICK_LABEL);
|
||||
|
||||
if (s_editMode > 0) s_editMode = 0;
|
||||
|
||||
int8_t sub = menuVerticalPosition - HEADER_LINE;
|
||||
|
||||
coord_t y = 0;
|
||||
uint8_t k = 0;
|
||||
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
USBJoystickChData * cch = usbJChAddress(sub);
|
||||
s_currIdx = sub;
|
||||
if (sub >= 0)
|
||||
POPUP_MENU_ADD_ITEM(STR_EDIT);
|
||||
if (cch->mode != USBJOYS_CH_NONE)
|
||||
POPUP_MENU_ADD_ITEM(STR_CLEAR);
|
||||
if (popupMenuItemsCount == 1) {
|
||||
popupMenuItemsCount = 0;
|
||||
s_currIdx = sub;
|
||||
pushMenu(menuModelUSBJoystickOne);
|
||||
}
|
||||
else {
|
||||
POPUP_MENU_START(onUSBJoystickMenu);
|
||||
}
|
||||
}
|
||||
|
||||
char str[20];
|
||||
|
||||
for (uint8_t i=0; i<NUM_BODY_LINES; i++) {
|
||||
y = MENU_HEADER_HEIGHT + 1 + i*FH;
|
||||
k = i + menuVerticalOffset;
|
||||
LcdFlags attr = (sub==k) ? INVERS : 0;
|
||||
|
||||
USBJoystickChData * cch = usbJChAddress(k);
|
||||
|
||||
putsChn(0, y, k+1, attr);
|
||||
lcdDrawChar(4*FW, y, cch->inversion ? '{' : ' ', 0);
|
||||
|
||||
lcdDrawTextAtIndex(MODE_POS, y, STR_VUSBJOYSTICK_CH_MODE_S, cch->mode, 0);
|
||||
if (cch->mode == USBJOYS_CH_BUTTON) {
|
||||
lcdDrawTextAtIndex(SUBMODE_POS, y, STR_VUSBJOYSTICK_CH_SWPOS, cch->switch_npos, 0);
|
||||
lcdDrawTextAtIndex(BTNMODE_POS, y, STR_VUSBJOYSTICK_CH_BTNMODE_S, cch->param, 0);
|
||||
|
||||
uint8_t last = cch->lastBtnNum();
|
||||
if (last > cch->btn_num)
|
||||
sprintf(str, "%u..%u", cch->btn_num, last);
|
||||
else
|
||||
sprintf(str, "%u", cch->btn_num);
|
||||
LcdFlags warn = 0;
|
||||
if (isUSBBtnNumCollision(k)) warn = INVERS;
|
||||
lcdDrawText(BTNNUM_POS, y, str, RIGHT|warn);
|
||||
}
|
||||
else if (cch->mode == USBJOYS_CH_AXIS) {
|
||||
LcdFlags warn = 0;
|
||||
if (isUSBAxisCollision(k)) warn = INVERS;
|
||||
lcdDrawTextAtIndex(SUBMODE_POS, y, STR_VUSBJOYSTICK_CH_AXIS, cch->param, warn);
|
||||
}
|
||||
else if (cch->mode == USBJOYS_CH_SIM) {
|
||||
LcdFlags warn = 0;
|
||||
if (isUSBSimCollision(k)) warn = INVERS;
|
||||
lcdDrawTextAtIndex(SUBMODE_POS, y, STR_VUSBJOYSTICK_CH_SIM, cch->param, warn);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user